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...
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
User contributions licensed under CC BY-SA 3.0