Search code examples
assemblyx86x86-64nasmuefi

UEFI boot services CreateEvent() returning status EFI_INVALID_PARAMETER


I'm writing a simple UEFI application in NASM assembly, and I'm trying to make an event for a free-running timer, but the call to CreateEvent always returns EFI_INVALID_PARAMETER and I'm not sure why.

section .text

_start:
    mov [ptrSystemTable], rdx
...
    mov rcx, EVT_TIMER
    mov rdx, TPL_APPLICATION
    mov r8, 0
    mov r9, 0
    lea rbx, [ptrTimerEvent]
    push rbx

    mov rax, [ptrSystemTable]
    mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
    call [rax + EFI_BOOT_SERVICES.CreateEvent]

    cmp rax, EFI_SUCCESS
    jne errorCode
...
section .data    
    ptrSystemTable  dq  0          
    ptrTimerEvent   dq  0
; Include file with a bunch of definitions and structures

EVT_TIMER               equ 0x80000000
TPL_APPLICATION         equ 4

%macro POINTER 0
    RESQ 1
    alignb 8
%endmacro

struc EFI_SYSTEM_TABLE
...
    .BootServices       POINTER
...
endstruc

struc EFI_BOOT_SERVICES
...
    .CheckEvent         POINTER
...
endstruc

According to 2.3.4.2 Detailed Calling Conventions in the UEFI spec:

The integer values are passed from left to right in Rcx, Rdx, R8, and R9 registers. The caller passes arguments five and above onto the stack.

So the arguments I'm passing should be:

 Type --> EVT_TIMER
 NotifyTpl --> TPL_APPLICATION
 NotifyFunction --> 0
 *NotifyContext --> 0
 *Event --> &ptrTimerEvent  

The spec gives multiple reasons why CreateEvent could return EFI_INVALID_PARAMETER, but I can't see how any of them are occurring in my code. Any pointers or questions would be greatly appreciated.


Solution

  • The calling convention for UEFI always has 0x20 bytes of free space at the top of the stack before the call. The fifth and subsequent parameters, if present, start at offset 0x20 on the stack.

    It's the same as the Windows / Microsoft x64 calling convention.

    Also, the stack must be aligned to a multiple of 0x10 before the call. The stack is aligned to an odd multiple of 8 when the function is entered, so you must adjust the stack pointer by an odd multiple of 8 within the function.

    default rel          ; Use RIP-relative symbol addressing, not absolute
    
    section .text
    
    _start:
        sub rsp, 0x28              ; 0x20 bytes of shadow space + 8 to align the stack
        mov [ptrSystemTable], rdx
    ...
        mov rcx, EVT_TIMER
        mov rdx, TPL_APPLICATION
        mov r8, 0
        mov r9, 0
        lea rax, [ptrTimerEvent]
        mov [rsp+0x20], rax         ; 5th arg on the stack above the shadow space
    
        mov rax, [ptrSystemTable]
        mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
        call [rax + EFI_BOOT_SERVICES.CreateEvent]
    
        cmp rax, EFI_SUCCESS
        jne errorCode
    ...