Having trouble calling ExitBootServices from a bare-bones PE+ uefi application made with nasm

0

I have made a PE+ UEFI application in nasm and have been able to use EFI_BOOT_SERVICES to print text, to load the GOP, and when I call GetMemoryMap the return value is EFI_SUCCESS (0) and I can return safely to the EFI shell and I can reboot the (virtual) machine with a call to ResetSystem.

I can't get it to successfully exit the boot services.

My goal is to eventually generate the binary in a language other than c or assembly (oberon, for the curious) but to do that I have to be able to create a minimum viable uefi binary, and the last step of this stage is to get the uefi itself out of the way.

The following code prints 'here we go' and then it does not print 'fail' nor does it shut down:

;default rel
org 0x8000000
section .header

DOS:
    dd 0x00005a4d       
    times 14 dd 0
    dd 0x00000080
    times 16 dd 0

PECOFF:
        dd `PE\0\0`     ; sig
    dw 0x8664       ; type
    dw 3            ; sections
    dd 0x5cba52f6       ; timestamp
    dq 0            ; * symbol table + # symbols
    dw osize        ; oheader size
    dw 0x202e       ; characteristics

OHEADER:
    dd 0x0000020b       ; oheader + 0000 linker sig
    dd 8192 ;codesize       ; code size
    dd 8192 ;datasize       ; data size
    dd 0            ; uninitialized data size
    dd 4096         ; * entry
    dd 4096         ; * code base
    dq 0x8000000        ; * image base
    dd 4096         ; section alignment
    dd 4096         ; file alignment
    dq 0            ; os maj, min, image maj, min
    dq 0            ; subsys maj, min, reserved
    dd 0x5000       ; image size
    dd 4096         ; headers size
    dd 0            ; checksum
    dd 0x0040000A       ; dll characteristics & subsystem
    dq 0x10000      ; stack reserve size
    dq 0x10000      ; stack commit size
    dq 0x10000      ; heap reserve size
    dq 0            ; heap reserve commit
    dd 0            ; loader flags
    dd 0x10         ; rva count

DIRS:
    times 5 dq 0        ; unused
    dd 0x8005000        ; virtual address .reloc
    dd 0            ; size .reloc
        times 10 dq 0       ; unused 
OEND:
osize equ OEND - OHEADER

SECTS:
.1:
    dq  `.text`     ; name
    dd  8192 ;codesize      ; virtual size
    dd  4096        ; virtual address   
    dd  8192        ; raw data size
    dd  4096        ; * raw data
    dq  0           ; * relocations, * line numbers
    dd  0           ; # relocations, # line numbers
    dd  0x60000020      ; characteristics

.2:
        dq  `.data`
        dd  8192 ;datasize      
        dd  12288
        dd  8192 
        dd  12288       
        dq  0
        dd  0
        dd  0xC0000040      


.3:
    dq  `.reloc`
    dd  0   
    dd  20480
    dd  0
    dd  20480
    dq  0
    dd  0
    dd  0x02000040

    times 4096 - ($-$$) db 0  ;align the text section on a 4096 byte boundary

section .text follows=.header ; vstart=0x10001000

EFI_SUCCESS                                 equ 0
EFI_SYSTEM_TABLE_SIGNATURE                  equ 0x5453595320494249
EFI_SYSTEM_TABLE_CONOUT                         equ 64
EFI_SYSTEM_TABLE_RUNTIMESERVICES                equ 88
EFI_SYSTEM_TABLE_BOOTSERVICES                   equ 96

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET           equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING        equ 8

EFI_BOOT_SERVICES_GETMEMORYMAP              equ 56
EFI_BOOT_SERVICES_LOCATEHANDLE              equ 176
EFI_BOOT_SERVICES_LOADIMAGE             equ 200
EFI_BOOT_SERVICES_EXIT                  equ 216
EFI_BOOT_SERVICES_EXITBOOTSERVICES          equ 232
EFI_BOOT_SERVICES_LOCATEPROTOCOL            equ 320

EFI_RUNTIME_SERVICES_RESETSYSTEM            equ 104
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE           equ 24

EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERBASE   equ 32
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERSIZE   equ 40


    sub rsp, 6*8    
    mov [Handle], rcx         
    mov [SystemTable], rdx    

    ; find the interface to GOP
    mov rax, [SystemTable]
    mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
    mov rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID
    mov rdx, 0
    lea r8, [Interface]
    call [rax + EFI_BOOT_SERVICES_LOCATEPROTOCOL]

    mov rcx, [Interface]
    mov rcx, [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE]
    mov rbx, [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERBASE]
    mov [FB], rbx
    mov rcx, [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERSIZE]
    mov [FBS], rcx
        cmp rax, EFI_SUCCESS
        jne oops
g2:
       lea rdx, [herewego]
       mov rcx, [SystemTable]
       mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT]
       call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]

    ; get the memory map
    lea rcx, [MMSize]
    lea rdx, [MMap]
    lea r8, [MMKey]
    lea r9, [MMDsz]
    lea r10, [MMDsv]
    push r10
    mov rax, [SystemTable]
    mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
    call [rax + EFI_BOOT_SERVICES_GETMEMORYMAP]
    pop r10
    cmp rax, EFI_SUCCESS
    jne oops
        ; exit boot services
        mov rcx, [Handle]
        mov rdx, [MMKey]
        mov rax, [SystemTable]
        mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
        call [rax + EFI_BOOT_SERVICES_EXITBOOTSERVICES]
        cmp rax, EFI_SUCCESS
        je g5

        ; get the memory map
        lea rcx, [MMSize]
        lea rdx, [MMap]
        lea r8, [MMKey]
        lea r9, [MMDsz]
        lea r10, [MMDsv]
        push r10
        mov rax, [SystemTable]
        mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
        call [rax + EFI_BOOT_SERVICES_GETMEMORYMAP]
        pop r10
        cmp rax, EFI_SUCCESS
        jne oops
        ; exit boot services
        mov rcx, [Handle]
        mov rdx, [MMKey]
        mov rax, [SystemTable]
        mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
        call [rax + EFI_BOOT_SERVICES_EXITBOOTSERVICES]
        cmp rax, EFI_SUCCESS
        jne oops

g5:

       mov rcx, 2 ;EfiResetShutdown    
       mov rdx, EFI_SUCCESS            ; return status
       mov rax, [SystemTable]
       mov rax, [rax + EFI_SYSTEM_TABLE_RUNTIMESERVICES]
       call [rax + EFI_RUNTIME_SERVICES_RESETSYSTEM]

;   add rsp, 6*8
;   mov eax, EFI_SUCCESS 
;   retn
oops:
        lea rdx, [fail]
        mov rcx, [SystemTable]
        mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT]
        call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
    jmp $-1

    times 8192-($-$$) db 0  

codesize equ $ - $$

section .data follows=.text ;vstart=0x10003000    

Handle      dq 0
SystemTable dq 0
Interface   dq 0
FB      dq 0
FBS     dq 0
MMSize      dq 4096
MMPtr       dq 0x8004000
MMKey       dq 0
MMDsz       dq 48
MMDsv       dq 0

EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID db 0xde, 0xa9, 0x42, 0x90, 0xdc, 0x23, 0x38, 0x4a
                                  db 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a
fail        db __utf16__ `fail.\r\n\0`
nok     db __utf16__ `Not OK.\r\n\0`
yok     db __utf16__ `OK.\r\n\0`
herewego    db __utf16__ `here we go\r\n\0`

    times 4096-($-$$) db 0

MMap:
    times 4096 db 0

datasize equ $ - $$


section .reloc follows=.data ;align=64


In the code above I am calling GetMemoryMap and ExitBootServices twice... I see many places reporting that if you get an error the first time you must call it again as the memory map may change inside the first call to ExitBootServices.

I've put the whole thing up at https://github.com/charlesap/nasm-uefi including the OVMF.fd i'm using with QEMU to test and the qemu-system-x86_64 parameters I am using if that is helpful to see what I am doing wrong. There's not much to it, I am trying to do a very basic thing...

assembly
x86-64
nasm
uefi
asked on Stack Overflow Apr 25, 2019 by CharlesAP • edited Apr 29, 2019 by Michael Petch

1 Answer

0

This version does not hang up in exitbootservices, and can write to the framebuffer and/or reset the system with a call to runtimeservices. It misbehaves if the printhex calls are removed, so clearly it is not correct, but it does continue to execute, which is something...

bits 64
org 0x8000000
section .header

DOS:
    dd 0x00005a4d       
    times 14 dd 0
    dd 0x00000080
    times 16 dd 0

PECOFF:
        dd `PE\0\0`     ; sig
    dw 0x8664       ; type
    dw 3            ; sections
    dd 0x5cba52f6       ; timestamp
    dq 0            ; * symbol table + # symbols
    dw osize        ; oheader size
    dw 0x202e       ; characteristics

OHEADER:
    dd 0x0000020b       ; oheader + 0000 linker sig
    dd 8192 ;codesize       ; code size
    dd 8192 ;datasize       ; data size
    dd 0            ; uninitialized data size
    dd 4096         ; * entry
    dd 4096         ; * code base
    dq 0x8000000        ; * image base
    dd 4096         ; section alignment
    dd 4096         ; file alignment
    dq 0            ; os maj, min, image maj, min
    dq 0            ; subsys maj, min, reserved
    dd 0x5000       ; image size
    dd 4096         ; headers size
    dd 0            ; checksum
    dd 0x0040000A       ; dll characteristics & subsystem
    dq 0x10000      ; stack reserve size
    dq 0x10000      ; stack commit size
    dq 0x10000      ; heap reserve size
    dq 0            ; heap reserve commit
    dd 0            ; loader flags
    dd 0x10         ; rva count

DIRS:
    times 5 dq 0        ; unused
    dd 0x8005000        ; virtual address .reloc
    dd 0            ; size .reloc
        times 10 dq 0       ; unused 
OEND:
osize equ OEND - OHEADER

SECTS:
.1:
    dq  `.text`     ; name
    dd  8192 ;codesize      ; virtual size
    dd  4096        ; virtual address   
    dd  8192        ; raw data size
    dd  4096        ; * raw data
    dq  0           ; * relocations, * line numbers
    dd  0           ; # relocations, # line numbers
    dd  0x60000020      ; characteristics

.2:
        dq  `.data`
        dd  8192 ;datasize      
        dd  12288
        dd  8192 
        dd  12288       
        dq  0
        dd  0
        dd  0xC0000040      


.3:
    dq  `.reloc`
    dd  0   
    dd  20480
    dd  0
    dd  20480
    dq  0
    dd  0
    dd  0x02000040

    times 4096 - ($-$$) db 0  ;align the text section on a 4096 byte boundary

section .text follows=.header 

EFI_SUCCESS                                 equ 0
EFI_SYSTEM_TABLE_SIGNATURE                  equ 0x5453595320494249
EFI_SYSTEM_TABLE_CONOUT                         equ 64
EFI_SYSTEM_TABLE_RUNTIMESERVICES                equ 88
EFI_SYSTEM_TABLE_BOOTSERVICES                   equ 96

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET           equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING        equ 8

EFI_BOOT_SERVICES_GETMEMORYMAP              equ 56
EFI_BOOT_SERVICES_LOCATEHANDLE              equ 176
EFI_BOOT_SERVICES_LOADIMAGE             equ 200
EFI_BOOT_SERVICES_EXIT                  equ 216
EFI_BOOT_SERVICES_EXITBOOTSERVICES          equ 232
EFI_BOOT_SERVICES_LOCATEPROTOCOL            equ 320

EFI_RUNTIME_SERVICES_RESETSYSTEM            equ 104


 sub rsp, 6*8    
 mov [Handle], rcx         
 mov [SystemTable], rdx    

 mov rax, [SystemTable]
 mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES]
 mov [BS], rax

 mov rax, [SystemTable]
 mov rax, [rax + EFI_SYSTEM_TABLE_RUNTIMESERVICES]
 mov [RTS], rax

 lea rdx, [herewego]
 mov rcx, [SystemTable]
 mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT]
 call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]

 ; get the memory map
 mov qword [memmapsize], 4096
 lea rcx, [memmapsize]
 lea rdx, [memmap]
 lea r8, [memmapkey]
 lea r9, [memmapdescsize]
 lea r10, [memmapdescver]
 mov [STK],rsp
 push r10
 sub rsp, 4*8
 mov rbx, [BS]
 call [rbx + EFI_BOOT_SERVICES_GETMEMORYMAP]
 add rsp, 4*8
 pop r10
 mov rsp, [STK]
 cmp rax, EFI_SUCCESS
 jne oops

 ; find the interface to GOP
 mov rbx, [SystemTable]
 mov rbx, [rbx + EFI_SYSTEM_TABLE_BOOTSERVICES]
 mov rcx, _EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID
 mov rdx, 0
 lea r8, [Interface]
 call [rbx + EFI_BOOT_SERVICES_LOCATEPROTOCOL]
 cmp rax, EFI_SUCCESS
 jne oops

 mov rcx, [Interface]
 mov rcx, [rcx + 0x18 ] ;EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE
 mov rbx, [rcx + 0x18 ] ;EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERBASE
 mov [FB], rbx
 mov rcx, [rcx + 0x20 ] ;EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE_FRAMEBUFFERSIZE
 mov [FBS], rcx
 cmp rax, EFI_SUCCESS
 jne oops

       mov rbx, [FB]
       push rax
       push rcx
       push rdx
       call printhex
       pop rdx
       pop rcx
       pop rax

       mov rbx, [FBS]
       push rax
       push rcx
       push rdx
       call printhex
       pop rdx
       pop rcx
       pop rax

 ; exit boot services
 mov rcx, [Handle]
 mov rdx, [memmapkey]
 mov rbx, [SystemTable]
 mov rbx, [rbx + EFI_SYSTEM_TABLE_BOOTSERVICES]
 call [rbx + EFI_BOOT_SERVICES_EXITBOOTSERVICES]
 cmp rax, EFI_SUCCESS
 je g5

       mov rbx, [memmapkey]
       push rax
       push rcx
       push rdx
       call printhex
       pop rdx
       pop rcx
       pop rax

 ; repeat the call to get the memory map
 mov qword [memmapsize], 4096
 lea rcx, [memmapsize]
 lea rdx, [memmap]
 lea r8, [memmapkey]
 lea r9, [memmapdescsize]
 lea r10, [memmapdescver]
 mov [STK],rsp
 push r10
 sub rsp, 4*8
 mov rbx, [BS]
 call [rbx + EFI_BOOT_SERVICES_GETMEMORYMAP]
 add rsp, 4*8
 pop r10
 mov rsp, [STK]
 cmp rax, EFI_SUCCESS
 jne oops

       mov rbx, [memmapkey]
       push rax
       push rcx
       push rdx
       call printhex
       pop rdx
       pop rcx
       pop rax

 ; exit boot services again
 mov rcx, [Handle]
 mov rdx, [memmapkey]
 xor r8, r8
 mov rbx, [SystemTable]
 mov rbx, [rbx + EFI_SYSTEM_TABLE_BOOTSERVICES]
 call [rbx + EFI_BOOT_SERVICES_EXITBOOTSERVICES]
 ;cmp rax, EFI_SUCCESS
 ;je g5
 ;jmp oops

 mov rcx, [FB]
 mov rax, [FBS]
Q:
 dec rax
 mov byte[rcx+rax],255
 jnz Q

W:
 jmp W

g5:
 mov rcx, 2 ;EfiResetShutdown    
 mov rdx, EFI_SUCCESS
 mov rax, [SystemTable]
 mov rax, [rax + EFI_SYSTEM_TABLE_RUNTIMESERVICES]
 call [rax + EFI_RUNTIME_SERVICES_RESETSYSTEM]

oops:
 lea rdx, [fail]
 mov rcx, [SystemTable]
 mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT]
 call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
 jmp $-1

printhex:
 mov rbp, 16
.loop:
    rol rbx, 4
    mov rax, rbx
    and rax, 0Fh
    lea rcx, [_Hex]
    mov rax, [rax + rcx]
    mov byte [_Num], al
        lea rdx, [_Num]
        mov rcx, [SystemTable]
        mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT]
        call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
    dec rbp
 jnz .loop
 lea rdx, [_Nl]
 mov rcx, [SystemTable]
 mov rcx, [rcx + EFI_SYSTEM_TABLE_CONOUT] 
 call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]

 ret

    times 8192-($-$$) db 0  

codesize equ $ - $$

section .data follows=.text 

Handle          dq 0
SystemTable     dq 0
Interface       dq 0
BS      dq 0
RTS     dq 0
STK         dq 0
FB              dq 0
FBS             dq 0
memmapsize      dq 4096
memmapkey       dq 0
memmapdescsize  dq 48
memmapdescver   dq 0

_EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID db 0xde, 0xa9, 0x42, 0x90, 0xdc, 0x23, 0x38, 0x4a
                                  db 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a
fail     db __utf16__ `fail.\r\n\0`
nok      db __utf16__ `Not OK.\r\n\0`
yok      db __utf16__ `OK.\r\n\0`
herewego db __utf16__ `here we go\r\n\0`
_Hex     db '0123456789ABCDEF'
_Num     dw 0,0
_Nl      dw 13,10,0

    times 4096-($-$$) db 0

memmap:
    times 4096 db 0

datasize equ $ - $$


section .reloc follows=.data 


answered on Stack Overflow Apr 28, 2019 by CharlesAP

User contributions licensed under CC BY-SA 3.0