From: Jonas Hvid Date: Sat, 3 Oct 2020 23:25:48 +0000 (+0200) Subject: Supply UEFI module path as environment variable X-Git-Url: https://git.rrq.au/?a=commitdiff_plain;h=d0a849ffeb541a8c285a92558cedcd6611072540;p=rrq%2Fjonasforth.git Supply UEFI module path as environment variable We'd like to have multiple backends -- e.g. Linux instead of UEFI. That would make testing less cumbersome. Also remove an outdated comment. --- diff --git a/Makefile b/Makefile index ff474b0..362c429 100644 --- a/Makefile +++ b/Makefile @@ -14,9 +14,9 @@ OVMF_CODE.fd: /usr/share/ovmf/x64/OVMF_CODE.fd OVMF_VARS.fd: /usr/share/ovmf/x64/OVMF_VARS.fd cp $< $@ -out/main: main.asm impl.asm bootstrap.asm sys.f uefi.asm +out/main: main.asm impl.asm bootstrap.asm sys.f os/uefi.asm mkdir -p out - fasm $< out/main + OS_INCLUDE=os/uefi.asm fasm $< $@ .PHONY: clean clean: diff --git a/main.asm b/main.asm index 43eef7a..0190d18 100644 --- a/main.asm +++ b/main.asm @@ -1,18 +1,6 @@ ;; vim: syntax=fasm -include "uefi.asm" - -;; "Syscalls" {{{ - -;; [NOTE] Volatile registers Linux (syscalls) vs UEFI -;; -;; Linux syscalls: RAX, RCX, R11 -;; UEFI: RAX, RCX, R11, RDX, R8, R9, R10 - -;; We are in the process of replacing our dependency on Linux with a dependency -;; on UEFI. The following macros attempt to isolate what would be syscalls in -;; Linux; thus, we will be able to replace these with UEFI-based implementations, -;; and in theory we should expect the program to work. +include '%OS_INCLUDE%' ;; Print a string of a given length. ;; @@ -64,8 +52,6 @@ macro sys_terminate code { call uefi_terminate } -;; }}} - ;; The code in this macro is placed at the end of each Forth word. When we are ;; executing a definition, this code is what causes execution to resume at the ;; next word in that definition. diff --git a/os/uefi.asm b/os/uefi.asm new file mode 100644 index 0000000..d116b2c --- /dev/null +++ b/os/uefi.asm @@ -0,0 +1,192 @@ +;; vim: syntax=fasm + +format pe64 dll efi +entry main + +;; EFI struct definitions {{{ + +EFI_NOT_READY = 0x8000_0000_0000_0000 or 6 + +;; Based on https://wiki.osdev.org/Uefi.inc +macro struct name { + virtual at 0 + name name + end virtual +} + +struc EFI_TABLE_HEADER { + dq ? + dd ? + dd ? + dd ? + dd ? +} + +struc EFI_SYSTEM_TABLE { + .Hdr EFI_TABLE_HEADER + .FirmwareVendor dq ? ; CHAR16* + .FirmwareRevision dd ? ; UINT32 + align 8 + .ConsoleInHandle dq ? ; EFI_HANDLE + .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* + .ConsoleOutHandle dq ? ; EFI_HANDLE + .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* + ; ... +} +struct EFI_SYSTEM_TABLE + +struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { + .Reset dq ? ; EFI_TEXT_RESET + .OutputString dq ? ; EFI_TEXT_STRING + ; ... +} +struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + +struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL { + .Reset dq ? ; EFI_INPUT_RESET + .ReadKeyStroke dq ? ; EFI_INPUT_READ_KEY + ; ... +} +struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL + +struc EFI_INPUT_KEY { + .ScanCode dw ? ; UINT16 + .UnicodeChar dw ? ; CHAR16 + align 8 +} +struct EFI_INPUT_KEY + +;; }}} + +section '.text' code executable readable + +uefi_initialize: + ; At program startup, RDX contains an EFI_SYSTEM_TABLE*. + mov [system_table], rdx + ret + +;; Print a string of the given length. +;; +;; Inputs: +;; - RCX = String buffer +;; - RDX = String length +uefi_print_string: + ;; We take an input string of bytes without any terminator. We need to turn + ;; this string into a string of words, terminated by a null character. + + mov rdi, .output_buffer ; Current location in output string + +.copy_byte: + ;; When there are no characters left in the input string, we are done. + cmp rdx, 0 + je .done + + ;; Load byte from input string + mov al, byte [rcx] + + ;; Copy byte to output string + + cmp al, $A + jne .not_newline +.newline: + ;; It's a newline; replace it with '\r\n' in output string. + mov byte [rdi], $D + inc rdi + mov byte [rdi], 0 + inc rdi + mov byte [rdi], $A + inc rdi + mov byte [rdi], 0 + inc rdi + jmp .pop + +.not_newline: + ;; Not a newline, proceed as normal: + mov byte [rdi], al + inc rdi + + ;; The output string has words rather than bytes for charactesr, so we need + ;; to add an extra zero: + mov byte [rdi], 0 + inc rdi + +.pop: + ;; We finished copying character to output string, so pop it from the input + ;; string. + inc rcx + dec rdx + + jmp .copy_byte +.done: + ;; Append a final null-word: + mov word [rdi], 0 + + ; At this point we have our null-terminated word-string at .output_buffer. Now + ; we just need to print it. + + mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx + mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut] ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx + mov rdx, .output_buffer + mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx + sub rsp, 32 + call rbx + add rsp, 32 + ret + +;; Read a character as an ASCII byte into the given buffer. +;; +;; Inputs: +;; - RCX = Character buffer (1 byte) +uefi_read_char: + mov r15, rcx +.read_key: + mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx + mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn] ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx + mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx + mov rdx, input_key ; EFI_INPUT_KEY* rdx + sub rsp, 32 + call rbx + add rsp, 32 + + mov r8, EFI_NOT_READY + cmp rax, r8 + je .read_key + + mov ax, [input_key.UnicodeChar] + mov [r15], al + + ;; Special handling of enter (UEFI gives us '\r', but we want '\n'.) + cmp ax, $D + jne .no_enter + mov byte [r15], $A +.no_enter: + + ;; Print the character + mov rcx, r15 + mov rdx, 1 + call uefi_print_string + + ret + +;; Terminate with the given error code. +;; +;; Inputs: +;; - RCX = Error code +uefi_terminate: + mov rcx, terminated_msg + mov rdx, terminated_msg.len + call uefi_print_string + jmp $ + +section '.data' readable writable + +system_table dq ? ; EFI_SYSTEM_TABLE* + +terminated_msg db 0xD, 0xA, '(The program has terminated.)', 0xD, 0xA +.len = $ - terminated_msg + +uefi_print_string.output_buffer rq 0x400 + +char_buffer db ? + +input_key EFI_INPUT_KEY diff --git a/uefi.asm b/uefi.asm deleted file mode 100644 index d116b2c..0000000 --- a/uefi.asm +++ /dev/null @@ -1,192 +0,0 @@ -;; vim: syntax=fasm - -format pe64 dll efi -entry main - -;; EFI struct definitions {{{ - -EFI_NOT_READY = 0x8000_0000_0000_0000 or 6 - -;; Based on https://wiki.osdev.org/Uefi.inc -macro struct name { - virtual at 0 - name name - end virtual -} - -struc EFI_TABLE_HEADER { - dq ? - dd ? - dd ? - dd ? - dd ? -} - -struc EFI_SYSTEM_TABLE { - .Hdr EFI_TABLE_HEADER - .FirmwareVendor dq ? ; CHAR16* - .FirmwareRevision dd ? ; UINT32 - align 8 - .ConsoleInHandle dq ? ; EFI_HANDLE - .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* - .ConsoleOutHandle dq ? ; EFI_HANDLE - .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* - ; ... -} -struct EFI_SYSTEM_TABLE - -struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { - .Reset dq ? ; EFI_TEXT_RESET - .OutputString dq ? ; EFI_TEXT_STRING - ; ... -} -struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL - -struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL { - .Reset dq ? ; EFI_INPUT_RESET - .ReadKeyStroke dq ? ; EFI_INPUT_READ_KEY - ; ... -} -struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL - -struc EFI_INPUT_KEY { - .ScanCode dw ? ; UINT16 - .UnicodeChar dw ? ; CHAR16 - align 8 -} -struct EFI_INPUT_KEY - -;; }}} - -section '.text' code executable readable - -uefi_initialize: - ; At program startup, RDX contains an EFI_SYSTEM_TABLE*. - mov [system_table], rdx - ret - -;; Print a string of the given length. -;; -;; Inputs: -;; - RCX = String buffer -;; - RDX = String length -uefi_print_string: - ;; We take an input string of bytes without any terminator. We need to turn - ;; this string into a string of words, terminated by a null character. - - mov rdi, .output_buffer ; Current location in output string - -.copy_byte: - ;; When there are no characters left in the input string, we are done. - cmp rdx, 0 - je .done - - ;; Load byte from input string - mov al, byte [rcx] - - ;; Copy byte to output string - - cmp al, $A - jne .not_newline -.newline: - ;; It's a newline; replace it with '\r\n' in output string. - mov byte [rdi], $D - inc rdi - mov byte [rdi], 0 - inc rdi - mov byte [rdi], $A - inc rdi - mov byte [rdi], 0 - inc rdi - jmp .pop - -.not_newline: - ;; Not a newline, proceed as normal: - mov byte [rdi], al - inc rdi - - ;; The output string has words rather than bytes for charactesr, so we need - ;; to add an extra zero: - mov byte [rdi], 0 - inc rdi - -.pop: - ;; We finished copying character to output string, so pop it from the input - ;; string. - inc rcx - dec rdx - - jmp .copy_byte -.done: - ;; Append a final null-word: - mov word [rdi], 0 - - ; At this point we have our null-terminated word-string at .output_buffer. Now - ; we just need to print it. - - mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx - mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut] ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx - mov rdx, .output_buffer - mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx - sub rsp, 32 - call rbx - add rsp, 32 - ret - -;; Read a character as an ASCII byte into the given buffer. -;; -;; Inputs: -;; - RCX = Character buffer (1 byte) -uefi_read_char: - mov r15, rcx -.read_key: - mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx - mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn] ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx - mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx - mov rdx, input_key ; EFI_INPUT_KEY* rdx - sub rsp, 32 - call rbx - add rsp, 32 - - mov r8, EFI_NOT_READY - cmp rax, r8 - je .read_key - - mov ax, [input_key.UnicodeChar] - mov [r15], al - - ;; Special handling of enter (UEFI gives us '\r', but we want '\n'.) - cmp ax, $D - jne .no_enter - mov byte [r15], $A -.no_enter: - - ;; Print the character - mov rcx, r15 - mov rdx, 1 - call uefi_print_string - - ret - -;; Terminate with the given error code. -;; -;; Inputs: -;; - RCX = Error code -uefi_terminate: - mov rcx, terminated_msg - mov rdx, terminated_msg.len - call uefi_print_string - jmp $ - -section '.data' readable writable - -system_table dq ? ; EFI_SYSTEM_TABLE* - -terminated_msg db 0xD, 0xA, '(The program has terminated.)', 0xD, 0xA -.len = $ - terminated_msg - -uefi_print_string.output_buffer rq 0x400 - -char_buffer db ? - -input_key EFI_INPUT_KEY