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