forth

A WIP implementation of Forth targeting UEFI x86-64.
git clone git://git.christianermann.dev/forth
Log | Files | Refs

efi.asm (6900B)


      1 ; This file is adapted from `uefi.inc` hosted on osdev.org. It's purpose is
      2 ; to provide an easy-to-use interface to the EFI protocol. I've stripped a lot
      3 ; of stuff out as I don't need it at the moment. I'll probably have to add some
      4 ; of it back at a later date.
      5 ;
      6 
      7 ; These definitions are for portability across 32- and 64-bit systems and
      8 ; provide automatic alignment in structure definitions.
      9 struc int8 {
     10     . db ?
     11 }
     12 struc int16 {
     13     align 2
     14     . dw ?
     15 }
     16 struc int32 {
     17     align 4
     18     . dd ?
     19 }
     20 struc int64 {
     21     align 8
     22     . dq ?
     23 }
     24 struc intn {
     25     align 8
     26     . dq ?
     27 }
     28 struc dptr {
     29     align 8
     30     . dq ?
     31 }
     32 
     33 ; Definitions of status codes returned by various EFI calls.
     34 EFIERR = 0x8000000000000000
     35 EFI_SUCCESS           = 0
     36 EFI_LOAD_ERROR        = EFIERR or 1
     37 EFI_INVALID_PARAMETER = EFIERR or 2
     38 EFI_UNSUPPORTED       = EFIERR or 3
     39 EFI_BAD_BUFFER_SIZE   = EFIERR or 4
     40 EFI_BUFFER_TOO_SMALL  = EFIERR or 5
     41 EFI_NOT_READY         = EFIERR or 6
     42 EFI_DEVICE_ERROR      = EFIERR or 7
     43 EFI_WRITE_PROTECTED   = EFIERR or 8
     44 EFI_OUT_OF_RESOURCES  = EFIERR or 9
     45 EFI_VOLUME_CORRUPTED  = EFIERR or 10
     46 EFI_VOLUME_FULL       = EFIERR or 11
     47 EFI_NO_MEDIA          = EFIERR or 12
     48 EFI_MEDIA_CHANGED     = EFIERR or 13
     49 EFI_NOT_FOUND         = EFIERR or 14
     50 EFI_ACCESS_DENIED     = EFIERR or 15
     51 EFI_NO_RESPONSE       = EFIERR or 16
     52 EFI_NO_MAPPING        = EFIERR or 17
     53 EFI_TIMEOUT           = EFIERR or 18
     54 EFI_NOT_STARTED       = EFIERR or 19
     55 EFI_ALREADY_STARTED   = EFIERR or 20
     56 EFI_ABORTED           = EFIERR or 21
     57 EFI_ICMP_ERROR        = EFIERR or 22
     58 EFI_TFTP_ERROR        = EFIERR or 23
     59 EFI_PROTOCOL_ERROR    = EFIERR or 24
     60 
     61 
     62 ; Helper macro for definition of relative structure member offsets.
     63 macro struct name
     64 {
     65     virtual at 0
     66         name name
     67     end virtual
     68 }
     69 
     70 ; Definitions of EFI structures
     71 
     72 struc EFI_TABLE_HEADER {
     73     .Signature  int64
     74     .Revision   int32
     75     .HeaderSize int32
     76     .CRC32      int32
     77     .Reserved   int32
     78 }
     79 struct EFI_TABLE_HEADER
     80 
     81 struc EFI_SYSTEM_TABLE {
     82     .Hdr                  EFI_TABLE_HEADER
     83     .FirmwareVendor       dptr
     84     .FirmwareRevision     int32
     85     .ConsoleInHandle      dptr
     86     .ConIn                dptr
     87     .ConsoleOutHandle     dptr
     88     .ConOut               dptr
     89     .StandardErrorHandle  dptr
     90     .StdErr               dptr
     91     .RuntimeServices      dptr
     92     .BootServices         dptr
     93     .NumberOfTableEntries intn
     94     .ConfigurationTable   dptr
     95 }
     96 struct EFI_SYSTEM_TABLE
     97 
     98 struc SIMPLE_TEXT_OUTPUT_INTERFACE {
     99     .Reset             dptr
    100     .OutputString      dptr
    101     .TestString        dptr
    102     .QueryMode         dptr
    103     .SetMode           dptr
    104     .SetAttribute      dptr
    105     .ClearScreen       dptr
    106     .SetCursorPosition dptr
    107     .EnableCursor      dptr
    108     .Mode              dptr
    109 }
    110 struct SIMPLE_TEXT_OUTPUT_INTERFACE
    111 
    112 struc SIMPLE_INPUT_INTERFACE {
    113     .Reset         dptr
    114     .ReadKeyStroke dptr
    115     .WaitForKey    dptr
    116 }
    117 struct SIMPLE_INPUT_INTERFACE
    118 
    119 struc INPUT_KEY {
    120     .ScanCode    dw ?
    121     .UnicodeChar dw ?
    122     align 8
    123 }
    124 struct INPUT_KEY
    125 
    126 macro preserve_registers {
    127     push rbx
    128     push rcx
    129     push rdx
    130 }
    131 
    132 macro restore_registers {
    133     pop rdx
    134     pop rcx
    135     pop rbx
    136 }
    137 
    138 sys_initialize:
    139 ; Store the address of the EFI system table in the `system_table` variable.
    140 ;
    141 ; This function should be called ASAP after program start to ensure that the
    142 ; RDX register still contains the address of the system table.
    143 ;
    144 ; `system_table` is defined as an 8-byte variable in the data section at the
    145 ; bottom of this file.
    146 ;
    147     preserve_registers
    148     mov [system_table], rdx
    149     restore_registers
    150     ret
    151 
    152 sys_clear_screen:
    153 ; Clear the screen.
    154 ;
    155 ; This function will clobber the RCX and RBX registers.
    156 ;
    157     preserve_registers
    158     mov rcx, [system_table]
    159     mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
    160     mov rbx, [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen]
    161     sub rsp, 32
    162     call rbx
    163     add rsp, 32
    164     restore_registers
    165     ret
    166 
    167 sys_print_string:
    168 ; Print an ascii string to stdout.
    169 ;
    170 ; This function uses the `.output_buffer` local variable to store the UTF-16
    171 ; null-terminated version of the input string. 1024 bytes (512 characters) are
    172 ; reserved for this buffer in the data section at the bottom of this file.
    173 ;
    174 ; This function will clobber the RDI and RAX registers.
    175 ;
    176 ; Args:
    177 ;    RCX: The address of an ASCII string. The string does not need to be
    178 ;         null-terminated.
    179 ;    RDX: The length of the string.
    180 ;
    181     preserve_registers
    182     mov rdi, .output_buffer
    183 
    184 .copy_char:
    185 ; Copy a character from the ASCII string into the AL register.
    186 ;
    187 ; If there are no characters left, print the converted UTF-16 string.
    188 ;
    189     cmp rdx, 0
    190     je .print_string
    191 
    192     mov al, byte [rcx]
    193     cmp al, 0xA
    194     jne .not_newline
    195 
    196 .is_newline:
    197 ; Convert "\n" (ASCII) to "\r\n" (UTF-16).
    198 ;
    199 ; The converted character is stored in `.output_buffer`.
    200 ;
    201     mov byte [rdi], 0xD
    202     inc rdi
    203     mov byte [rdi], 0
    204     inc rdi
    205     mov byte [rdi], 0xA
    206     inc rdi
    207     mov byte [rdi], 0
    208     inc rdi
    209     jmp .next_char
    210 
    211 .not_newline:
    212 ; Convert an ASCII character into a UTF-16 character.
    213 ;
    214 ; The converted character is stored in `.output_buffer`.
    215 ;
    216     mov byte [rdi], al
    217     inc rdi
    218     mov byte [rdi], 0
    219     inc rdi
    220 
    221 .next_char:
    222 ; Process the next character in the ASCII string.
    223 ;
    224     inc rcx
    225     dec rdx
    226     jmp .copy_char
    227 
    228 .print_string:
    229 ; Print a UTF-16 string to stdout.
    230 ;
    231 ; The string is null-terminated before being passed to the EFI OutputString
    232 ; function in the RDX register.
    233 ;
    234     mov word [rdi], 0
    235     mov rdx, .output_buffer
    236     mov rcx, [system_table]
    237     mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]
    238     mov rbx, [rcx + SIMPLE_TEXT_OUTPUT_INTERFACE.OutputString]
    239     sub rsp, 32
    240     call rbx
    241     add rsp, 32
    242     restore_registers
    243     ret
    244 
    245 sys_read_char:
    246 ; Read an ASCII character from stdin.
    247 ;
    248 ; The EFI ReadKeyStroke function actually returns a UTF-16 character but I only
    249 ; want to deal with ASCII characters for now.
    250 ;
    251     preserve_registers
    252 
    253 .read_key:
    254 ; Call the EFI ReadKeyStroke function until a key is pressed.
    255 ;
    256 ; The status code is returned in the RAX register. If a key is successfully
    257 ; read, it is stored in the `.input_key` local variable through a pointer in
    258 ; the RDX register.
    259 ;
    260     mov rcx, [system_table]
    261     mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]
    262     mov rbx, [rcx + SIMPLE_INPUT_INTERFACE.ReadKeyStroke]
    263     mov rdx, .input_key
    264 
    265     sub rsp, 32
    266     call rbx
    267     add rsp, 32
    268 
    269     mov r8, EFI_NOT_READY
    270     cmp rax, r8
    271     je .read_key
    272 
    273 .process_key:
    274 ; Move the character read from stdin into the RAX register.
    275 ;
    276 ; Carriage returns ("\r") are replaced with newlines ("\n").
    277 ;
    278     movzx rax, word [.input_key.UnicodeChar]
    279     cmp ax, 0xD
    280     jne .end
    281     mov rax, 0xA
    282 
    283 .end:
    284 ; Restore registers saved at the start of the function
    285 ;
    286     restore_registers
    287     ret
    288 
    289 section '.data' data readable writeable
    290 
    291 system_table dq ?
    292 
    293 sys_print_string.output_buffer rq 0x400
    294 
    295 sys_read_char.input_key INPUT_KEY
    296 sys_read_char.char_buffer db ?
    297