format pe64 dll efi entry main ; The UEFI calling convention is, as told in the documentation, the same ; as the standard cdecl but for convenience (because it looks like cdecl ; isn't that much standardized) here it is (in C-like notation): ; ; rax func(rcx, rdx, r8, r9, rsp+32, rsp+64, ...) ; ; basically the rest of arguments is passed through the stack with 32bit ; offset as it was designed for variadic functions ; Also important fact about uefi is that uefi apps are called images. ; for further reading I think I recommend the official uefi documentation ; I'm not sure because maybe there might be better source ; macro that saves the most important registers and calls the function macro call_safe func { push rcx push rbx call func pop rbx pop rcx } section '.text' code executable readable include 'uefi.inc' main: ; storing the args that the main function is called with ; main(*ImageHandle, *SystemTable) mov [ImgHdl], rcx mov [SysTbl], rdx sub rsp, 6*8+8 ; storing the most used structures for faster use mov rax, [rdx + EFI_SYSTEM_TABLE.BootServices] mov [BootSrvc], rax mov rax, [rdx + EFI_SYSTEM_TABLE.ConOut] mov [Output], rax ; preparing args and calling ; ConOut.SetAttribute(Output, BG_CLR | FG_CLR) mov rax, EFI_BACKGROUND_BLACK or EFI_GREEN mov rcx, [Output] call_safe [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.SetAttribute] ; clearing the screen ; ConOut.ClearScreen(ConOut) mov rcx, [Output] call_safe [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen] ; printing the initial message to indicate ; that the bootloader started successfully mov rdx, BootMsg call print ; TODO: Get and store framebuffer ; Getting the memory map is kind of harder (not in this case), normally ; you would need to call the function two times to get the buffer size ; then allocate it (+ some padding because it will expand because of the ; allocation) and then call it again, but as I'm storing it somewhere ; in memory, I'm not using for now, I can just call it two times without ; the allocation. ; BootServices.GetMemoryMap( ; *MemMapSize, ; *MemMapBuffer, ; *MemMapKey, ; *MemMapDescSize, ; *MemMapDescVer) .GetMemMap: lea rcx, [MemMapSize] mov rdx, [MemMapBuff] lea r8, [MemMapKey] lea r9, [MemMapDescSize] ; notice the last argument is moved to r10 and then "pushed" to the stack ; with 32bit offset lea r10, [MemMapDescVer] mov [rsp+32], r10 call [BootSrvc + EFI_BOOT_SERVICES_TABLE.GetMemoryMap] ; if it returns buffer too small run the function again as it filled out ; the needed buffer length cmp al, EFI_BUFFER_TOO_SMALL je .GetMemMap ; if it returned something else it maybe a problem possible TODO: proper ; error handling for now it just puts out error and exits with non-zero code cmp rax, EFI_SUCCESS jne .error ; print a message saying that we didn't f**k up yet... mov rdx, OKMsg call print ; preparing args and calling ; BootServices.ExitBootServices(*ImegeHandle, *MemMapKey) mov rcx, [ImgHdl] mov rdx, [MemMapKey] call_safe [BootSrvc + EFI_BOOT_SERVICES_TABLE.ExitBootServices] cmp rax, EFI_SUCCESS jne .error mov eax, EFI_SUCCESS jmp .exit .error: mov rdx, ErrMsg call print mov eax, 1 .exit: ret ; print the string in rdx print: mov rcx, [Output] call_safe [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString] ret ; Should not be reachable hlt jmp $ section '.data' data readable writeable ImgHdl dq ? SysTbl dq ? BootSrvc dq ? Output dq ? MemMapSize dq 32768 MemMapKey dq ? MemMapDescSize dq ? MemMapDescVer dq ? MemMapBuff dq 0x220000 BootMsg du '[BOOT]: Booting the system...',13,10,0 ErrMsg du '[BOOT]: There was an error :(',13,10,0 OKMsg du '[BOOT]: Everything is OK :), for now...',13,10,0 section '.reloc' fixups data discardable