Working kernel written on C and userspace-ready #1
@@ -9,13 +9,13 @@ set(UEFI_COMPILE_OPTIONS
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_executable(BOOTAA64
|
add_executable(BOOTAA64
|
||||||
Source/uefi/efi_entry.c
|
Source/modules/uefi/efi_entry.c
|
||||||
Source/main.c
|
Source/main.c
|
||||||
)
|
)
|
||||||
target_compile_options(BOOTAA64 PRIVATE ${UEFI_COMPILE_OPTIONS})
|
target_compile_options(BOOTAA64 PRIVATE ${UEFI_COMPILE_OPTIONS})
|
||||||
target_include_directories(BOOTAA64 PRIVATE
|
target_include_directories(BOOTAA64 PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Source
|
${CMAKE_CURRENT_SOURCE_DIR}/Source
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/uefi
|
${CMAKE_CURRENT_SOURCE_DIR}/Source/modules/uefi
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_options(BOOTAA64 PRIVATE
|
target_link_options(BOOTAA64 PRIVATE
|
||||||
|
|||||||
+78
-20
@@ -1,10 +1,11 @@
|
|||||||
#include "uefi/uefi.h"
|
#include "modules/uefi/uefi.h"
|
||||||
|
#include "modules/elf/elf.h"
|
||||||
#include "../../Common/bootinfo.h"
|
#include "../../Common/bootinfo.h"
|
||||||
|
|
||||||
#define PAGE_SIZE 0x1000
|
#define PAGE_SIZE 0x1000
|
||||||
#define WSTR(str) ((wchar_t*)L##str)
|
#define WSTR(str) ((wchar_t*)L##str)
|
||||||
|
|
||||||
static wchar_t* kernel_path = WSTR("ksOSKernel.bin");
|
static wchar_t* kernel_path = WSTR("ksOSKernel.elf");
|
||||||
static efi_guid_t dtb_guid = {
|
static efi_guid_t dtb_guid = {
|
||||||
0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}
|
0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}
|
||||||
};
|
};
|
||||||
@@ -13,6 +14,14 @@ static void print(const wchar_t* msg) {
|
|||||||
ST->ConOut->OutputString(ST->ConOut, (wchar_t*)msg);
|
ST->ConOut->OutputString(ST->ConOut, (wchar_t*)msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* memset(void* destination, int value, uint64_t count) {
|
||||||
|
uint8_t* ptr = (uint8_t*)destination;
|
||||||
|
while (count--) {
|
||||||
|
*ptr++ = (uint8_t)value;
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
static efi_status_t die(void) {
|
static efi_status_t die(void) {
|
||||||
while (1) {
|
while (1) {
|
||||||
__asm__ volatile("wfi");
|
__asm__ volatile("wfi");
|
||||||
@@ -83,33 +92,73 @@ static efi_status_t read_file_info(efi_file_handle_t* file, efi_file_info_t** fi
|
|||||||
return file->GetInfo(file, &file_info_guid, &file_info_size, *file_info);
|
return file->GetInfo(file, &file_info_guid, &file_info_size, *file_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static efi_status_t load_kernel(efi_file_handle_t* root, efi_physical_address_t* kernel_addr, uint64_t* kernel_size) {
|
static efi_status_t load_kernel(efi_file_handle_t* root, efi_physical_address_t* kernel_addr, uint64_t* kernel_size, efi_file_handle_t** out_handle) {
|
||||||
efi_file_handle_t* kernel_file = NULL;
|
efi_file_handle_t* kernel_file = NULL;
|
||||||
efi_file_info_t* kernel_info = NULL;
|
|
||||||
efi_status_t status = root->Open(root, &kernel_file, kernel_path, EFI_FILE_MODE_READ, 0);
|
efi_status_t status = root->Open(root, &kernel_file, kernel_path, EFI_FILE_MODE_READ, 0);
|
||||||
if (EFI_ERROR(status)) {
|
if (EFI_ERROR(status)) return status;
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
efi_file_info_t* kernel_info = NULL;
|
||||||
status = read_file_info(kernel_file, &kernel_info);
|
status = read_file_info(kernel_file, &kernel_info);
|
||||||
if (EFI_ERROR(status)) {
|
if (EFI_ERROR(status)) return status;
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*kernel_size = kernel_info->FileSize;
|
*kernel_size = kernel_info->FileSize;
|
||||||
|
|
||||||
uintn_t bytes_to_read = (uintn_t)*kernel_size;
|
|
||||||
uintn_t kernel_pages = (*kernel_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
uintn_t kernel_pages = (*kernel_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||||
*kernel_addr = 0;
|
|
||||||
|
|
||||||
status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, kernel_pages, kernel_addr);
|
status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderData, kernel_pages, kernel_addr);
|
||||||
if (EFI_ERROR(status)) {
|
if (EFI_ERROR(status)) return status;
|
||||||
return status;
|
|
||||||
|
uintn_t bytes_to_read = (uintn_t)*kernel_size;
|
||||||
|
status = kernel_file->Read(kernel_file, &bytes_to_read, (void*)*kernel_addr);
|
||||||
|
if (EFI_ERROR(status)) return status;
|
||||||
|
|
||||||
|
*out_handle = kernel_file;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static efi_status_t parse_elf_headers(efi_physical_address_t kernel_addr) {
|
||||||
|
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)kernel_addr;
|
||||||
|
|
||||||
|
if (elf_header->e_ident[0] != 0x7f || elf_header->e_ident[1] != 'E' ||
|
||||||
|
elf_header->e_ident[2] != 'L' || elf_header->e_ident[3] != 'F') {
|
||||||
|
return fail(WSTR("Invalid ELF header\r\n"));
|
||||||
|
}
|
||||||
|
if (elf_header->e_machine != 0xB7) { // AArch64
|
||||||
|
return fail(WSTR("Invalid ELF machine\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
status = kernel_file->Read(kernel_file, &bytes_to_read, (void*)*kernel_addr);
|
return EFI_SUCCESS;
|
||||||
if (EFI_ERROR(status) || bytes_to_read != (uintn_t)*kernel_size) {
|
}
|
||||||
return EFI_LOAD_ERROR;
|
|
||||||
|
static efi_status_t load_elf_segments(efi_physical_address_t kernel_addr, efi_file_handle_t* kernel_file) {
|
||||||
|
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)kernel_addr;
|
||||||
|
|
||||||
|
for (int i = 0; i < elf_header->e_phnum; i++) {
|
||||||
|
Elf64_Phdr* phdr = (Elf64_Phdr*)(kernel_addr + elf_header->e_phoff + i * elf_header->e_phentsize);
|
||||||
|
|
||||||
|
if (phdr->p_type != PT_LOAD) continue;
|
||||||
|
if (phdr->p_vaddr < 0x40000000) {
|
||||||
|
print(WSTR("Skipping low/weird segment\r\n"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintn_t pages = (phdr->p_memsz + 0xFFF) / 0x1000;
|
||||||
|
efi_physical_address_t segment_addr = phdr->p_vaddr;
|
||||||
|
|
||||||
|
efi_status_t status = gBS->AllocatePages(AllocateAddress, EfiLoaderData, pages, &segment_addr);
|
||||||
|
if (EFI_ERROR(status)) {
|
||||||
|
if (status == EFI_NOT_FOUND) print(WSTR(" (Range not in RAM) "));
|
||||||
|
if (status == EFI_OUT_OF_RESOURCES) print(WSTR(" (Occupied) "));
|
||||||
|
return fail(WSTR("Address conflict!\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((void*)segment_addr, 0, phdr->p_memsz);
|
||||||
|
kernel_file->SetPosition(kernel_file, phdr->p_offset);
|
||||||
|
|
||||||
|
uintn_t size_to_read = phdr->p_filesz;
|
||||||
|
status = kernel_file->Read(kernel_file, &size_to_read, (void*)segment_addr);
|
||||||
|
if (EFI_ERROR(status) || size_to_read != phdr->p_filesz) {
|
||||||
|
return fail(WSTR("File read error\r\n"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
@@ -175,11 +224,20 @@ efi_status_t bootloader_main(void) {
|
|||||||
|
|
||||||
efi_physical_address_t kernel_addr = 0;
|
efi_physical_address_t kernel_addr = 0;
|
||||||
uint64_t kernel_size = 0;
|
uint64_t kernel_size = 0;
|
||||||
status = load_kernel(root, &kernel_addr, &kernel_size);
|
efi_file_handle_t* kernel_file = NULL;
|
||||||
|
status = load_kernel(root, &kernel_addr, &kernel_size, &kernel_file);
|
||||||
if (EFI_ERROR(status)) {
|
if (EFI_ERROR(status)) {
|
||||||
return fail(WSTR("Failed to load ksOSKernel.bin\r\n"));
|
return fail(WSTR("Failed to load ksOSKernel.bin\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Elf64_Ehdr* elf_header = (Elf64_Ehdr*)kernel_addr;
|
||||||
|
|
||||||
|
status = parse_elf_headers(kernel_addr);
|
||||||
|
if (EFI_ERROR(status)) return status;
|
||||||
|
|
||||||
|
status = load_elf_segments(kernel_addr, kernel_file);
|
||||||
|
if (EFI_ERROR(status)) return status;
|
||||||
|
|
||||||
Bootinfo* boot_info = NULL;
|
Bootinfo* boot_info = NULL;
|
||||||
status = gBS->AllocatePool(EfiLoaderData, sizeof(Bootinfo), (void**)&boot_info);
|
status = gBS->AllocatePool(EfiLoaderData, sizeof(Bootinfo), (void**)&boot_info);
|
||||||
if (EFI_ERROR(status) || boot_info == NULL) {
|
if (EFI_ERROR(status) || boot_info == NULL) {
|
||||||
@@ -203,7 +261,7 @@ efi_status_t bootloader_main(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*kernel_entry_t)(Bootinfo*);
|
typedef void (*kernel_entry_t)(Bootinfo*);
|
||||||
kernel_entry_t kernel_main = (kernel_entry_t)kernel_addr;
|
kernel_entry_t kernel_main = (kernel_entry_t)elf_header->e_entry;
|
||||||
kernel_main(boot_info);
|
kernel_main(boot_info);
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../modules/uefi/uefi.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t e_ident[16];
|
||||||
|
uint16_t e_type;
|
||||||
|
uint16_t e_machine;
|
||||||
|
uint32_t e_version;
|
||||||
|
uint64_t e_entry;
|
||||||
|
uint64_t e_phoff;
|
||||||
|
uint64_t e_shoff;
|
||||||
|
uint32_t e_flags;
|
||||||
|
uint16_t e_ehsize;
|
||||||
|
uint16_t e_phentsize;
|
||||||
|
uint16_t e_phnum;
|
||||||
|
uint16_t e_shentsize;
|
||||||
|
uint16_t e_shnum;
|
||||||
|
uint16_t e_shstrndx;
|
||||||
|
} Elf64_Ehdr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t p_type;
|
||||||
|
uint32_t p_flags;
|
||||||
|
uint64_t p_offset;
|
||||||
|
uint64_t p_vaddr;
|
||||||
|
uint64_t p_paddr;
|
||||||
|
uint64_t p_filesz;
|
||||||
|
uint64_t p_memsz;
|
||||||
|
uint64_t p_align;
|
||||||
|
} Elf64_Phdr;
|
||||||
|
|
||||||
|
#define PT_LOAD 1
|
||||||
@@ -36,6 +36,8 @@ typedef uint16_t wchar_t;
|
|||||||
#define EFI_LOAD_ERROR EFIERR(1)
|
#define EFI_LOAD_ERROR EFIERR(1)
|
||||||
#define EFI_UNSUPPORTED EFIERR(3)
|
#define EFI_UNSUPPORTED EFIERR(3)
|
||||||
#define EFI_BUFFER_TOO_SMALL EFIERR(5)
|
#define EFI_BUFFER_TOO_SMALL EFIERR(5)
|
||||||
|
#define EFI_OUT_OF_RESOURCES EFIERR(9)
|
||||||
|
#define EFI_NOT_FOUND EFIERR(14)
|
||||||
#define EFI_ABORTED EFIERR(21)
|
#define EFI_ABORTED EFIERR(21)
|
||||||
|
|
||||||
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
|
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002
|
||||||
@@ -33,14 +33,11 @@ target_link_options(Kernel PRIVATE
|
|||||||
-no-pie
|
-no-pie
|
||||||
-T "${CMAKE_CURRENT_SOURCE_DIR}/linker.ld"
|
-T "${CMAKE_CURRENT_SOURCE_DIR}/linker.ld"
|
||||||
-z max-page-size=0x1000
|
-z max-page-size=0x1000
|
||||||
|
--image-base=0x40100000
|
||||||
|
--no-dynamic-linker
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(Kernel PROPERTIES
|
set_target_properties(Kernel PROPERTIES
|
||||||
OUTPUT_NAME "Kernel.elf"
|
OUTPUT_NAME "ksOSKernel.elf"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(TARGET Kernel POST_BUILD
|
|
||||||
COMMAND ${LLVM_OBJCOPY} -O binary ${CMAKE_BINARY_DIR}/Kernel.elf ${CMAKE_BINARY_DIR}/Kernel.bin
|
|
||||||
COMMENT "Generating ksOSKernel.bin from Kernel.elf"
|
|
||||||
)
|
|
||||||
+2
-2
@@ -7,8 +7,8 @@ build:
|
|||||||
|
|
||||||
cmake --build {{TEMP_DIR}}/Kernel
|
cmake --build {{TEMP_DIR}}/Kernel
|
||||||
|
|
||||||
cp {{TEMP_DIR}}/Kernel/Kernel.bin {{BUILD_DIR}}/Kernel/ksOSKernel.bin
|
cp {{TEMP_DIR}}/Kernel/ksOSKernel.elf {{BUILD_DIR}}/Kernel/ksOSKernel.elf
|
||||||
@echo "✅ Kernel ready at: {{BUILD_DIR}}/Kernel/ksOSKernel.bin"
|
@echo "✅ Kernel ready at: {{BUILD_DIR}}/Kernel/ksOSKernel.elf"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf {{TEMP_DIR}}/Kernel
|
rm -rf {{TEMP_DIR}}/Kernel
|
||||||
|
|||||||
+14
-9
@@ -1,15 +1,20 @@
|
|||||||
ENTRY(_start)
|
PHDRS
|
||||||
|
{
|
||||||
|
text PT_LOAD FLAGS(5); /* Read | Execute */
|
||||||
|
data PT_LOAD FLAGS(6); /* Read | Write */
|
||||||
|
}
|
||||||
|
|
||||||
SECTIONS {
|
SECTIONS
|
||||||
. = 0;
|
{
|
||||||
|
. = 0x40100000;
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
*(.text.boot)
|
*(.text.boot)
|
||||||
*(.text*)
|
*(.text*)
|
||||||
}
|
} :text
|
||||||
|
|
||||||
.rodata : { *(.rodata*) }
|
.rodata : { *(.rodata*) } :text
|
||||||
.data : { *(.data*) }
|
|
||||||
.bss : { *(.bss*) *(COMMON) }
|
.data : { *(.data*) } :data
|
||||||
|
.bss : { *(.bss*) *(COMMON) } :data
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ DISPLAY_FLAGS := if OS_NAME == "macos" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ACCEL_INFO := if ACCEL == "" { "none (TCG)" } else { ACCEL }
|
ACCEL_INFO := if ACCEL == "" { "none (TCG)" } else { ACCEL }
|
||||||
|
RAM := "2G"
|
||||||
|
|
||||||
export BUILD_DIR := justfile_directory() + "/.build"
|
export BUILD_DIR := justfile_directory() + "/.build"
|
||||||
export TEMP_DIR := BUILD_DIR + "/temp"
|
export TEMP_DIR := BUILD_DIR + "/temp"
|
||||||
@@ -64,14 +65,14 @@ _prep:
|
|||||||
@mkfs.fat -F 32 {{IMG_FILE}} > /dev/null
|
@mkfs.fat -F 32 {{IMG_FILE}} > /dev/null
|
||||||
@mmd -i {{IMG_FILE}} ::/EFI ::/EFI/BOOT
|
@mmd -i {{IMG_FILE}} ::/EFI ::/EFI/BOOT
|
||||||
@mcopy -i {{IMG_FILE}} {{BOOT_BIN}} ::/EFI/BOOT/BOOTAA64.EFI
|
@mcopy -i {{IMG_FILE}} {{BOOT_BIN}} ::/EFI/BOOT/BOOTAA64.EFI
|
||||||
@mcopy -i {{IMG_FILE}} {{BUILD_DIR}}/Kernel/ksOSKernel.bin ::/ksOSKernel.bin
|
@mcopy -i {{IMG_FILE}} {{BUILD_DIR}}/Kernel/ksOSKernel.elf ::/ksOSKernel.elf
|
||||||
|
|
||||||
@run: _image
|
@run: _image
|
||||||
@echo "🚀 Launching (accel: {{ACCEL_INFO}})..."
|
@echo "🚀 Launching (accel: {{ACCEL_INFO}})..."
|
||||||
qemu-system-aarch64 {{ACCEL}} \
|
qemu-system-aarch64 {{ACCEL}} \
|
||||||
-machine virt,acpi=off \
|
-machine virt,acpi=off \
|
||||||
-cpu {{CPU}} \
|
-cpu {{CPU}} \
|
||||||
-m 512M \
|
-m {{RAM}} \
|
||||||
-device ramfb \
|
-device ramfb \
|
||||||
{{DISPLAY_FLAGS}} \
|
{{DISPLAY_FLAGS}} \
|
||||||
-drive if=pflash,format=raw,readonly=on,file={{OVMF_ARM}} \
|
-drive if=pflash,format=raw,readonly=on,file={{OVMF_ARM}} \
|
||||||
@@ -85,7 +86,7 @@ _prep:
|
|||||||
qemu-system-aarch64 {{ACCEL}} \
|
qemu-system-aarch64 {{ACCEL}} \
|
||||||
-machine virt,acpi=off \
|
-machine virt,acpi=off \
|
||||||
-cpu {{CPU}} \
|
-cpu {{CPU}} \
|
||||||
-m 512M \
|
-m {{RAM}} \
|
||||||
-device ramfb \
|
-device ramfb \
|
||||||
{{DISPLAY_FLAGS}} \
|
{{DISPLAY_FLAGS}} \
|
||||||
-drive if=pflash,format=raw,readonly=on,file={{OVMF_ARM}} \
|
-drive if=pflash,format=raw,readonly=on,file={{OVMF_ARM}} \
|
||||||
|
|||||||
Reference in New Issue
Block a user