niedziela, 7 lutego 2016

UEFI programming using pure assembler (32bit mode)

I was writing some small UEFI applications. UEFI is a powerful BIOS successor, which introduces a lot of features. Unfortunately not every implementations on real hardware supports all these things. I neeed only basic functionality like writing some text for test purposes and wrote small test programs using pure assembler.

In the internet you can easily find an asm code for 64 bit architecture but not for 32 bit. I shared my translated code https://github.com/eszkadev/UEFI-32bit-asm-examples

Most important file is efi.inc which describes EFI structures:
 ;  
 ; EFI.inc  
 ; Based on: http://wiki.osdev.org/Uefi.inc  
 ; 2015 Szymon Kłos  
 ;  
   
 struc int8 {  
  . db ?  
 }  
   
 struc int16 {  
  align 2  
  . dw ?  
 }  
   
 struc int32 {  
  align 4  
  . dd ?  
 }  
   
 struc int64 {  
  align 8  
  . dq ?  
 }  
   
 struc intn {  
  align 4  
  . dd ?  
 }  
   
 struc dptr {  
  align 4  
  . dd ?  
 }  
   
  ; symbols  
   
 EFI_SUCCESS                       equ    0  
   
 EFI_SYSTEM_TABLE_SIGNATURE        equ    20494249h  
 EFI_SYSTEM_TABLE_SIGNATURE2       equ    54535953h  
 EFI_RUNTIME_SERVICES_SIGNATURE    equ    544e5552h  
 EFI_RUNTIME_SERVICES_SIGNATURE2   equ    56524553h  
   
  ; helper macro for definition of relative structure member offsets  
   
 macro struct name  
 {  
  virtual at 0  
   name name  
  end virtual  
 }  
   
  ; structures  
   
 struc EFI_TABLE_HEADER {  
  .Signature                  int64  
  .Revision                   int32  
  .HeaderSize                 int32  
  .CRC32                      int32  
  .Reserved                   int32  
 }  
 struct EFI_TABLE_HEADER  
   
 struc EFI_SYSTEM_TABLE {  
  .Hdr                        EFI_TABLE_HEADER  
  .FirmwareVendor             dptr  
  .FirmwareRevision           int32  
  .ConsoleInHandle            dptr  
  .ConIn                      dptr  
  .ConsoleOutHandle           dptr  
  .ConOut                     dptr  
  .StandardErrorHandle        dptr  
  .StdErr                     dptr  
  .RuntimeServices            dptr  
  .BootServices               dptr  
  .NumberOfTableEntries       intn  
  .ConfigurationTable         dptr  
 }  
 struct EFI_SYSTEM_TABLE  
   
 struc SIMPLE_TEXT_OUTPUT_INTERFACE {  
  .Reset                      dptr  
  .OutputString               dptr  
  .TestString                 dptr  
  .QueryMode                  dptr  
  .SetMode                    dptr  
  .SetAttribute               dptr  
  .ClearScreen                dptr  
  .SetCursorPosition          dptr  
  .EnableCursor               dptr  
  .Mode                       dptr  
 }  
 struct SIMPLE_TEXT_OUTPUT_INTERFACE  
   
 struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL {  
  .Reset                      dptr  
  .ReadKeyStroke              dptr  
  .WaitForKey                 dptr  
 }  
 struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL  
   
 struc EFI_INPUT_KEY {  
  .ScanCode                   int16  
  .UnicodeChar                int16  
 }  
 struct EFI_INPUT_KEY  
   
 struc EFI_RUNTIME_SERVICES {  
  .Hdr                        EFI_TABLE_HEADER  
  .GetTime                    dptr  
  .SetTime                    dptr  
  .GetWakeUpTime              dptr  
  .SetWakeUpTime              dptr  
  .SetVirtualAddressMap       dptr  
  .ConvertPointer             dptr  
  .GetVariable                dptr  
  .GetNextVariableName        dptr  
  .SetVariable                dptr  
  .GetNextHighMonotonicCount  dptr  
  .ResetSystem                dptr  
 }  
 struct EFI_RUNTIME_SERVICES  
   
 struc EFI_TIME {  
  .Year                       int16  
  .Month                      int8  
  .Day                        int8  
  .Hour                       int8  
  .Minute                     int8  
  .Second                     int8  
  .Pad1                       int8  
  .Nanosecond                 int32  
  .TimeZone                   int16  
  .Daylight                   int8  
  .Pad2                       int8  
  .sizeof                     rb 1  
 }  
 struct EFI_TIME  

Pointer to the EFI_SYSTEM_TABLE structure is passed to your entry function. EFI uses C calling convention and you can get it from the stack:

 format pe dll efi  
 entry main  
   
 section '.text' code executable readable  
   
 include 'efi.inc'  
   
 main:  
     push ebp  
     mov ebp, esp  
   
  ; get args ( ImageHandle and SystemTable pointer )  
   
     mov ecx, [ebp+8]  
     mov [ImageHandle], ecx  
     mov edx, [ebp+12]  
     mov [SystemTable], edx  
   
  ; ... YOUR CODE  
   
     mov eax, EFI_SUCCESS  
   
     pop ebp  
     retn  
   
 section '.data' data readable writeable  
   
 ImageHandle        dd        ?  
 SystemTable        dd        ?  
   
 section '.reloc' fixups data discardable  

Simple HelloWorld: https://github.com/eszkadev/UEFI-32bit-asm-examples/blob/master/HelloWorld.asm

EFI application binary must be in a PE format. You can compile this code with fasm. Then place it on your disc and run from UEFI Shell (for example using Virtualbox).

Links:
http://wiki.osdev.org/Uefi.inc
http://x86asm.net/articles/uefi-programming-first-steps/index.html

Brak komentarzy:

Prześlij komentarz