5

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.

Peter Frost
  • 351
  • 3
  • 6
  • I tried to rewrite your code in C `SystemTable->BootServices->CreateEvent(EVT_TIMER, TPL_APPLICATION, NULL, NULL, &ptrTimerEvent)`, it returns `EFI_SUCCESS`. It seems to be there are some problems in your definitions or passing the 5th argument by stack. – KagurazakaKotori Mar 08 '20 at 07:40
  • And my compiler puts `ptrTimerEvent` at `[rsp+0x20]`, I cannot figure out why. – KagurazakaKotori Mar 08 '20 at 07:52

1 Answers1

6

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
...
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
prl
  • 11,716
  • 2
  • 13
  • 31
  • Thank you for reply, I just have a few questions. So when the UEFI firmware calls the entry point (`_start`) is it unaligned because of the return address put on the stack? Does that mean in any function you access with a `call` that you should decrement the stack pointer by 8 if you're going to be doing any other function calls involving the stack within that function? – Peter Frost Mar 08 '20 at 18:25
  • Also, just to make sure I've understood that, when you're adding that 5th argument to the stack, could you instead do a `push rax` and then `sub rsp, 0x18` to get the 0x20 offset before the function call. And change the `sub rsp, 0x28` at the begining to just `sub rsp, 0x8` – Peter Frost Mar 08 '20 at 18:29
  • 1
    For your first question, yes you understand the alignment correctly. But you should decrement the stack pointer by 0x28 (or more), not just 8. – prl Mar 08 '20 at 20:56
  • 1
    For your second question, no, the fifth parameter has to be at rsp+0x20 before the call. The steps you propose would put it at rsp+0x18. If you remove the `sub rsp, 0x28` at the beginning, then you can do `push rax` and `sub rsp, 0x20` before the call. – prl Mar 08 '20 at 20:58