使用汇编语言来编写UEFI Application 完全没有问题,理论上编写 DXE Driver 也没有问题。最近抽空研究了一下这个问题。

从网上的资料来说,可以使用 FASM来完成这个工作,在http://x86asm.net/articles/uefi-programming-first-steps/ 可以看到一篇名为 “UEFI Programming - First Steps”的文章。但是经过我的实验使用 FASM 最出来的EFI 在文件头部上(PE Header)就存在很大的问题,比如: Image Size 给出来的不正确,还有 BaseImage 给出的不正确。有可能是我没有使用正确的参数导致的,但是看起来研究会很麻烦,并且最终能否成功严重存疑于是放弃了。

接下来研究如何使用 Nasm 来实现编写一个 UEFI Application。在 https://hackerpulp.com/os/os-development-windows-1-building-uefi-applications-nasm/https://github.com/BrianOtto/nasm-uefi 找到了一个例子,经过修改可以正常工作。

1.原文是编写一个 BootLoader,所以最后完成显示之后Hang 住即可,这里我们需要正常返回,最终ASM 代码如下:

; Copyright 2018-2019 Brian Otto @ https://hackerpulp.com
; 
; Permission to use, copy, modify, and/or distribute this software for any 
; purpose with or without fee is hereby granted, provided that the above 
; copyright notice and this permission notice appear in all copies.
; 
; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 
; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
; AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
; PERFORMANCE OF THIS SOFTWARE.

; generate 64-bit code
bits 64

; contains the code that will run
section .text

; allows the linker to see this symbol
global _start

; see http://www.uefi.org/sites/default/files/resources/UEFI Spec 2_7_A Sept 6.pdf#G8.1001729
struc EFI_TABLE_HEADER
    .Signature    RESQ 1
    .Revision     RESD 1
    .HeaderSize   RESD 1
    .CRC32        RESD 1
    .Reserved     RESD 1
endstruc

; see http://www.uefi.org/sites/default/files/resources/UEFI Spec 2_7_A Sept 6.pdf#G8.1001773
struc EFI_SYSTEM_TABLE
    .Hdr                  RESB EFI_TABLE_HEADER_size
    .FirmwareVendor       RESQ 1
    .FirmwareRevision     RESD 1
    .ConsoleInHandle      RESQ 1
    .ConIn                RESQ 1
    .ConsoleOutHandle     RESQ 1
    .ConOut               RESQ 1
    .StandardErrorHandle  RESQ 1
    .StdErr               RESQ 1
    .RuntimeServices      RESQ 1
    .BootServices         RESQ 1
    .NumberOfTableEntries RESQ 1
    .ConfigurationTable   RESQ 1
endstruc

; see http://www.uefi.org/sites/default/files/resources/UEFI Spec 2_7_A Sept 6.pdf#G16.1016807
struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
    .Reset             RESQ 1
    .OutputString      RESQ 1
    .TestString	       RESQ 1
    .QueryMode	       RESQ 1
    .SetMode	       RESQ 1
    .SetAttribute      RESQ 1
    .ClearScreen       RESQ 1
    .SetCursorPosition RESQ 1
    .EnableCursor      RESQ 1
    .Mode              RESQ 1
endstruc

_start:

    push rax    ;ConOut requires a push here. I don't know why

    ; reserve space for 4 arguments
    sub rsp, 4 * 8

    ; rdx points to the EFI_SYSTEM_TABLE structure
    ; which is the 2nd argument passed to us by the UEFI firmware
    ; adding 64 causes rcx to point to EFI_SYSTEM_TABLE.ConOut
    mov rcx, [rdx + 64]

    ; load the address of our string into rdx
    lea rdx, [rel strHello]

    ; EFI_SYSTEM_TABLE.ConOut points to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
    ; call OutputString on the value in rdx
    call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString]
    
    add rsp, 4 * 8
    pop rax     
    ret

codesize equ $ - $$

; contains nothing - but it is required by UEFI
section .reloc

; contains the data that will be displayed
section .data
    ; this must be a Unicode string
    strHello db __utf16__ `Hello World !\n\r\0`

datasize equ $ - $$

上面代码很简单,就是根据传入的参数调用 EFI_SYSTEM_TABLE.ConOut 来完成字符串显示。特别的,根据之前的经验要在调用ConOut的时候多向堆栈压入一个8字节内容,否则模拟器会崩溃。

2.使用 Nasm(推荐使用 NASM 2.14 win64 或者更高版本)编译生成 OBJ 文件:

nasm -f win64 nasmuefi.asm

3.使用 Link 来生成 EFI 文件。原文使用的参数不全,导致编译出来的EFI 文件无法运行。经过研究可以使用下面的参数:

link /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64  /DLL /ENTRY:_start  /SUBSYSTEM:EFI_APPLICATION /SAFESEH:NO /BASE:0 /DRIVER NasmUEFI.obj

(可以加入/DEBUG 生成带有 PDB文件名信息的EFI 文件)

        这样我们得到了 NasmUEFI.EFI 文件,大小是 768 bytes.

可以在NT32 模拟器上运行,同样的我在实体机上验证过也可以正常运行。

可以看到,汇编语言可以用来编写UEFI Application,相比C语言来说复杂的多。

完整的代码下载:

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>