OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 1:33 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Simple Assembly UEFI Application -- Can't Exit Boot Services
PostPosted: Fri Apr 26, 2019 4:18 pm 
Offline

Joined: Fri Apr 26, 2019 3:46 pm
Posts: 6
I can make a simple pe+ UEFI application with fasm or nasm, I can output text using SIMPLE_TEXT_OUTPUT_INTERFACE, I can get the memory map using EFI_BOOT_SERVICES, I can reset the system using EFI_SYSTEM_TABLE.RuntimeServices but I cannot get EFI_BOOT_SERVICES.ExitBootServices to work.

I've searched OSDev and scoured the Internet... I've gotten desperate enough to actually ask a question. Is there something obvious I'm missing?

I've put the source assembly and the one assembly include file and build instructions I am following up on https://github.com/charlesap/fasm-uefi in case that's where the real problem is.

I expect the following code to use RuntimeServices to reboot after completing ExitBootServices. It just hangs. It will reboot just fine if I comment out the ExitBootServices part.

Help?

Code:
format pe64 dll efi
entry main

section '.text' code executable readable

include 'efi.inc'

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

mov [Handle], rcx         ; ImageHandle
mov [SystemTable], rdx    ; pointer to SystemTable

lea rdx, [_hello]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]

; get the memory map

mov dword [memmapdescsize], 48
lea rcx, [memmapsize]
mov qword [rcx], 4096
lea rdx, [memmapbuff]
lea r8, [memmapkey]
lea r9, [memmapdescsize]
lea rax, [memmapdescver]
push rax
mov rax, [SystemTable]
mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
call [rax + EFI_BOOT_SERVICES.GetMemoryMap]
pop rcx
test rax, rax
jnz oops

mov rcx, [Handle]
mov rdx, [memmapkey]
mov rax, [SystemTable]
mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
call [rax + EFI_BOOT_SERVICES.ExitBootServices]
cmp rax, EFI_SUCCESS
je reboot

; if first time fails, second time is supposed to succeed:

mov dword [memmapdescsize], 48
lea rcx, [memmapsize]
mov qword [rcx], 4096
lea rdx, [memmapbuff]
lea r8, [memmapkey]
lea r9, [memmapdescsize]
lea rax, [memmapdescver]
push rax
mov rax, [SystemTable]
mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
call [rax + EFI_BOOT_SERVICES.GetMemoryMap]
pop rcx
test rax, rax
jnz oops

mov rcx, [Handle]
mov rdx, [memmapkey]
mov rax, [SystemTable]
mov rax, [rax + EFI_SYSTEM_TABLE.BootServices]
call [rax + EFI_BOOT_SERVICES.ExitBootServices]
   

reboot:
mov rax, [SystemTable]
mov rax, [rax + EFI_SYSTEM_TABLE.RuntimeServices]
mov rcx, EfiResetShutdown
mov rdx, EFI_SUCCESS
xor r8, r8
xor r9, r9
call [rax + EFI_RUNTIME_SERVICES.ResetSystem]
jmp oops

   
oops:
lea rdx, [_nok]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]

jmp $-1

add rsp, 4*8
mov eax, 0 ; was EFI_SUCCESS
retn


section '.data' data readable writeable

memmapbuff: rb 4096

Handle      dq 0
SystemTable dq 0

memmapsize:     dq 4096
memmapkey:      dq 0
memmapdescsize: dq 0
memmapdescver:  dq 0

_yok      du 'ok.',13,10,0
_nok     du 'not ok.',13,10,0
_hello du 'hello world',13,10,0


section '.reloc' fixups data discardable


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 5:07 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
Hi,

Code:
call [rax + EFI_BOOT_SERVICES.GetMemoryMap]
It's a miracle if your code does anything at all, the UEFI ABI is not like that. You can only pass 4 arguments in registers, the others must be in stack, and you must preserve space for those 4 arguments. Also you must pass the number of arguments as a first argument on every function call. Therefore the address of the memory key (required by ExitBootServices) is NOT passed in r8.

Check out the wiki Uefi.inc. There the uefi_call_wrapper macro counts the arguments and calls uefifunc with SysV-like ABI (passing argument count in AL, and the uefi func in RBX). It worth mentioning that it also calculates function addresses dynamically. Then uefifunc arranges the stack and registers properly and calls the real function using the fastcall-like UEFI ABI.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 3:09 pm 
Offline

Joined: Fri Apr 26, 2019 3:46 pm
Posts: 6
Thanks for the pointer. Perhaps uefi.inc can help... I hope to make raw calls as simply as possible. Clearly I'm missing something!


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 3:55 pm 
Offline
Member
Member
User avatar

Joined: Fri Feb 17, 2017 4:01 pm
Posts: 640
Location: Ukraine, Bachmut
Quote:
You can only pass 4 arguments in registers, the others must be in stack

he does this.

Quote:
Also you must pass the number of arguments as a first argument on every function call.

WHAT? it's BS. there is no such a thing in the UEFI spec.

As of the OP problem, you don't need to call both GetMemoryMap() and ExitBootServices() twice, it's wrong. I mean - testing only for zero (EFI_SUCCESS) after the first call to GetMemoryMap() is not enough. And also wrong to just duplicate code after the fail without any actions. The algorithm for dealing with this pair is like this:
Code:
/* you first call GetMemoryMap() to learn how big memory map is going to be
* passing some reasonable sized buffer
*/
PVOID Buffer;
St = AllocatePool(EfiLoaderData, MemoryMapSize, &Buffer);
...
Counter = 1;
Addendum = 0x1000; /* modify it if you see, that this is too small and function doesn't succeed quickly */
do{
  Status = GetMemoryMap(&MemoryMapSize, (EFI_MEMORY_DESCRIPTOR *)Buffer, &MapKey, &DescriptorSize, &DescriptorVersion);
  switch(Status){
    case EFI_BUFFER_TOO_SMALL:
      Counter++;
      FreePool(Buffer); /* or FreePages() depending on what allocation mechanism you were using */
      MemoryMapSize += Addendum;
      St = AllocatePool(EfiLoaderData, MemoryMapSize, &Buffer);
      if(St != EFI_SUCCESS)
          Fail("Can't allocate memory for the memory map.");
      break;
   case EFI_INVALID_PARAMETER:
      Fail("My loader has a bug.");
  }
}while(Status == EFI_BUFFER_TOO_SMALL);

DebugPrint("GetMemoryMap succeeded from the %d time.", Count); /* for addendum tuning */
Status = ExitBootServices(MyHandle, MapKey);
if(Status == EFI_SUCCESS){
    /* here, you do your crazy stuff, BUT without relying on Boot Services */
}else{
    Fail("I passed a wrong MapKey to ExitBootServices().");
}

And yet, may I give an advice? Write in C, don't torturize your 4ss. :D At least UEFI part; it's specially been done for C writing.

Quote:
Thanks for the pointer. Perhaps uefi.inc can help... I hope to make raw calls as simply as possible. Clearly I'm missing something!

I'd not rely on those pointers, since they are obviously broken, bzt often claims things that are true only for him.

_________________
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).


Last edited by zaval on Sat Apr 27, 2019 5:03 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 4:42 pm 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
zaval wrote:
Quote:
Also you must pass the number of arguments as a first argument on every function call.

WHAT? it's BS. there is no such a thing in the UEFI spec.


I concur.

zaval wrote:
And yet, may I give an advice? Write in C, don't torturize your 4ss. :D At least UEFI part; it's specially been done for C writing.


I concur. Why torture yourself with assembly when you don't need it. Here is another example from my own code base:

https://github.com/kiznit/rainbow-os/bl ... #L146-L184

Code:
static EFI_STATUS ExitBootServices()
{
    UINTN size = 0;
    UINTN allocatedSize = 0;
    EFI_MEMORY_DESCRIPTOR* descriptors = nullptr;
    UINTN memoryMapKey = 0;
    UINTN descriptorSize = 0;
    UINT32 descriptorVersion = 0;

    // 1) Retrieve the memory map from the firmware
    EFI_STATUS status;
    while ((status = BS->GetMemoryMap(&size, descriptors, &memoryMapKey, &descriptorSize, &descriptorVersion)) == EFI_BUFFER_TOO_SMALL)
    {
        // Extra space to play safe with "partial shutdown" when calling ExitBootServices().
        size += descriptorSize * 10;

        descriptors = (EFI_MEMORY_DESCRIPTOR*)realloc(descriptors, size);
        if (!descriptors)
        {
            return EFI_OUT_OF_RESOURCES;
        }

        allocatedSize = size;
    }

    // 2) Exit boot services - it is possible for the firmware to modify the memory map
    // during a call to ExitBootServices(). A so-called "partial shutdown".
    // When that happens, ExitBootServices() will return EFI_INVALID_PARAMETER.
    while ((status = BS->ExitBootServices(g_efiImage, memoryMapKey)) == EFI_INVALID_PARAMETER)
    {
        // Memory map changed during ExitBootServices(), the only APIs we are allowed to
        // call at this point are GetMemoryMap() and ExitBootServices().
        size = allocatedSize;
        status = BS->GetMemoryMap(&size, descriptors, &memoryMapKey, &descriptorSize, &descriptorVersion);
        if (EFI_ERROR(status))
        {
            return status;
        }
    }

    return status;
}

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 6:22 pm 
Offline

Joined: Fri Apr 26, 2019 3:46 pm
Posts: 6
It is my intention to get away from assembly as soon as I can... but my goal is to move to a native Oberon instead of c, which somewhat constrains my toolchain...

Also I have banged on the code for long enough that it has started working and now I have to figure out why.

I'll post an update.


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 6:38 pm 
Offline
Member
Member

Joined: Mon Feb 02, 2015 7:11 pm
Posts: 898
charlesap wrote:
It is my intention to get away from assembly as soon as I can... but my goal is to move to a native Oberon instead of c, which somewhat constrains my toolchain...


Surely you can use C to bootstrap your Oberon runtime?

charlesap wrote:
Also I have banged on the code for long enough that it has started working and now I have to figure out why.


Well that's good news (hopefully). Getting UEFI to cooperate is rather painful.

_________________
https://github.com/kiznit/rainbow-os


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sat Apr 27, 2019 9:26 pm 
Offline

Joined: Fri Apr 26, 2019 3:46 pm
Posts: 6
So here's the code that works (successfully calls ExitBootServices and then successfully calls ResetSystem) :

Code:
format pe64 dll efi
entry main

section '.text' code executable readable

include 'efi.inc'

main:
sub rsp, 6*8              ; reserve space for 6 arguments

mov [Handle], rcx         ; ImageHandle
mov [SystemTable], rdx    ; pointer to SystemTable

lea rdx, [_hello]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]

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, [SystemTable]
mov rbx, [rbx + EFI_SYSTEM_TABLE.BootServices]
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


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 reboot

; if first time fails, second time is supposed to succeed:

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 rax, 5
mov rbx, [SystemTable]
mov rbx, [rbx + EFI_SYSTEM_TABLE.BootServices]
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

mov rcx, [Handle]
mov rdx, [memmapkey]
mov rbx, [SystemTable]
mov rbx, [rbx + EFI_SYSTEM_TABLE.BootServices]
call [rbx + EFI_BOOT_SERVICES.ExitBootServices]

reboot:

mov rbx, [SystemTable]
mov rbx, [rbx + EFI_SYSTEM_TABLE.RuntimeServices]

mov rcx, EfiResetShutdown
mov rdx, EFI_SUCCESS
xor r8, r8
call [rbx + EFI_RUNTIME_SERVICES.ResetSystem]
jmp oops

   
oops:
lea rdx, [_nok]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
A:
jmp A

eek:
lea rdx, [_eek]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
B:
jmp B


boop:
lea rdx, [_boop]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
ret


add rsp, 6*8
mov eax, 0 ; was EFI_SUCCESS
retn

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 + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
   dec rbp
jnz .loop
lea rdx, [_Nl]
mov rcx, [SystemTable]
mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
call [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]

ret


section '.data' data readable writeable
memmap:       times 4096 db 0
Handle      dq 0
SystemTable dq 0
;RTS       dq 0
;BS       dq 0
STK       dq 0
ptrmemmap:   dq 0
memmapsize:     dq 0
memmapkey:      dq 0
memmapdescsize: dq 0
memmapdescver:  dq 0

_yok      du 'ok.',13,10,0
_nok     du 'not ok.',13,10,0
_eek      du 'eek!',13,10,0
_boop     du 'boop',13,10,0
_hello du 'hello world',13,10,0
_Hex               db '0123456789ABCDEF'
_Num               dw 0,0
_Nl               dw 13,10,0



section '.reloc' fixups data discardable




The weird thing is that if I comment out either block where I am using printhex to output the memmapkey, the code hangs inside ExitBootServices. If I don't comment out the printhex calls I can go on with the uefi disabled and I can reboot the system, I can write to the framebuffer, etc. as one would expect.


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 1:48 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 20, 2011 2:01 pm
Posts: 110
zaval wrote:
Quote:
You can only pass 4 arguments in registers, the others must be in stack

he does this.

Quote:
Also you must pass the number of arguments as a first argument on every function call.

WHAT? it's BS. there is no such a thing in the UEFI spec.

It kind of is in the UEFI spec. You need a 16 byte aligned stack, using the "C calling convention"
UEFI Spec wrote:
The caller passes the first four integer arguments in registers. 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. All arguments must be right-justified in the register in which they are passed. This ensures the callee can process only the bits in the register that are required.
The caller passes arrays and strings via a pointer to memory allocated by the caller. The caller passes structures and unions of size 8, 16, 32, or 64 bits as if they were integers of the same size. The caller is not allowed to pass structures and unions of other than these sizes and must pass these unions and structures via a pointer.
The callee must dump the register parameters into their shadow space if required. The most common requirement is to take the address of an argument.

This is the MS x64 calling convention - https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019

_________________
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 4:02 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
charlesap wrote:
The weird thing is that if I comment out either block where I am using printhex to output the memmapkey, the code hangs inside ExitBootServices. If I don't comment out the printhex calls I can go on with the uefi disabled and I can reboot the system, I can write to the framebuffer, etc. as one would expect.
Keep in mind that print (I mean Simple Text Output protocol) may or may not allocate temporary variables depending on it's arguments. Your memmapkey is only valid if the memory map does not change. Therefore you must avoid calling anything that may allocate or free memory between the GetMemMap and ExitBootServices, otherwise ExitBootServices will always return with an "Invalid key" error.

What you wrote seems to be quite the opposite, which is strange. Without digging deep into your code it looks like you pass the wrong argument somehow, and printhex somehow sets it in the correct register too. I would recommend to use a debugger and check the input arguments for ExitBootServices with and without printhex, inputs should be the same.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 9:35 am 
Online
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
Why is it with some EFI calls you allocate the required space (32 bytes) before making a call and other places you don't. You need the shadow space allocated on the stack before each EFI call. Maybe I'm missing something, but most of your calls seem to have this missing. Maybe I have misread your code and you are doing it.


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 12:26 pm 
Offline

Joined: Fri Apr 26, 2019 3:46 pm
Posts: 6
While I do make some room on the stack at the very beginning of my routine (that's the sub rsp, 6*8) I think maybe the garbage in the shadow space may be what is tripping up ExitBootServices. Perhaps. Gonna have to test that. That's the only way I figure the pushes and pops of printhex would make a difference.


Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 4:17 pm 
Offline
Member
Member
User avatar

Joined: Fri Feb 17, 2017 4:01 pm
Posts: 640
Location: Ukraine, Bachmut
MichaelPetch wrote:
Why is it with some EFI calls you allocate the required space (32 bytes) before making a call and other places you don't. You need the shadow space allocated on the stack before each EFI call. Maybe I'm missing something, but most of your calls seem to have this missing. Maybe I have misread your code and you are doing it.

No. Every function needs to do this only once (at its beginning). It allocates at the end of its stack (topmost) the number of slots for parameters. The number of slots is the number of parameters of a function from the set of functions, that this function calls, that has the biggest number of parameters. :twisted: Say, here, the main function calls several functions, from which GetMemoryMap() has the largest count of parameters - 5. So, the main function allocates 5 slots in its stack frame for the parameters.

I looked deeper at the code. It shows not understanding the calling convention. If I remembered x86 asm, I'd write it, but. For example, you don't need all those manipulations with rsp before a function call.
A stack frame of a function must be like this:
[saved nonvolatile registers] --- highest address
[your stack variables]
[argN] --- N the biggest number of a parameter from the set of functions this function will call
[arg(N-1)]
...
[arg5]
[r9] --- slot for r9, just have it, not initialize
[r8]
[rdx]
[rcx] --- the lowest address, rsp points here before any function call

So when calling GetMemoryMap() and doing:
Code:
lea r10, [memmapdescver]
mov [STK],rsp
push r10

you place the 5th parameter in a wrong position.
instead, you need:
Code:
lea r10, [memmapdescver]
mov [rsp + 4*8], r10    ; see scheme above

_________________
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).


Last edited by zaval on Sun Apr 28, 2019 7:17 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Sun Apr 28, 2019 5:24 pm 
Offline
Member
Member
User avatar

Joined: Fri Feb 17, 2017 4:01 pm
Posts: 640
Location: Ukraine, Bachmut
Ok, try this. But remember, I wrote in x86 a long time ago. :mrgreen:

Code:
format pe64 dll efi
entry main

section '.text' code executable readable

include 'efi.inc'

main:
   push   rbx
   push   r12
   sub   rsp, 5 * 8   ; keeping it 16 byte aligned

   mov   [Handle], rcx   ; ImageHandle
   mov   rbx, rdx   ; SystemTable
   mov   r12, [rdx + EFI_SYSTEM_TABLE.BootServices]

   lea   rdx, [szHello]
   mov   rcx, [rbx + EFI_SYSTEM_TABLE.ConOut]
   call   [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]

   mov   rdx, [memmapsize]
a:   mov   rcx, EfiLoaderData
   lea   r8, [memmap]
   call   [r12 + EFI_BOOT_SERVICES.AllocatePool]
   cmp   rax, EFI_SUCCESS
   je   b
   ; Fail
   lea   rdx, [szAllocFail]
   mov   rcx, [rbx + EFI_SYSTEM_TABLE.ConOut]
   call   [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
   jmp   Fail

b:   lea   rcx, [memmapsize]
   mov   rdx, [memmap]
   lea   r8, [memmapkey]
   lea   r9, [memmapdescsize]
   lea   r10, [memmapdescver]
   mov   [rsp + 4 * 8], r10
   call   [r12 + EFI_BOOT_SERVICES.GetMemoryMap]

   cmp   rax, EFI_SUCCESS
   je   d
   cmp   rax, EFI_BUFFER_TOO_SMALL
   je   c
   ; Fail
   lea   rdx, [szGetMemMapFail]
   mov   rcx, [rbx + EFI_SYSTEM_TABLE.ConOut]
   call   [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
   jmp   Fail

c:   mov   rcx, [memmap]
   call   [r12 + EFI_BOOT_SERVICES.FreePool]
   mov   rdx, [memmapsize]
   add   rdx, 400h
   mov   [memmapsize], rdx
   jmp   a

d:   mov   rcx, [Handle]
   mov   rdx, [memmapkey]
   call   [r12 + EFI_BOOT_SERVICES.ExitBootServices]
   cmp   rax, EFI_SUCCESS
   je   reboot
   ; Fail
   lea   rdx, [szExitBootServFail]
   mov   rcx, [rbx + EFI_SYSTEM_TABLE.ConOut]
   call   [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
   jmp   Fail

reboot:
   mov   rax, [rbx + EFI_SYSTEM_TABLE.RuntimeServices]
   mov   rcx, EfiResetShutdown
   mov   rdx, EFI_SUCCESS
   xor   r8, r8
   call   [rax + EFI_RUNTIME_SERVICES.ResetSystem]

Fail:   mov   rax, EFI_NOT_READY   ; what else? :D
   add   rsp, 5 * 8
   pop   r12
   pop   rbx
   ret
   


section '.data' data readable writeable
memmap:      dq 0   ; this is a pointer to a buffer for the memory map
Handle:      dq 0
SystemTable:   dq 0
memmapsize:     dq 1000h
memmapkey:      dq 0
memmapdescsize: dq 0
memmapdescver:  dq 0

szHello         du 'hello, world', 13, 10, 0
szAllocFail      du 'allocation failed', 13, 10, 0
szGetMemMapFail      du 'getting memory map failed', 13, 10, 0
szExitBootServFail   du 'exiting boot services failed', 13, 10, 0



section '.reloc' fixups data discardable


Added later.
1. fixed a very unpleasant bug (memmap was db instead of dq).
2. fixed missed return address influence on the stack alignment

_________________
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).


Last edited by zaval on Mon Apr 29, 2019 3:31 pm, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: Simple Assembly UEFI Application -- Can't Exit Boot Serv
PostPosted: Mon Apr 29, 2019 2:11 pm 
Online
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
A version of the code that handles the stack alignment; moves the saving and restoring of registers into the function print_hex; properly handles the shadow space could look like:

Code:
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+8    ; Stack is misaligned by 8 when control is transferred to
                   ; the EFI entry point. In addition to the shadow space
                   ; (32 bytes) and space for stack based paramaters to be
                   ; saved - we also have to allocate an additional
                   ; 8 bytes to ensure stack alignment on a 16-byte boundary
                   ; 8+(6*8+8)=64, 64 is evenly divisible by 16 at this point

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 [rsp+32], r10         ; Don't push R10 on the stack, move it directly to
                           ; the stack immediately above the shadow space
mov rbx, [BS]
call [rbx + EFI_BOOT_SERVICES_GETMEMORYMAP]
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]
       call printhex

       mov rbx, [FBS]
       call printhex

; 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
je fillframe

       mov rbx, [memmapkey]
       call printhex

; 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 rbx, [BS]
mov [rsp+32], r10         ; Don't push R10 on the stack, move it directly to
                           ; the stack immediately above the shadow space
call [rbx + EFI_BOOT_SERVICES_GETMEMORYMAP]
cmp rax, EFI_SUCCESS
jne oops

       mov rbx, [memmapkey]
       call printhex

; 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

fillframe:
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 $

printhex:
                         ; Stack msialigned by 8 at function entry
mov rbp, 16
push rax
push rcx
push rdx                ; 3 pushes also align stack on 16 byte boundary
                         ; (8+3*8)=32, 32 evenly divisible by 16
sub rsp, 32             ; Allocate 32 bytes of shadow space
.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]

add rsp, 32
pop rdx
pop rcx
pop rax
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
This code was also fixed so that when the first call to ExitBootServices succeeds it also fills the frame buffer (rather than shutting down). The main entry point should save and restore the volatile registers (and restore them when finished) hoeever given that your code has no path to get to the final `ret` it can be overlooked in this case.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot], MichaelPetch and 54 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group