ekern

ekern.git
git clone git://git.lenczewski.org/ekern.git
Log | Files | Refs | README | LICENSE

commit 5ccc01e7a53e1ba2c68b8f02a72696d6bdedbd45
Author: Mikołaj Lenczewski <mblenczewski@gmail.com>
Date:   Thu, 27 Jun 2024 21:21:57 +0000

Initial commit

Diffstat:
A.editorconfig | 21+++++++++++++++++++++
A.gitignore | 7+++++++
ALICENSE | 18++++++++++++++++++
AREADME | 2++
Abuild.sh | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aclean.sh | 7+++++++
Aconfig.sh | 14++++++++++++++
Adocs/acpi-6.5.pdf | 0
Adocs/uefi-2.10.pdf | 0
Aeuser/asm-example/build.sh | 17+++++++++++++++++
Aeuser/c-example/build.sh | 5+++++
Aeuser/c-example/example.c | 0
Aeuser/cpp-example/build.sh | 5+++++
Ainclude/efi.h | 888+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/qemu-amd64.sh | 24++++++++++++++++++++++++
Asrc/boot-amd64.asm | 547+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/boot.c | 24++++++++++++++++++++++++
Asrc/boot.ld | 18++++++++++++++++++
Asrc/kernel-amd64.c | 4++++
Asrc/kernel.c | 7+++++++
Asrc/kernel.ld | 20++++++++++++++++++++
Atoolchain.sh | 34++++++++++++++++++++++++++++++++++
22 files changed, 1740 insertions(+), 0 deletions(-)

diff --git a/.editorconfig b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +guidelines = 80, 120, 160 + +[*.{c,h,s}] +indent_style = tab +indent_size = 8 + +[*.{sh}] +indent_style = tab +indent_size = 8 + +[*.{md,txt}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore @@ -0,0 +1,7 @@ +bin/ +obj/ +mnt/ + +serial.log + +**/.*.swp diff --git a/LICENSE b/LICENSE @@ -0,0 +1,18 @@ +The MIT-Zero License + +Copyright (c) 2023 Mikołaj Lenczewski <mblenczewski@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README b/README @@ -0,0 +1,2 @@ +ekern +============================================================================== diff --git a/build.sh b/build.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +. "$(dirname $0)/toolchain.sh" + +BIN="$ROOT/bin" +OBJ="$ROOT/obj" +MNT="$ROOT/mnt/$ARCH" + +set -ex + +mkdir -p $BIN $OBJ $MNT + +# build eboot (bin/eboot-$ARCH.efi) + +clang -o $OBJ/boot-$ARCH.o -c $ROOT/src/boot.c \ + --target=x86_64-unknown-windows -ffreestanding -mno-red-zone $CPPFLAGS + +clang -o $BIN/boot-$ARCH.efi $OBJ/boot-$ARCH.o \ + --target=x86_64-unknown-windows -nostdlib -Wl,-subsystem:efi_application -Wl,-entry:efi_main -fuse-ld=lld-link + +# build ekern (bin/ekern-$ARCH.sys) + +$CC -o $OBJ/start.o -c $ROOT/src/kernel.c $CFLAGS $CPPFLAGS +$CC -o $OBJ/kernel-$ARCH.o -c $ROOT/src/kernel-$ARCH.c $CFLAGS $CPPFLAGS +$LD -o $BIN/kernel-$ARCH.elf -T $ROOT/src/kernel.ld $OBJ/start.o $OBJ/kernel-$ARCH.o +$OBJCOPY -O binary $BIN/kernel-$ARCH.elf $BIN/kernel-$ARCH.sys + +# build UEFI image (bin/uefi.sys) + +cat $BIN/boot-$ARCH.efi > $BIN/uefi.sys +# cat $BIN/boot-$ARCH.efi $BIN/kernel-$ARCH.sys > $BIN/uefi.sys + +# build euser (mnt/$ARCH/* and bin/euser-$ARCH.img) + +dd if=/dev/zero of=$BIN/euser-$ARCH.img bs=1048576 count=64 >/dev/null 2>&1 + +for program in $USERLAND; do + ( + set +x + BIN="$(realpath "$BIN")"; + OBJ="$(realpath "$OBJ")"; + MNT="$(realpath "$MNT")"; + set -x + cd "$ROOT/euser/$program"; + . "./build.sh" + ) +done + +# create bootable disk image (bin/disk.img) +# --- +# see: https://wiki.osdev.org/Bootable_Disk + +dd if=/dev/zero of=$BIN/disk.img bs=1M count=64 >/dev/null 2>&1 + +cat <<'EOF' | sfdisk $BIN/disk.img +label: gpt +unit: sectors + +type=U, start=2048, size=2048, bootable +type=L, start=4096 +EOF + +## format efi partition +dd if=/dev/zero of=$BIN/esp.img bs=512 count=2048 >/dev/null 2>&1 + +mformat -i $BIN/esp.img +mmd -i $BIN/esp.img ::/EFI ::/EFI/BOOT +mcopy -i $BIN/esp.img $BIN/uefi.sys ::/EFI/BOOT/BOOT$UEFIARCH.EFI + +dd if=$BIN/esp.img of=$BIN/disk.img bs=512 seek=2048 count=2048 conv=notrunc >/dev/null 2>&1 + +## format data partition +## TODO: format remaining data partition instead of writing raw bytes +dd if=$BIN/euser-$ARCH.img of=$BIN/disk.img bs=512 seek=4096 conv=notrunc >/dev/null 2>&1 + +# run qemu emulator + +"$ROOT/scripts/qemu-$ARCH.sh" $ROOT/bin/disk.img diff --git a/clean.sh b/clean.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +. "$(dirname $0)/config.sh" + +set -ex + +rm -rf bin obj mnt diff --git a/config.sh b/config.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +ROOT="$(dirname $0)" + +# target architecture, one of: amd64 aarch64 riscv64 +ARCH=amd64 + +# uefi architecture, corresponds to target arch: X64 +UEFIARCH=X64 + +# userland programs to compile, any under euser/ +#USERLAND="$USERLAND asm-example" +#USERLAND="$USERLAND c-example" +#USERLAND="$USERLAND cpp-example" diff --git a/docs/acpi-6.5.pdf b/docs/acpi-6.5.pdf Binary files differ. diff --git a/docs/uefi-2.10.pdf b/docs/uefi-2.10.pdf Binary files differ. diff --git a/euser/asm-example/build.sh b/euser/asm-example/build.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +SOURCE="" +case $ARCH in + amd64|aarch64) + SOURCE="example-$ARCH.asm" + ;; + + *) + echo "Unsupported architecture: $ARCH" + exit 0 + ;; +esac + +set -ex + +$AS -o $MNT/asm-example.bin $SOURCE $ASMFLAGS diff --git a/euser/c-example/build.sh b/euser/c-example/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -ex + +$CC -o $MNT/c-example.bin example.c $CFLAGS $CPPFLAGS $LDFLAGS diff --git a/euser/c-example/example.c b/euser/c-example/example.c diff --git a/euser/cpp-example/build.sh b/euser/cpp-example/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -ex + +$CXX -o $MNT/cpp-example.bin example.cpp $CXXFLAGS $CPPFLAGS $LDFLAGS diff --git a/include/efi.h b/include/efi.h @@ -0,0 +1,888 @@ +#ifndef EFI_H +#define EFI_H + +/* generated from the uefi-2.10 docs */ + +#define IN +#define OUT +#define OPTIONAL +#define CONST const + +#define EFIAPI + +#include <stdalign.h> +#include <stddef.h> +#include <stdint.h> + +typedef int8_t b8; + +#define true 1 +#define false 0 + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +// typedef uint128_t u128; +typedef uintmax_t umm; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; +// typedef int128_t s128; +typedef intmax_t smm; + +typedef unsigned char c8; +typedef uint16_t c16; + +typedef struct efi_guid { + alignas(u32) u8 vs[16]; +} efi_guid_t; + +#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((struct efi_guid) {.vs = { \ + ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff, \ + ((b) >> 8) & 0xff, (b) & 0xff, \ + ((c) >> 8) & 0xff, (c) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7), \ +}}) + +typedef umm efi_status_t; + +#define EFI_ERROR 0x8000000000000000 +#define EFI_WARNING 0x0000000000000000 + +#define EFIERR(err) (EFI_ERROR | (err)) +#define EFIWARN(err) (EFI_WARNING | (err)) + +enum efi_status { + EFI_SUCCESS = 0, + + EFI_WARN_UNKNOWN_GLYPH = EFIWARN(1), + EFI_WARN_DELETE_FAILURE = EFIWARN(2), + EFI_WARN_WRITE_FAILURE = EFIWARN(3), + EFI_WARN_BUFFER_TOO_SMALL = EFIWARN(4), + EFI_WARN_STALE_DATA = EFIWARN(5), + EFI_WARN_FILE_SYSTEM = EFIWARN(6), + EFI_WARN_RESET_REQUIRED = EFIWARN(7), + + EFI_LOAD_ERROR = EFIERR(1), + EFI_INVALID_PARAMETER = EFIERR(2), + EFI_UNSUPPORTED = EFIERR(3), + EFI_BAD_BUFFER_SIZE = EFIERR(4), + EFI_BUFFER_TOO_SMALL = EFIERR(5), + EFI_NOT_READY = EFIERR(6), + EFI_DEVICE_ERROR = EFIERR(7), + EFI_WRITE_PROTECTED = EFIERR(8), + EFI_OUT_OF_RESOURCES = EFIERR(9), + EFI_VOLUME_CORRUPTED = EFIERR(10), + EFI_VOLUME_FULL = EFIERR(11), + EFI_NO_MEDIA = EFIERR(12), + EFI_MEDIA_CHANGED = EFIERR(13), + EFI_NOT_FOUND = EFIERR(14), + EFI_ACCESS_DENIED = EFIERR(15), + EFI_NO_RESPONSE = EFIERR(16), + EFI_NO_MAPPING = EFIERR(17), + EFI_TIMEOUT = EFIERR(18), + EFI_NOT_STARTED = EFIERR(19), + EFI_ALREADY_STARTED = EFIERR(20), + EFI_ABORTED = EFIERR(21), + EFI_ICMP_ERROR = EFIERR(22), + EFI_TFTP_ERROR = EFIERR(23), + EFI_PROTOCOL_ERROR = EFIERR(24), + EFI_INCOMPATIBLE_VERSION = EFIERR(25), + EFI_SECURITY_VIOLATION = EFIERR(26), + EFI_CRC_ERROR = EFIERR(27), + EFI_END_OF_MEDIA = EFIERR(28), + EFI_END_OF_FILE = EFIERR(31), + EFI_INVALID_LANGUAGE = EFIERR(32), + EFI_COMPROMISED_DATA = EFIERR(33), + EFI_IP_ADDRESS_CONFLICT = EFIERR(34), + EFI_HTTP_ERROR = EFIERR(35), +}; + +typedef void *efi_handle_t; +typedef void *efi_event_t; +typedef u64 efi_lba_t; +typedef umm efi_tpl_t; + +struct efi_table_header { + u64 signature; + u32 revision; + u32 header_size; + u32 crc32; + u32 reserved0; +}; + +/* system table + * --- + * see: https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html + */ + +struct efi_system_table { + struct efi_table_header hdr; + + c16 *firmware_vendor; + u32 firmware_revision; + + efi_handle_t console_in_handle; + struct efi_simple_text_input_protocol *con_in; + + efi_handle_t console_out_handle; + struct efi_simple_text_output_protocol *con_out; + + efi_handle_t standard_error_handle; + struct efi_simple_text_output_protocol *std_err; + + struct efi_runtime_services *runtime_services; + struct efi_boot_services *boot_services; + + umm number_of_table_entries; + struct efi_configuration_table *configuration_table; +}; + +/* boot services + * --- + * see: https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html + */ + +#define EVT_TIMER 0x80000000 +#define EVT_RUNTIME 0x40000000 + +#define EVT_NOTIFY_WAIT 0x00000100 +#define EVT_NOTIFY_SIGNAL 0x00000200 + +#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 +#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202 + +typedef umm efi_tpl_t; + +#define TPL_APPLICATION 4 +#define TPL_CALLBACK 8 +#define TPL_NOTIFY 16 +#define TPL_HIGH_LEVEL 31 + +#define EFI_EVENT_NOTIFY(name) \ + void (EFIAPI name) (IN efi_event_t event, \ + IN void *context) + +typedef EFI_EVENT_NOTIFY(efi_event_notify_t); + +#define EFI_CREATE_EVENT(name) \ + efi_status_t (EFIAPI name) (IN u32 type, \ + IN efi_tpl_t notify_tpl, \ + IN efi_event_notify_t * OPTIONAL notify_function, \ + IN void * OPTIONAL notify_context, \ + OUT efi_event_t *event) + +#define EFI_EVENT_GROUP_EXIT_BOOT_SERVICES \ + ((efi_guid_t) {.vs = {0x27abf055, 0xb1b84c26, 0x8048748f, 0x37baa2df}}) + +#define EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES \ + ((efi_guid_t) {.vs = {0x8be0e274, 0x39704b44, 0x80c51ab9, 0x502f3bfc}}) + +#define EFI_EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE \ + ((efi_guid_t) {.vs = {0x13fa7698, 0xc83149c7, 0x87ea8f43, 0xfcc25196}}) + +#define EFI_EVENT_GROUP_MEMORY_MAP_CHANGE \ + ((efi_guid_t) {.vs = {0x78bee926, 0x692f48fd, 0x9edb0142, 0x2ef0d7ab}}) + +#define EFI_EVENT_GROUP_READY_TO_BOOT \ + ((efi_guid_t) {.vs = {0x7ce88fb3, 0x4bd74679, 0x87a8a8d8, 0xdee50d2b}}) + +#define EFI_EVENT_GROUP_AFTER_READY_TO_BOOT \ + ((efi_guid_t) {.vs = {0x3a2a00ad, 0x98b94cdf, 0xa4787027, 0x77f1c10b}}) + +#define EFI_EVENT_GROUP_RESET_SYSTEM \ + ((efi_guid_t) {.vs = {0x62ddaba56, 0x13fb485a, 0xa8daa3dd, 0x7912cb6b}}) + +#define EFI_CREATE_EVENT_EX(name) \ + efi_status_t (EFIAPI name) (IN u32 type, \ + IN efi_tpl_t notify_tpl, \ + IN efi_event_notify_t * OPTIONAL notify_function, \ + IN void CONST * OPTIONAL notify_context, \ + IN efi_guid_t CONST * OPTIONAL event_group, \ + OUT efi_event_t *event) + + +#define EFI_CLOSE_EVENT(name) \ + efi_status_t (EFIAPI name) (IN efi_event_t event) + +#define EFI_SIGNAL_EVENT(name) \ + efi_status_t (EFIAPI name) (IN efi_event_t event) + +#define EFI_WAIT_FOR_EVENT(name) \ + efi_status_t (EFIAPI name) (IN umm number_of_events, \ + IN efi_event_t *event, \ + OUT umm *index) + +#define EFI_CHECK_EVENT(name) \ + efi_status_t (EFIAPI name) (IN efi_event_t event) + +enum efi_timer_delay { + EFI_TIMER_CANCEL, + EFI_TIMER_PERIODIC, + EFI_TIMER_RELATIVE, +}; + +#define EFI_SET_TIMER(name) \ + efi_status_t (EFIAPI name) (IN efi_event_t event, \ + IN enum efi_timer_delay type, \ + IN u64 trigger_time) + +#define EFI_RAISE_TPL(name) \ + efi_tpl_t (EFIAPI name) (IN efi_tpl_t new_tpl) + +#define EFI_RESTORE_TPL(name) \ + void (EFIAPI name) (IN efi_tpl_t old_tpl) + +enum efi_allocate_type { + EFI_ALLOCATE_ANY_PAGES, + EFI_ALLOCATE_MAX_ADDRESS, + EFI_ALLOCATE_ADDRESS, + EFI_MAX_ALLOCATE_TYPE, +}; + +enum efi_memory_type { + EFI_RESERVED_MEMORY_TYPE, + EFI_LOADER_CODE, + EFI_LOADER_DATA, + EFI_BOOT_SERVICES_CODE, + EFI_BOOT_SERVICES_DATA, + EFI_RUNTIME_SERVICES_CODE, + EFI_RUNTIME_SERVICES_DATA, + EFI_CONVENTIONAL_MEMORY, + EFI_UNUSABLE_MEMORY, + EFI_ACPI_RECLAIM_MEMORY, + EFI_ACPI_MEMORY_NVS, + EFI_MEMORY_MAPPED_IO, + EFI_MEMORY_MAPPED_IO_PORT_SPACE, + EFI_PAL_CODE, + EFI_PERSISTENT_MEMORY, + EFI_UNACCEPTED_MEMORY_TYPE, + EFI_MAX_MEMORY_TYPE, +}; + +typedef u64 efi_physical_address_t; + +#define EFI_MEMORY_UC 0x0000000000000001 +#define EFI_MEMORY_WC 0x0000000000000002 +#define EFI_MEMORY_WT 0x0000000000000004 +#define EFI_MEMORY_WB 0x0000000000000008 +#define EFI_MEMORY_UCE 0x0000000000000010 +#define EFI_MEMORY_WP 0x0000000000001000 +#define EFI_MEMORY_RP 0x0000000000002000 +#define EFI_MEMORY_XP 0x0000000000004000 +#define EFI_MEMORY_NV 0x0000000000008000 +#define EFI_MEMORY_MORE_RELIABLE 0x0000000000010000 +#define EFI_MEMORY_RO 0x0000000000020000 +#define EFI_MEMORY_SP 0x0000000000040000 +#define EFI_MEMORY_CPU_CRYPTO 0x0000000000080000 +#define EFI_MEMORY_RUNTIME 0x8000000000000000 +#define EFI_MEMORY_ISA_VALID 0x4000000000000000 +#define EFI_MEMORY_ISA_MASK 0x0FFFF00000000000 + +typedef u64 efi_virtual_address_t; + +#define EFI_MEMORY_DESCRIPTOR_VERSION 1 + +struct efi_memory_descriptor { + u32 type; + efi_physical_address_t physical_start; + efi_virtual_address_t virtual_start; + u64 number_of_pages; + u64 attribute; +}; + +#define EFI_ALLOCATE_PAGES(name) \ + efi_status_t (EFIAPI name) (IN enum efi_allocate_type type, \ + IN enum efi_memory_type memory_type, \ + IN umm pages, \ + IN OUT efi_physical_address_t *memory) + +#define EFI_FREE_PAGES(name) \ + efi_status_t (EFIAPI name) (IN efi_physical_address_t memory, \ + IN umm pages) + +#define EFI_GET_MEMORY_MAP(name) \ + efi_status_t (EFIAPI name) (IN OUT umm *memory_map_size, \ + OUT struct efi_memory_descriptor *memory_map, \ + OUT umm *map_key, \ + OUT umm *descriptor_size, \ + OUT u32 *descriptor_version) + +#define EFI_ALLOCATE_POOL(name) \ + efi_status_t (EFIAPI name) (IN enum efi_memory_type pool_type, \ + IN umm size, \ + OUT void **buffer) + +#define EFI_FREE_POOL(name) \ + efi_status_t (EFIAPI name) (IN void *buffer) + +enum efi_interface_type { + EFI_NATIVE_INTERFACE, +}; + +#define EFI_INSTALL_PROTOCOL_INTERFACE(name) \ + efi_status_t (EFIAPI name) (IN OUT efi_handle_t *handle, \ + IN efi_guid_t *protocol, \ + IN enum efi_interface_type interface_type, \ + IN void *interface) + +#define EFI_UNINSTALL_PROTOCOL_INTERFACE(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + IN void *interface) + +#define EFI_REINSTALL_PROTOCOL_INTERFACE(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + IN void *old_interface, \ + IN void *new_interface) + +#define EFI_REGISTER_PROTOCOL_NOTIFY(name) \ + efi_status_t (EFIAPI name) (IN efi_guid_t *protocol, \ + IN efi_event_t event, \ + OUT void **registration) + +enum efi_locate_search_type { + EFI_ALL_HANDLES, + EFI_BY_REGISTER_NOTIFY, + EFI_BY_PROTOCOL, +}; + +#define EFI_LOCATE_HANDLE(name) \ + efi_status_t (EFIAPI name) (IN enum efi_locate_search_type search_type, \ + IN efi_guid_t *OPTIONAL protocol, \ + IN void *OPTIONAL search_key, \ + IN OUT umm *buffer_size, \ + OUT efi_handle_t *buffer) + +#define EFI_HANDLE_PROTOCOL(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + OUT void **interface) + +// TODO: implement definitions +struct efi_device_path_protocol { + u8 type; + u8 subtype; + u8 length[2]; +}; + +#define EFI_LOCATE_DEVICE_PATH(name) \ + efi_status_t (EFIAPI name) (IN efi_guid_t *protocol, \ + IN OUT struct efi_device_path_protocol **device_path, \ + OUT efi_handle_t *device) + +#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 +#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 +#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004 +#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008 +#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010 +#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020 + +#define EFI_OPEN_PROTOCOL(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + OUT void **OPTIONAL interface, \ + IN efi_handle_t agent_handle, \ + IN efi_handle_t controller_handle, \ + IN u32 attributes) + +#define EFI_CLOSE_PROTOCOL(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + IN efi_handle_t agent_handle, \ + IN efi_handle_t controller_handle) + +struct efi_open_protocol_information_entry { + efi_handle_t agent_handle; + efi_handle_t controller_handle; + u32 attributes; + u32 open_count; +}; + +#define EFI_OPEN_PROTOCOL_INFORMATION(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + IN efi_guid_t *protocol, \ + OUT struct efi_open_protocol_information_entry **entry_buffer, \ + OUT umm *entry_count) + +#define EFI_CONNECT_CONTROLLER(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t controller_handle, \ + IN efi_handle_t *OPTIONAL driver_image_handle, \ + IN struct efi_device_path_protocol *OPTIONAL remaining_device_path, \ + IN b8 recursive) + +#define EFI_DISCONNECT_CONTROLLER(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t controller_handle, \ + IN efi_handle_t OPTIONAL driver_image_handle, \ + IN efi_handle_t OPTIONAL child_handle) + +#define EFI_PROTOCOLS_PER_HANDLE(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, \ + OUT efi_guid_t ***protocol_buffer, \ + OUT umm *protocol_buffer_count) + +#define EFI_LOCATE_HANDLE_BUFFER(name) \ + efi_status_t (EFIAPI name) (IN enum efi_locate_search_type search_type, \ + IN efi_guid_t *OPTIONAL protocol, \ + IN void *OPTIONAL search_key, \ + OUT umm *no_handles, \ + OUT efi_handle_t **buffer) + +#define EFI_LOCATE_PROTOCOL(name) \ + efi_status_t (EFIAPI name) (IN efi_guid_t *protocol, \ + IN void *OPTIONAL registration, \ + OUT void **interface) + +#define EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES(name) \ + efi_status_t (EFIAPI name) (IN OUT efi_handle_t *handle, ...) + +#define EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t handle, ...) + +#define EFI_IMAGE_LOAD(name) \ + efi_status_t (EFIAPI name) (IN b8 boot_policy, \ + IN efi_handle_t parent_image_handle, \ + IN struct efi_device_path_protocol *OPTIONAL device_path, \ + IN void *OPTIONAL source_buffer, \ + IN umm source_size, \ + OUT efi_handle_t *image_handle) + +#define EFI_IMAGE_START(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t image_handle, \ + OUT umm *exit_data_size, \ + OUT c16 **OPTIONAL exit_data) + +#define EFI_IMAGE_UNLOAD(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t image_handle) + +#define EFI_IMAGE_ENTRY_POINT(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t image_handle, \ + IN struct efi_system_table *system_table) + +#define EFI_EXIT(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t image_handle, \ + IN efi_status_t exit_status, \ + IN umm exit_data_size, \ + IN c16 *OPTIONAL exit_data) + +#define EFI_EXIT_BOOT_SERVICES(name) \ + efi_status_t (EFIAPI name) (IN efi_handle_t image_handle, \ + IN umm map_key) + +#define EFI_SET_WATCHDOG_TIMER(name) \ + efi_status_t (EFIAPI name) (IN umm timeout, \ + IN u64 watchdog_code, \ + IN umm data_size, \ + IN c16 *OPTIONAL watchdog_data) + +#define EFI_STALL(name) \ + efi_status_t (EFIAPI name) (IN umm microseconds) + +#define EFI_COPY_MEM(name) \ + efi_status_t (EFIAPI name) (IN void *destination, \ + IN void *source, \ + IN umm length) + +#define EFI_SET_MEM(name) \ + efi_status_t (EFIAPI name) (IN void *buffer, \ + IN umm size, \ + IN u8 value) + +#define EFI_GET_NEXT_MONOTONIC_COUNT(name) \ + efi_status_t (EFIAPI name) (OUT u64 *count) + +#define EFI_INSTALL_CONFIGURATION_TABLE(name) \ + efi_status_t (EFIAPI name) (IN efi_guid_t *guid, \ + IN void *table) + +#define EFI_CALCULATE_CRC32(name) \ + efi_status_t (EFIAPI name) (IN void *data, \ + IN umm data_size, \ + OUT u32 *crc32) + +struct efi_boot_services { + struct efi_table_header hdr; + + EFI_RAISE_TPL(*raise_tpl); + EFI_RESTORE_TPL(*restore_tpl); + + EFI_ALLOCATE_PAGES(*allocate_pages); + EFI_FREE_PAGES(*free_pages); + EFI_GET_MEMORY_MAP(*get_memory_map); + EFI_ALLOCATE_POOL(*allocate_pool); + EFI_FREE_POOL(*free_pool); + + EFI_CREATE_EVENT(*create_event); + EFI_SET_TIMER(*set_timer); + EFI_WAIT_FOR_EVENT(*wait_for_event); + EFI_SIGNAL_EVENT(*signal_event); + EFI_CLOSE_EVENT(*close_event); + EFI_CHECK_EVENT(*check_event); + + EFI_INSTALL_PROTOCOL_INTERFACE(*install_protocol_interface); + EFI_REINSTALL_PROTOCOL_INTERFACE(*reinstall_protocol_interface); + EFI_UNINSTALL_PROTOCOL_INTERFACE(*uninstall_protocol_interface); + EFI_HANDLE_PROTOCOL(*handle_protocol); + void *reserved0; + EFI_REGISTER_PROTOCOL_NOTIFY(*register_protocol_notify); + EFI_LOCATE_HANDLE(*locate_handle); + EFI_LOCATE_DEVICE_PATH(*locate_device_path); + EFI_INSTALL_CONFIGURATION_TABLE(*install_configuration_table); + + EFI_IMAGE_LOAD(*load_image); + EFI_IMAGE_START(*start_image); + EFI_EXIT(*exit); + EFI_IMAGE_UNLOAD(*unload_image); + EFI_EXIT_BOOT_SERVICES(*exit_boot_services); + + EFI_GET_NEXT_MONOTONIC_COUNT(*get_next_monotonic_count); + EFI_STALL(*stall); + EFI_SET_WATCHDOG_TIMER(*set_watchdog_timer); + + // EFI 1.1+ + EFI_CONNECT_CONTROLLER(*connect_controller); + EFI_DISCONNECT_CONTROLLER(*disconnect_controller); + + EFI_OPEN_PROTOCOL(*open_protocol); + EFI_CLOSE_PROTOCOL(*close_protocol); + EFI_OPEN_PROTOCOL_INFORMATION(*open_protocol_information); + EFI_PROTOCOLS_PER_HANDLE(*protocols_per_handle); + EFI_LOCATE_HANDLE_BUFFER(*locate_handle_buffer); + EFI_LOCATE_PROTOCOL(*locate_protocol); + EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES(*install_multiple_protocol_interfaces); + EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES(*uninstall_multiple_protocol_interfaces); + + EFI_CALCULATE_CRC32(*calculate_crc32); + + EFI_COPY_MEM(*copy_mem); + EFI_SET_MEM(*set_mem); + + // UEFI 2.0+ + EFI_CREATE_EVENT_EX(*create_event_ex); +}; + + + +/* runtime services + * --- + * see: https://uefi.org/specs/UEFI/2.10/08_Services_Runtime_Services.html + */ + +struct efi_time { + u16 year; // 1999 - 9999 + u8 month; // 1 - 12 + u8 day; // 1 - 31 + u8 hour; // 0 - 23 + u8 minute; // 0 - 59 + u8 second; // 0 - 59 + u8 pad1; + u32 nanosecond; // 0 - 999,999,999 + s16 timezone; // -1440 - 1440 or 2047 + u8 daylight; + u8 pad2; +}; + +#define EFI_TIME_ADJUST_DAYLIGHT 0x01 +#define EFI_TIME_IN_DAYLIGHT 0x02 + +#define EFI_TIME_UNSPECIFIED_TIMEZONE 0x07ff + +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 + +// NOTE: deprecated +//#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 + +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#define EFI_VARIABLE_APPEND_WRITE 0x00000040 +#define EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS 0x00000080 + +#define EFI_VARIABLE_AUTHENTICATION_3_CERT_ID_SHA256 1 + +struct efi_variable_authentication_3_cert_id { + u8 type; + u32 id_size; + u8 id[]; +}; + +#define EFI_GET_VARIABLE(name) \ + efi_status_t (EFIAPI name) (IN c16 *variable_name, \ + IN efi_guid_t *vendor_guid, \ + OUT u32 *OPTIONAL attributes, \ + IN OUT umm *data_size, \ + OUT void *OPTIONAL data) + +#define EFI_GET_NEXT_VARIABLE_NAME(name) \ + efi_status_t (EFIAPI name) (IN OUT umm *variable_name_size, \ + IN OUT c16 *variable_name, \ + IN OUT efi_guid_t *vendor_guid) + +struct win_certificate_uefi_guid { + u8 pad0; // TODO +}; + +struct efi_variable_authentication { + u64 monotonic_count; + struct win_certificate_uefi_guid auth_info; +}; + +struct efi_variable_authentication_2 { + struct efi_time time_stamp; + struct win_certificate_uefi_guid auth_info; +}; + +#define EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE 1 +#define EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE 2 + +struct efi_variable_authentication_3 { + u8 version; + u8 type; + u32 metadata_size; + u32 flags; +}; + +struct efi_variable_authentication_3_nonce { + u32 nonce_size; + u8 nonce[]; +}; + +#define EFI_SET_VARIABLE(name) \ + efi_status_t (EFIAPI name) (IN c16 *variable_name, \ + IN efi_guid_t *vendor_guid, \ + IN u32 attributes, \ + IN umm data_size, \ + IN void *data) + +#define EFI_QUERY_VARIABLE_INFO(name) \ + efi_status_t (EFIAPI name) (IN u32 attributes, \ + OUT u64 *maximum_variable_storage_size, \ + OUT u64 *remaining_variable_storage_size, \ + OUT u64 *maximum_variable_size) + +struct efi_time_capabilities { + u32 resolution; + u32 accuracy; + b8 sets_to_zero; +}; + +#define EFI_GET_TIME(name) \ + efi_status_t (EFIAPI name) (OUT struct efi_time *time, \ + OUT struct efi_time_capabilities * OPTIONAL capabilities) + +#define EFI_SET_TIME(name) \ + efi_status_t (EFIAPI name) (IN struct efi_time *time) + +#define EFI_GET_WAKEUP_TIME(name) \ + efi_status_t (EFIAPI name) (OUT b8 *enabled, \ + OUT b8 *pending, \ + OUT struct efi_time *time) + +#define EFI_SET_WAKEUP_TIME(name) \ + efi_status_t (EFIAPI name) (IN b8 enable, \ + IN struct efi_time * OPTIONAL time) + +#define EFI_SET_VIRTUAL_ADDRESS_MAP(name) \ + efi_status_t (EFIAPI name) (IN umm memory_map_size, \ + IN umm descriptor_size, \ + IN u32 descriptor_version, \ + IN struct efi_memory_descriptor *virtual_map) + +#define EFI_OPTIONAL_PTR 0x00000001 + +#define EFI_CONVERT_POINTER(name) \ + efi_status_t (EFIAPI name) (IN umm debug_disposition, \ + IN void **address) + +enum efi_reset_type { + EFI_RESET_COLD, + EFI_RESET_WARM, + EFI_RESET_SHUTDOWN, + EFI_RESET_PLATFORM_SPECIFIC, +}; + +#define EFI_RESET_SYSTEM(name) \ + void (EFIAPI name) (IN enum efi_reset_type reset_type, \ + IN efi_status_t reset_status, \ + IN umm data_size, \ + IN void *OPTIONAL reset_data) + +#define EFI_GET_NEXT_HIGH_MONO_COUNT(name) \ + efi_status_t (EFIAPI name) (OUT u32 *high_count) + +struct efi_capsule_block_descriptor { + u64 length; + union { + efi_physical_address_t data_block; + efi_physical_address_t continuation_pointer; + }; +}; + +struct efi_capsule_header { + efi_guid_t capsule_guid; + u32 header_size; + u32 flags; + u32 capsule_image_size; +}; + +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 +#define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 + +struct efi_capsule_table { + u32 capsule_array_number; + void *capsule_ptr[]; +}; + +#define EFI_UPDATE_CAPSULE(name) \ + efi_status_t (EFIAPI name) (IN struct efi_capsule_header **capsule_header_array, \ + IN umm capsule_count, \ + IN efi_physical_address_t OPTIONAL scatter_gather_list) + +#define EFI_QUERY_CAPSULE_CAPABILITIES(name) \ + efi_status_t (EFIAPI name) (IN struct efi_capsule_header **capsule_header_array, \ + IN umm capsule_count, \ + OUT u64 *maxiumum_capsule_size, \ + OUT enum efi_reset_type *reset_type) + +struct efi_runtime_services { + struct efi_table_header hdr; + + EFI_GET_TIME(*get_time); + EFI_SET_TIME(*set_time); + EFI_GET_WAKEUP_TIME(*get_wakeup_time); + EFI_SET_WAKEUP_TIME(*set_wakeup_time); + + EFI_SET_VIRTUAL_ADDRESS_MAP(*set_virtual_address_map); + EFI_CONVERT_POINTER(*convert_pointer); + + EFI_GET_VARIABLE(*get_variable); + EFI_GET_NEXT_VARIABLE_NAME(*get_next_variable_name); + EFI_SET_VARIABLE(*set_variable); + + EFI_GET_NEXT_HIGH_MONO_COUNT(*get_next_high_monotonic_count); + EFI_RESET_SYSTEM(*reset_system); + + // UEFI 2.0+ + EFI_UPDATE_CAPSULE(*update_capsule); + EFI_QUERY_CAPSULE_CAPABILITIES(*query_capsule_capabilities); + + EFI_QUERY_VARIABLE_INFO(*query_variable_info); +}; + +/* configuration table + * --- + * see: https://uefi.org/specs/UEFI/2.10/04_EFI_System_Table.html#efi-configuration-table-properties-table + */ + +struct efi_configuration_table { + efi_guid_t vendor_guid; + void *vendor_table; +}; + +/* protocols + * =========================================================================== + */ + +/* input protocol + * --- + * see: https://uefi.org/specs/UEFI/2.10/12_Protocols_Console_Support.html#simple-text-input-protocol + */ + +#define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \ + (efi_guid_t) {.vs = {0x387477c1, 0x69c711d2, 0x8e3900a0, 0xc969723b}} + +struct efi_input_key { + u16 scan_code; + c16 unicode_char; +}; + +#define EFI_INPUT_RESET(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_input_protocol *this, \ + IN b8 extended_verification) + +#define EFI_INPUT_READ_KEY(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_input_protocol *this, \ + OUT struct efi_input_key *key) + +struct efi_simple_text_input_protocol { + EFI_INPUT_RESET(*reset); + EFI_INPUT_READ_KEY(*read_key_stroke); + efi_event_t wait_for_key; +}; + +/* output protocol + * --- + * see: https://uefi.org/specs/UEFI/2.10/12_Protocols_Console_Support.html#simple-text-output-protocol + */ + +#define EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID \ + (efi_guid_t) {.vs = {0x387477c2, 0x69c711d2, 0x8e3900a0, 0xc969723b}} + +#define EFI_TEXT_RESET(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN b8 extended_verification) + +#define EFI_TEXT_STRING(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN c16 *string) + +#define EFI_TEXT_TEST_STRING(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN c16 *string) + +#define EFI_TEXT_QUERY_MODE(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN umm mode_number, \ + OUT umm *columns, \ + OUT umm *rows) + +#define EFI_TEXT_SET_MODE(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN umm mode) + +#define EFI_TEXT_SET_ATTRIBUTE(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN umm attribute) + +#define EFI_TEXT_CLEAR_SCREEN(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this) + +#define EFI_TEXT_SET_CURSOR_POSITION(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN umm column, \ + IN umm row) + +#define EFI_TEXT_ENABLE_CURSOR(name) \ + efi_status_t (EFIAPI name) (IN struct efi_simple_text_output_protocol *this, \ + IN b8 visible) + +struct efi_simple_text_output_mode { + s32 max_mode; + s32 mode; + s32 attribute; + s32 cursor_column; + s32 cursor_row; + b8 cursor_visible; +}; + +struct efi_simple_text_output_protocol { + EFI_TEXT_RESET(*reset); + EFI_TEXT_STRING(*output_string); + EFI_TEXT_TEST_STRING(*test_string); + EFI_TEXT_QUERY_MODE(*query_mode); + EFI_TEXT_SET_MODE(*set_mode); + EFI_TEXT_SET_ATTRIBUTE(*set_attribute); + EFI_TEXT_CLEAR_SCREEN(*clear_screen); + EFI_TEXT_SET_CURSOR_POSITION(*set_cursor_position); + EFI_TEXT_ENABLE_CURSOR(*enable_cursor); + struct efi_simple_text_output_mode *mode; +}; + +#endif /* EFI_H */ diff --git a/scripts/qemu-amd64.sh b/scripts/qemu-amd64.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# NOTE: replace this with the path to a downloaded OVMF image +OVMF_CODE="/usr/share/edk2-ovmf/OVMF_CODE.fd" +OVMF_VARS="/usr/share/edk2-ovmf/OVMF_VARS.fd" + +# params: +# $1 - disk image + +qemu-system-x86_64 \ + -drive if=pflash,format=raw,unit=0,file=$OVMF_CODE,readonly=on \ + -drive if=pflash,format=raw,unit=1,file=$OVMF_VARS,readonly=on \ + -drive if=none,format=raw,id=disk0,file="$1" \ + -device ahci,id=ahci \ + -device ide-hd,drive=disk0,bus=ahci.0 \ + -net none \ + -nographic + + #-machine q35 \ + #-m 64 \ + #-smp sockets=1,cpus=4 \ + + #-chardev stdio,id=char0,logfile="serial.log",signal=off \ + #-serial chardev:char0 \ diff --git a/src/boot-amd64.asm b/src/boot-amd64.asm @@ -0,0 +1,547 @@ +.intel_syntax + +.globl EntryPoint + +.equ EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION, 10 + +.equ IMAGE_MACHINE_x64, 0x8664 + +.equ IMAGE_FILE_EXECUTABLE_IMAGE, 0x0002 +.equ IMAGE_FILE_LARGE_ADDRESS_AWARE, 0x0020 +.equ IMAGE_FILE_DEBUG_STRIPPED, 0x0200 + +.equ PE32PLUS_MAGIC, 0x020b + +.equ IMAGE_SCN_CNT_CODE, 0x00000020 +.equ IMAGE_SCN_CNT_DATA, 0x00000040 +.equ IMAGE_SCN_MEM_SHARED, 0x10000000 +.equ IMAGE_SCN_MEM_EXECUTE, 0x20000000 +.equ IMAGE_SCN_MEM_READ, 0x40000000 +.equ IMAGE_SCN_MEM_WRITE, 0x80000000 + +.code64 + +.section .header, "a", %progbits +BEGIN: + +HEADER: + +MSDOS_HDR: +MSDOS_HDR.e_magic: .byte 'M', 'Z', 0x00, 0x00 +.fill 14, 4, 0x00000000 +MSDOS_HDR.e_lfanew: .int (PE_HDR - MSDOS_HDR) +MSDOS_HDR_END: + +MSDOS_STUB: +.fill 16, 4, 0x00000000 +MSDOS_STUB_END: + +PE_HDR: +PE_HDR.PEMagic: .byte 'P', 'E', 0x00, 0x00 +PE_HDR.Machine: .short IMAGE_MACHINE_x64 +PE_HDR.NumberOfSections: .short 2 +PE_HDR.TimeDateStamp: .int 1717539955 +PE_HDR.PointerToSymbolTable: .int 0 +PE_HDR.NumberOfSymbols: .int 0 +PE_HDR.SizeOfOptionalHeader: .short (PEOPT_HDR_END - PEOPT_HDR) +PE_HDR.Characteristics: .short (IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_LARGE_ADDRESS_AWARE | IMAGE_FILE_EXECUTABLE_IMAGE) +PE_HDR_END: + +PEOPT_HDR: +PEOPT_HDR.PEOptMagic: .short PE32PLUS_MAGIC +PEOPT_HDR.MajorLinkerVersion: .byte 0 +PEOPT_HDR.MinorLinkerVersion: .byte 0 +PEOPT_HDR.SizeOfCode: .int (CODE_END - CODE) +PEOPT_HDR.SizeOfData: .int (DATA_END - DATA) +PEOPT_HDR.SizeOfBss: .int 0 +PEOPT_HDR.AddressOfEntryPoint: .int (EntryPoint - BEGIN) +PEOPT_HDR.BaseOfCode: .int (CODE - BEGIN) + +PEOPT_HDR.BaseOfData: /* missing due to 64-bit ImageBase in 64-bit PEs*/ +PEOPT_HDR.ImageBase: .quad 0x40000 +PEOPT_HDR.SectionAlignment: .int 0x1000 +PEOPT_HDR.FileAlignment: .int 0x1000 +PEOPT_HDR.MajorOSVersion: .short 0 +PEOPT_HDR.MinorOSVersion: .short 0 +PEOPT_HDR.MajorImageVersion: .short 0 +PEOPT_HDR.MinorImageVersion: .short 0 +PEOPT_HDR.MajorSubsystemVersion:.short 0 +PEOPT_HDR.MinorSubsystemVersion:.short 0 +PEOPT_HDR.Win32VersionValue: .int 0 +PEOPT_HDR.SizeOfImage: .int (END - BEGIN) +PEOPT_HDR.SizeOfHeaders: .int (HEADER_END - HEADER) +PEOPT_HDR.Checksum: .int 0 +PEOPT_HDR.Subsystem: .short EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION +PEOPT_HDR.DllCharacteristics: .short 0 +PEOPT_HDR.SizeOfStackReserve: .quad 0x200000 +PEOPT_HDR.SizeOfStackCommit: .quad 0x1000 +PEOPT_HDR.SizeOfHeapReserve: .quad 0x200000 +PEOPT_HDR.SizeOfHeapCommit: .quad 0x1000 +PEOPT_HDR.LoaderFlags: .int 0 +PEOPT_HDR.NumberOfRvaAndSizes: .int 0 + +PEOPT_HDR_END: + +SECTIONS_HDR: + +SECTION_CODE: +SECTION_CODE.Name: .byte '.', 't', 'e', 'x', 't', 0x00, 0x00, 0x00 +SECTION_CODE.VirtualSize: .int (CODE_END - CODE) +SECTION_CODE.VirtualAddress: .int (CODE - BEGIN) +SECTION_CODE.SizeOfRawData: .int (CODE_END - CODE) +SECTION_CODE.PointerToRawData: .int (CODE - BEGIN) +SECTION_CODE.PointerToRelocs: .int 0 +SECTION_CODE.PointerToLineNums: .int 0 +SECTION_CODE.NumberOfRelocs: .short 0 +SECTION_CODE.NumberOfLineNums: .short 0 +SECTION_CODE.Characteristics: .int (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ) + +SECTION_DATA: +SECTION_DATA.Name: .byte '.', 'd', 'a', 't', 'a', 0x00, 0x00, 0x00 +SECTION_DATA.VirtualSize: .int (DATA_END - DATA) +SECTION_DATA.VirtualAddress: .int (DATA - BEGIN) +SECTION_DATA.SizeOfRawData: .int (DATA_END - DATA) +SECTION_DATA.PointerToRawData: .int (DATA - BEGIN) +SECTION_DATA.PointerToRelocs: .int 0 +SECTION_DATA.PointerToLineNums: .int 0 +SECTION_DATA.NumberOfRelocs: .short 0 +SECTION_DATA.NumberOfLineNums: .short 0 +SECTION_DATA.Characteristics: .int (IMAGE_SCN_CNT_DATA | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE) + +SECTIONS_HDR_END: + +HEADER_END: + +.align 16 +.section .text, "ax", %progbits + +CODE: + +.equ EFI_SUCCESS, 0 + +.equ EFI_ERROR, 0x8000000000000000 +.equ EFI_LOAD_ERROR, 1 +.equ EFI_INVALID_PARAMETER, 2 +.equ EFI_UNSUPPORTED, 3 +.equ EFI_BUFFER_TOO_SMALL, 4 +.equ EFI_NOT_FOUND, 14 + +.equ EFI_WARNING, 0x0000000000000000 + +/* typedef void *EFI_HANDLE; + * typedef EFI_HANDLE EFI_IMAGE_HANDLE; + * + * typedef struct { + * u64 Signature; + * u32 Revision; + * u32 HeaderSize; + * u32 CRC32; + * u32 Reserved; + * } EFI_TABLE_HEADER; + */ +.equ EFI_TABLE_HEADER_SZ, 24 + +/* typedef struct { + * EFI_GUID VendorGuid; + * void *VendorTable; + * } EFI_CONFIGURATION_TABLE; + * + * typedef struct { + * EFI_TABLE_HEADER Hdr; + * c16 *FirmwareVendor; + * u32 FirmwareRevision; + * EFI_HANDLE ConsoleInHandle; + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + * EFI_HANDLE ConsoleOutHandle; + * EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + * EFI_HANDLE StandardErrorHandle; + * EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; + * EFI_RUNTIME_SERVICES *RuntimeServices; + * EFI_BOOT_SERVICES *BootServices; + * umm NumberOfTableEntries; + * EFI_CONFIGURATION_TABLE *ConfigurationTable; + * } EFI_SYSTEM_TABLE; + */ +.equ EFI_SYSTEM_TABLE_CON_IN, (EFI_TABLE_HEADER_SZ + 24) +.equ EFI_SYSTEM_TABLE_CON_OUT, (EFI_TABLE_HEADER_SZ + 40) +.equ EFI_SYSTEM_TABLE_STD_ERR, (EFI_TABLE_HEADER_SZ + 56) +.equ EFI_SYSTEM_TABLE_RUNTIME_SERVICES, (EFI_TABLE_HEADER_SZ + 64) +.equ EFI_SYSTEM_TABLE_BOOT_SERVICES, (EFI_TABLE_HEADER_SZ + 72) +.equ EFI_SYSTEM_TABLE_NUBER_OF_TABLE_ENTS, (EFI_TABLE_HEADER_SZ + 80) +.equ EFI_SYSTEM_TABLE_CONFIGURATION_TABLE, (EFI_TABLE_HEADER_SZ + 88) + +/* typedef struct { + * EFI_INPUT_RESET Reset; + * EFI_INPUT_READ_KEY ReadKeyStroke; + * EFI_EVENT WaitForKey; + * } EFI_SIMPLE_TEXT_INPUT_PROTOCOL; + * + * typedef struct { + * s32 MaxMode; + * + * s32 Mode; + * s32 Attribute; + * s32 CursorColumn; + * s32 CursorRow; + * b8 CursorVisible; + * } SIMPLE_TEXT_OUTPUT_MODE; + * + * typedef struct { + * EFI_TEXT_RESET Reset; + * EFI_TEXT_STRING OutputString; + * EFI_TEXT_TEST_STRING TestString; + * EFI_TEXT_QUERY_MODE QueryMode; + * EFI_TEXT_SET_MODE SetMode; + * EFI_TEXT_SET_ATTRIBUTE SetAttribute; + * EFI_TEXT_CLEAR_SCREEN ClearScreen; + * EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition; + * EFI_TEXT_ENABLE_CURSOR EnableCursor; + * SIMPLE_TEXT_OUTPUT_MODE *Mode; + * } EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; + */ +.equ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET, 0 +.equ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUT_STRING, 8 +.equ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_ATTRIBUTE, 40 +.equ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_CLEAR_SCREEN, 48 + +.equ EFI_FG_WHITE, 0x0F + +.equ EFI_BG_BLACK, 0x00 +.equ EFI_BG_RED, 0x40 + +/* typedef struct { + * EFI_TABLE_HEADER Hdr; + * + * EFI_GET_TIME GetTime; + * EFI_SET_TIME SetTime; + * EFI_GET_WAKEUP_TIME GetWakeupTime; + * EFI_SET_WAKEUP_TIME SetWakeupTime; + * + * EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; + * EFI_CONVERT_POINTER ConvertPointer; + * + * EFI_GET_VARIABLE GetVariable; + * EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; + * EFI_SET_VARIABLE SetVariable; + * + * EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHightMonotonicCount; + * EFI_RESET_SYSTEM ResetSystem; + * + * // UEFI 2.0+ specified fields + * } EFI_RUNTIME_SERVICES; + */ +.equ EFI_RUNTIME_SERVICES_RESET_SYSTEM, (EFI_TABLE_HEADER_SZ + 80) + +.equ EFI_RESET_COLD, 0 +.equ EFI_RESET_WARM, 1 +.equ EFI_RESET_SHUTDOWN, 2 +.equ EFI_RESET_PLATFORM_SPECIFIC, 3 + +/* typedef struct { + * EFI_TABLE_HEADER Hdr; + * + * EFI_RAISE_TPL RaiseTPL; + * EFI_RESTORE_TPL RestoreTPL; + * + * EFI_ALLOCATE_PAGES AllocatePages; + * EFI_FREE_PAGES FreePages; + * EFI_GET_MEMORY_MAP GetMemoryMap; + * EFI_ALLOCATE_POOL AllocatePool; + * EFI_FREE_POOL FreePool; + * + * EFI_CREATE_EVENT CreateEvent; + * EFI_SET_TIMER SetTimer; + * EFI_WAIT_FOR_EVENT WaitForEvent; + * EFI_SIGNAL_EVENT SignalEvent; + * EFI_CLOSE_EVENT CloseEvent; + * EFI_CHECK_EVENT CheckEvent; + * + * EFI_INSTALL_PROTOCOL_INTERFACE InstallProtocolInterface; + * EFI_REINSTALL_PROTOCOL_INTERFACE ReinstallProtocolInterface; + * EFI_UNINSTALL_PROTOCOL_INTERFACE UninstallProtocolInterface; + * EFI_HANDLE_PROTOCOL HandleProtocol; + * void *Reserved; + * EFI_REGISTER_PROTOCOL_NOTIFY RegisterProtocolNotify; + * EFI_LOCATE_HANDLE LocateHandle; + * EFI_LOCATE_DEVICE_PATH LocateDevicePath; + * EFI_INSTALL_CONFIGURATION_TABLE InstallConfigurationTable; + * + * EFI_IMAGE_LOAD LoadImage; + * EFI_IMAGE_START StartImage; + * EFI_EXIT Exit; + * EFI_IMAGE_UNLOAD UnloadImage; + * EFI_EXIT_BOOT_SERVICES ExitBootServices; + * + * EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount; + * EFI_STALL Stall; + * EFI_SET_WATCHDOG_TIMER SetWatchdogTimer; + * + * // EFI 1.1+ specified fields + * EFI_CONNECT_CONTROLLER ConnectController; + * EFI_DISCONNECT_CONTROLLER DisconnectController; + * + * EFI_OPEN_PROTOCOL OpenProtocol; + * EFI_CLOSE_PROTOCOL CloseProtocol; + * EFI_OPEN_PROTOCOL_INFORMATION OpenProtocolInformation; + * + * EFI_PROTOCOLS_PER_HANDLE ProtocolsPerHandle; + * EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer; + * EFI_LOCATE_PROTOCOL LocateProtocol; + * EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES InstallMultipleProtocolInterfaces; + * EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES UninstallMultipleProtocolInterfaces; + * + * EFI_CALCULATE_CRC32 CalculateCRC32; + * + * EFI_COPY_MEM CopyMem; + * EFI_SET_MEM SetMem; + * + * // UEFI 2.0+ specified fields + * } EFI_BOOT_SERVICES; + */ +.equ EFI_BOOT_SERVICES_GET_MEMORY_MAP, (EFI_TABLE_HEADER_SZ + 40) +.equ EFI_BOOT_SERVICES_EXIT, (EFI_TABLE_HEADER_SZ + 192) +.equ EFI_BOOT_SERVICES_EXIT_BOOT_SERVICES, (EFI_TABLE_HEADER_SZ + 208) +.equ EFI_BOOT_SERVICES_STALL, (EFI_TABLE_HEADER_SZ + 224) +.equ EFI_BOOT_SERVICES_LOCATE_PROTOCOL, (EFI_TABLE_HEADER_SZ + 296) + +.equ MICROS, 1000000 + +/* we get called with the following state: + * rcx: EFI_HANDLE + * rdx: EFI_SYSTEM_TABLE * + * rsp: 8 byte offset, <retaddr> + * + * NOTE: UEFI requires a 16-byte aligned stack, and due to the return address + * being passed on the stack, we have an 8-byte offset. UEFI also expects + * a 32-byte shadow space to allow pushing all 4 potential arg registers. + */ +EntryPoint: + + /* NOTE: fix up the stack due to the following rules: + * sub rsp, (a + b*8) + * - a : realign stack pointer to 16-bytes, accounting for the passed retaddr + * - b : we need to reserve shadow space for preserved registers + */ + sub rsp, (8 + 8*8) + + mov rax, [rdx + EFI_SYSTEM_TABLE_CON_OUT] + mov rax, [rax + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET] + mov rcx, [rdx + EFI_SYSTEM_TABLE_CON_OUT] + xor rdx, rdx + call rax + + mov rax, [rdx + EFI_SYSTEM_TABLE_CON_OUT] + mov rax, [rax + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUT_STRING] + mov rcx, [rdx + EFI_SYSTEM_TABLE_CON_OUT] + lea rdx, [MSG_LOAD_KERNEL] + call rax + +wait: + jmp wait + + add rsp, (8 + 8*8) + ret + + mov EFI_IMAGE_HANDLE, rcx + mov EFI_SYSTEM_TABLE, rdx + + mov rax, [rdx + EFI_SYSTEM_TABLE_CON_OUT] + mov EFI_STDOUT, rax + + mov rax, [rdx + EFI_SYSTEM_TABLE_RUNTIME_SERVICES] + mov EFI_RUNTIME_SERVICES, rax + + mov rax, [rdx + EFI_SYSTEM_TABLE_BOOT_SERVICES] + mov EFI_BOOT_SERVICES, rax + + mov rax, [rdx + EFI_SYSTEM_TABLE_CONFIGURATION_TABLE] + mov EFI_CONFIGURATION_TABLE, rax + + jmp load_kernel + +get_memory_map: + /* typedef struct { + * u32 Type; + * EFI_PHYSICAL_ADDRESS PhysicalStart; + * EFI_VIRTUAL_ADDRESS VirtualStart; + * u64 NumberOfPages; + * u64 Attribute; + * } EFI_MEMORY_DESCRIPTOR; + * + * typedef EFI_STATUS (EFIAPI *EFI_GET_MEMORY_MAP) ( + * IN OUT umm *MemoryMapSize, + * OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + * OUT umm *MapKey, + * OUT umm *DescriptorSize, + * OUT u32 *DescriptorVersion + * ); + */ + lea rcx, MEMORY_MAP_SIZE + lea rdx, MEMORY_MAP + lea r8, MEMORY_MAP_KEY + lea r9, MEMORY_DESCRIPTOR_SIZE + lea rax, MEMORY_DESCRIPTOR_VERSION + mov [rsp+32], rax + mov rax, EFI_BOOT_SERVICES + call [rax + EFI_BOOT_SERVICES_GET_MEMORY_MAP] + cmp al, EFI_BUFFER_TOO_SMALL /* correct buffer size returned in [MEMORY_MAP_SIZE] */ + je get_memory_map + + cmp rax, EFI_SUCCESS + jne failure + + /* typedef EFI_STATUS (EFIAPI *EFI_EXIT_BOOT_SERVICES) ( + * IN EFI_HANDLE ImageHandle, + * IN umm MapKey + * ); + */ + mov rcx, EFI_IMAGE_HANDLE + mov rdx, MEMORY_MAP_KEY + mov rax, EFI_BOOT_SERVICES + call [rax + EFI_BOOT_SERVICES_EXIT_BOOT_SERVICES] + cmp rax, EFI_SUCCESS /* was our memory map key invalid? */ + jne get_memory_map + +load_kernel: + + /* typedef EFI_STATUS (EFIAPI *EFI_TEXT_RESET) ( + * IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + * IN b8 ExtendedVerification + * ); + */ + mov rcx, EFI_STDOUT + mov rdx, 1 + call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET] + cmp rax, EFI_SUCCESS + jne failure + + /* typedef EFI_STATUS (EFIAPI *EFI_TEXT_SET_ATTRIBUTE) ( + * IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + * IN umm Attribute + * ); + */ + mov rcx, EFI_STDOUT + mov rdx, EFI_FG_WHITE | EFI_BG_BLACK + call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_ATTRIBUTE] + cmp rax, EFI_SUCCESS + jne failure + + /* typedef EFI_STATUS (EFIAPI *EFI_TEXT_OUTPUT_CLEAR_SCREEN) ( + * IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + * ); + */ + mov rcx, EFI_STDOUT + call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_CLEAR_SCREEN] + cmp rax, EFI_SUCCESS + jne failure + + /* typedef EFI_STATUS (EFIAPI *EFI_TEXT_OUTPUT_STRING) ( + * IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + * IN c16 *String + * ); + */ + mov rcx, EFI_STDOUT + lea rdx, MSG_LOAD_KERNEL + call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUT_STRING] + cmp rax, EFI_SUCCESS + jne failure + + /* typedef EFI_STATUS (EFIAPI *EFI_STALL) ( + * IN umm Microseconds + * ); + */ + mov rcx, 10 * MICROS + mov rax, [EFI_BOOT_SERVICES] + call [rax + EFI_BOOT_SERVICES_STALL] + + /* TODO: enable efi framebuffer for drawing */ + + /* typedef EFI_STATUS (EFIAPI *EFI_RESET_SYSTEM) ( + * IN EFI_RESET_TYPE ResetType, + * IN EFI_STATUS ExitStatus, + * IN umm DataSize, + * IN c16 *ResetData OPTIONAL + * ); + */ + mov rcx, EFI_RESET_SHUTDOWN + mov rdx, EFI_SUCCESS + mov r8, 0 + mov r9, 0 + mov rax, [EFI_RUNTIME_SERVICES] + call [rax + EFI_RUNTIME_SERVICES_RESET_SYSTEM] + + /* EFI_RESET_SYSTEM does not return */ + + mov rax, EFI_SUCCESS + jmp exit + +failure: + mov rcx, [EFI_STDOUT] + lea rdx, [MSG_FAILURE] + call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUT_STRING] + + /* typedef EFI_STATUS (EFIAPI *EFI_EXIT) ( + * IN EFI_HANDLE ImageHandle, + * IN EFI_STATUS ExitStatus, + * IN umm ExitDataSize, + * IN c16 *ExitData OPTIONAL + * ); + */ + mov rcx, [EFI_IMAGE_HANDLE] + mov rdx, EFI_ERROR | EFI_LOAD_ERROR + mov r8, 0 + mov r9, 0 + mov rax, [EFI_BOOT_SERVICES] + call [rax + EFI_BOOT_SERVICES_EXIT] + + /* EFI_EXIT does not return */ + + mov rax, EFI_ERROR | EFI_LOAD_ERROR + jmp exit + +exit: + add rsp, (8 + 8*8) /* undo UEFI stack constraints */ + retn + +CODE_END: + +.align 4096 +.section .data, "aw", %progbits + +DATA: + +MSG_LOAD_KERNEL: .short 'U', 'E', 'F', 'I', 13, 10, 0 + +.align 16 +EFI_IMAGE_HANDLE: .quad 0 +EFI_SYSTEM_TABLE: .quad 0 + +EFI_STDOUT: .quad 0 + +EFI_RUNTIME_SERVICES: .quad 0 +EFI_BOOT_SERVICES: .quad 0 +EFI_CONFIGURATION_TABLE: .quad 0 + +MEMORY_MAP_SIZE: .quad (32 * 1024) /* initially requested memory map buffer size */ +MEMORY_MAP: .quad 0x200000 /* address at which to place the memory map */ +MEMORY_MAP_KEY: .quad 0 +MEMORY_DESCRIPTOR_SIZE: .quad 0 +MEMORY_DESCRIPTOR_VERSION: .quad 0 + +/* all strings are in unicode */ +.align 16 +MSG_FAILURE: .short 'F', 'A', 'I', 'L', 13, 10, 0 + +/* +.align 16 +MSG_LOAD_KERNEL: .short 'U', 'E', 'F', 'I', 13, 10, 0 +*/ + +DATA_END: + +.align 4096 +PAYLOAD: + +PAYLOAD_END: + +END: diff --git a/src/boot.c b/src/boot.c @@ -0,0 +1,24 @@ +#include "efi.h" + +EFI_IMAGE_ENTRY_POINT(efi_main) +{ + efi_status_t status; + +#if 1 + status = system_table->con_out->reset(system_table->con_out, false); + if (status != 0) + return status; + + status = system_table->con_out->clear_screen(system_table->con_out); + if (status != 0) + return status; +#endif + + status = system_table->con_out->output_string(system_table->con_out, u"UEFI"); + if (status != 0) + return status; + + system_table->runtime_services->reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); + + return EFI_SUCCESS; +} diff --git a/src/boot.ld b/src/boot.ld @@ -0,0 +1,18 @@ +ENTRY(EntryPoint) + +SECTIONS +{ + . = 0x40000; + + .header : { + *(.header) + } + + .text : { + *(.text) + } + + .data : { + *(.data) + } +} diff --git a/src/kernel-amd64.c b/src/kernel-amd64.c @@ -0,0 +1,4 @@ +void +kmain(void) +{ +} diff --git a/src/kernel.c b/src/kernel.c @@ -0,0 +1,7 @@ +extern void kmain(void); + +void +_start(void) +{ + kmain(); +} diff --git a/src/kernel.ld b/src/kernel.ld @@ -0,0 +1,20 @@ +SECTIONS +{ + . = 0x100000; + + .text : { + *(.text) + } + + .data : { + *(.data) + } + + .rodata : { + *(.rodata) + } + + .bss : { + *(.bss) + } +} diff --git a/toolchain.sh b/toolchain.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +. "$(dirname $0)/config.sh" + +TARGET="" +case $ARCH in + amd64) + TARGET="x86_64-unknown-elf" + ;; + + aarch64) + TARGET="aarch64-unknown-elf" + ;; + + riscv64) + TARGET="riscv64-unknown-elf" + ;; + + *) + echo "Unsupported architecture: $ARCH. Please see config.sh for all supported architectures" + exit 1 + ;; +esac + +export CC="clang" +export CXX="clang++" +export LD="ld.lld" +export OBJCOPY="llvm-objcopy" + +export ASFLAGS="--target=$TARGET" +export CFLAGS="--target=$TARGET -ffreestanding -nostdlib -nostdinc" +export CXXFLAGS="--target=$TARGET -ffreestanding -nostdlib -nostdinc" +export CPPFLAGS="-DEARCH=$ARCH -Iinclude" +export LDFLAGS=""