boot: Add EFISTUB and refactor boot protocols
This commit is contained in:
@@ -5,6 +5,7 @@ config KERNEL_FLAVOR
|
||||
default "vanilla"
|
||||
|
||||
source "arch/$(ARCH)/Kconfig"
|
||||
source "boot/Kconfig"
|
||||
source "mem/Kconfig"
|
||||
source "sched/Kconfig"
|
||||
source "dev/Kconfig"
|
||||
|
||||
@@ -4,6 +4,7 @@ config X86_64
|
||||
bool
|
||||
default y
|
||||
select ACPI
|
||||
select HAVE_EFISTUB
|
||||
select HAVE_MMU
|
||||
select HAVE_MMU_GENERIC
|
||||
select HAVE_TLB_SHOOTDOWN_IPI
|
||||
|
||||
@@ -5,3 +5,20 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint64_t pte_t;
|
||||
|
||||
enum : pte_t {
|
||||
ARCH_FLAG_PRESENT = 1 << 0,
|
||||
ARCH_FLAG_READ_WRITE = 1 << 1,
|
||||
ARCH_FLAG_USER_MODE = 1 << 2,
|
||||
ARCH_FLAG_WRITE_THROUGH = 1 << 3,
|
||||
ARCH_FLAG_CACHE_DISABLE = 1 << 4,
|
||||
ARCH_FLAG_ACCESSED = 1 << 5,
|
||||
ARCH_FLAG_DIRTY = 1 << 6,
|
||||
ARCH_FLAG_SIZE = 1 << 7,
|
||||
ARCH_FLAG_GLOBAL = 1 << 8,
|
||||
ARCH_FLAG_AVAILABLE = 1 << 9,
|
||||
ARCH_FLAG_ATTRIBUTE_TABLE = 1 << 7,
|
||||
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
|
||||
|
||||
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
|
||||
};
|
||||
|
||||
@@ -187,6 +187,7 @@ enum : uint64_t {
|
||||
CR4_OSFXSR = 1ull << 9,
|
||||
CR4_OSXMMEXCPT = 1ull << 10,
|
||||
CR4_UMIP = 1ull << 11,
|
||||
CR4_LA57 = 1ull << 12,
|
||||
CR4_VMXE = 1ull << 13,
|
||||
CR4_SMXE = 1ull << 14,
|
||||
CR4_FSGSBASE = 1ull << 16,
|
||||
|
||||
@@ -2,20 +2,3 @@
|
||||
|
||||
#pragma once
|
||||
#include <zinnia/pmap.h>
|
||||
|
||||
enum : pte_t {
|
||||
ARCH_FLAG_PRESENT = 1 << 0,
|
||||
ARCH_FLAG_READ_WRITE = 1 << 1,
|
||||
ARCH_FLAG_USER_MODE = 1 << 2,
|
||||
ARCH_FLAG_WRITE_THROUGH = 1 << 3,
|
||||
ARCH_FLAG_CACHE_DISABLE = 1 << 4,
|
||||
ARCH_FLAG_ACCESSED = 1 << 5,
|
||||
ARCH_FLAG_DIRTY = 1 << 6,
|
||||
ARCH_FLAG_SIZE = 1 << 7,
|
||||
ARCH_FLAG_GLOBAL = 1 << 8,
|
||||
ARCH_FLAG_AVAILABLE = 1 << 9,
|
||||
ARCH_FLAG_ATTRIBUTE_TABLE = 1 << 7,
|
||||
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
|
||||
|
||||
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
config HAVE_EFISTUB
|
||||
bool
|
||||
|
||||
menu "Boot loaders"
|
||||
|
||||
config BOOT_EFISTUB
|
||||
bool "EFI stub"
|
||||
depends on HAVE_EFISTUB
|
||||
default y
|
||||
help
|
||||
Kernel loader for UEFI systems.
|
||||
|
||||
config BOOT_LIMINE
|
||||
bool "Limine"
|
||||
default n
|
||||
help
|
||||
ELF loader with the Limine protocol.
|
||||
|
||||
endmenu
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "efi_arch.h"
|
||||
|
||||
typedef uint64_t efi_status;
|
||||
typedef void* efi_handle;
|
||||
typedef uint16_t char16;
|
||||
|
||||
constexpr efi_status EFI_SUCCESS = 0;
|
||||
constexpr efi_status EFI_ERROR = 1ull << 63;
|
||||
constexpr efi_status EFI_LOAD_ERROR = EFI_ERROR | 1;
|
||||
constexpr efi_status EFI_INVALID_PARAMETER = EFI_ERROR | 2;
|
||||
constexpr efi_status EFI_UNSUPPORTED = EFI_ERROR | 3;
|
||||
constexpr efi_status EFI_BUFFER_TOO_SMALL = EFI_ERROR | 5;
|
||||
constexpr efi_status EFI_OUT_OF_RESOURCES = EFI_ERROR | 9;
|
||||
constexpr efi_status EFI_NOT_FOUND = EFI_ERROR | 14;
|
||||
|
||||
struct efi_guid {
|
||||
uint32_t data1;
|
||||
uint16_t data2;
|
||||
uint16_t data3;
|
||||
uint8_t data4[8];
|
||||
};
|
||||
|
||||
enum efi_memory_type : uint32_t {
|
||||
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,
|
||||
};
|
||||
|
||||
enum efi_allocate_type : uint32_t {
|
||||
EFI_ALLOCATE_ANY_PAGES,
|
||||
EFI_ALLOCATE_MAX_ADDRESS,
|
||||
EFI_ALLOCATE_ADDRESS,
|
||||
};
|
||||
|
||||
enum efi_locate_search_type : uint32_t {
|
||||
EFI_LOCATE_ALL_HANDLES,
|
||||
EFI_LOCATE_BY_REGISTER_NOTIFY,
|
||||
EFI_LOCATE_BY_PROTOCOL,
|
||||
};
|
||||
|
||||
struct efi_memory_descriptor {
|
||||
uint32_t type;
|
||||
uint64_t physical_start;
|
||||
uint64_t virtual_start;
|
||||
uint64_t number_of_pages;
|
||||
uint64_t attribute;
|
||||
};
|
||||
|
||||
struct efi_table_header {
|
||||
uint64_t signature;
|
||||
uint32_t revision;
|
||||
uint32_t header_size;
|
||||
uint32_t crc32;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct efi_simple_text_output_protocol {
|
||||
void* reset;
|
||||
efi_status(EFIAPI* output_string)(struct efi_simple_text_output_protocol* self, const char16* string);
|
||||
};
|
||||
|
||||
struct efi_boot_services {
|
||||
struct efi_table_header hdr;
|
||||
void* raise_tpl;
|
||||
void* restore_tpl;
|
||||
efi_status(EFIAPI* allocate_pages)(
|
||||
enum efi_allocate_type type,
|
||||
enum efi_memory_type memory_type,
|
||||
size_t pages,
|
||||
uint64_t* memory
|
||||
);
|
||||
efi_status(EFIAPI* free_pages)(uint64_t memory, size_t pages);
|
||||
efi_status(EFIAPI* get_memory_map)(
|
||||
size_t* map_size,
|
||||
struct efi_memory_descriptor* map,
|
||||
size_t* map_key,
|
||||
size_t* descriptor_size,
|
||||
uint32_t* descriptor_version
|
||||
);
|
||||
efi_status(EFIAPI* allocate_pool)(enum efi_memory_type pool_type, size_t size, void** buffer);
|
||||
efi_status(EFIAPI* free_pool)(void* buffer);
|
||||
void* create_event;
|
||||
void* set_timer;
|
||||
void* wait_for_event;
|
||||
void* signal_event;
|
||||
void* close_event;
|
||||
void* check_event;
|
||||
void* install_protocol_interface;
|
||||
void* reinstall_protocol_interface;
|
||||
void* uninstall_protocol_interface;
|
||||
efi_status(EFIAPI* handle_protocol)(efi_handle handle, const struct efi_guid* protocol, void** interface);
|
||||
void* reserved;
|
||||
void* register_protocol_notify;
|
||||
void* locate_handle;
|
||||
void* locate_device_path;
|
||||
void* install_configuration_table;
|
||||
void* load_image;
|
||||
void* start_image;
|
||||
void* exit;
|
||||
void* unload_image;
|
||||
efi_status(EFIAPI* exit_boot_services)(efi_handle image_handle, size_t map_key);
|
||||
void* get_next_monotonic_count;
|
||||
void* stall;
|
||||
void* set_watchdog_timer;
|
||||
void* connect_controller;
|
||||
void* disconnect_controller;
|
||||
void* open_protocol;
|
||||
void* close_protocol;
|
||||
void* open_protocol_information;
|
||||
void* protocols_per_handle;
|
||||
efi_status(EFIAPI* locate_handle_buffer)(
|
||||
enum efi_locate_search_type search_type,
|
||||
const struct efi_guid* protocol,
|
||||
void* search_key,
|
||||
size_t* no_handles,
|
||||
efi_handle** buffer
|
||||
);
|
||||
efi_status(EFIAPI* locate_protocol)(const struct efi_guid* protocol, void* registration, void** interface);
|
||||
};
|
||||
|
||||
struct efi_configuration_table {
|
||||
struct efi_guid vendor_guid;
|
||||
void* vendor_table;
|
||||
};
|
||||
|
||||
struct efi_system_table {
|
||||
struct efi_table_header hdr;
|
||||
char16* firmware_vendor;
|
||||
uint32_t firmware_revision;
|
||||
efi_handle console_in_handle;
|
||||
void* con_in;
|
||||
efi_handle console_out_handle;
|
||||
struct efi_simple_text_output_protocol* con_out;
|
||||
efi_handle standard_error_handle;
|
||||
void* std_err;
|
||||
void* runtime_services;
|
||||
struct efi_boot_services* boot_services;
|
||||
size_t number_of_table_entries;
|
||||
struct efi_configuration_table* configuration_table;
|
||||
};
|
||||
|
||||
struct efi_loaded_image_protocol {
|
||||
uint32_t revision;
|
||||
efi_handle parent_handle;
|
||||
struct efi_system_table* system_table;
|
||||
efi_handle device_handle;
|
||||
void* file_path;
|
||||
void* reserved;
|
||||
uint32_t load_options_size;
|
||||
void* load_options;
|
||||
void* image_base;
|
||||
uint64_t image_size;
|
||||
enum efi_memory_type image_code_type;
|
||||
enum efi_memory_type image_data_type;
|
||||
void* unload;
|
||||
};
|
||||
|
||||
constexpr uint64_t EFI_FILE_MODE_READ = 1;
|
||||
|
||||
struct efi_file_protocol {
|
||||
uint64_t revision;
|
||||
efi_status(EFIAPI* open)(
|
||||
struct efi_file_protocol* self,
|
||||
struct efi_file_protocol** new_handle,
|
||||
const char16* file_name,
|
||||
uint64_t open_mode,
|
||||
uint64_t attributes
|
||||
);
|
||||
efi_status(EFIAPI* close)(struct efi_file_protocol* self);
|
||||
void* delete;
|
||||
efi_status(EFIAPI* read)(struct efi_file_protocol* self, size_t* buffer_size, void* buffer);
|
||||
void* write;
|
||||
efi_status(EFIAPI* get_position)(struct efi_file_protocol* self, uint64_t* position);
|
||||
efi_status(EFIAPI* set_position)(struct efi_file_protocol* self, uint64_t position);
|
||||
};
|
||||
|
||||
struct efi_simple_file_system_protocol {
|
||||
uint64_t revision;
|
||||
efi_status(EFIAPI* open_volume)(struct efi_simple_file_system_protocol* self, struct efi_file_protocol** root);
|
||||
};
|
||||
|
||||
enum efi_pixel_format : uint32_t {
|
||||
EFI_PIXEL_RGBX_8,
|
||||
EFI_PIXEL_BGRX_8,
|
||||
EFI_PIXEL_BIT_MASK,
|
||||
EFI_PIXEL_BLT_ONLY,
|
||||
};
|
||||
|
||||
struct efi_pixel_bitmask {
|
||||
uint32_t red;
|
||||
uint32_t green;
|
||||
uint32_t blue;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct efi_gop_mode_info {
|
||||
uint32_t version;
|
||||
uint32_t horizontal_resolution;
|
||||
uint32_t vertical_resolution;
|
||||
enum efi_pixel_format pixel_format;
|
||||
struct efi_pixel_bitmask pixel_information;
|
||||
uint32_t pixels_per_scan_line;
|
||||
};
|
||||
|
||||
struct efi_gop_mode {
|
||||
uint32_t max_mode;
|
||||
uint32_t mode;
|
||||
struct efi_gop_mode_info* info;
|
||||
size_t size_of_info;
|
||||
uint64_t frame_buffer_base;
|
||||
size_t frame_buffer_size;
|
||||
};
|
||||
|
||||
struct efi_graphics_output_protocol {
|
||||
void* query_mode;
|
||||
void* set_mode;
|
||||
void* blt;
|
||||
struct efi_gop_mode* mode;
|
||||
};
|
||||
|
||||
static const struct efi_guid EFI_LOADED_IMAGE_PROTOCOL_GUID =
|
||||
{0x5b1b'31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
|
||||
static const struct efi_guid EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID =
|
||||
{0x964e'5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
|
||||
static const struct efi_guid EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID =
|
||||
{0x9042'a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}};
|
||||
static const struct efi_guid EFI_ACPI_20_TABLE_GUID =
|
||||
{0x8868'e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81}};
|
||||
static const struct efi_guid EFI_ACPI_10_TABLE_GUID =
|
||||
{0xeb9d'2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}};
|
||||
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "efi_arch.h"
|
||||
#include "pe.h"
|
||||
|
||||
extern const char __pe_code_size[];
|
||||
extern const char __pe_data_sizes[];
|
||||
extern const char __pe_entry[];
|
||||
extern const char __pe_image_sizes[];
|
||||
extern const char __pe_base_relocs[];
|
||||
extern const char __pe_text_virtual[];
|
||||
extern const char __pe_text_raw[];
|
||||
extern const char __pe_reloc_virtual[];
|
||||
extern const char __pe_reloc_raw[];
|
||||
extern const char __pe_rodata_virtual[];
|
||||
extern const char __pe_rodata_raw[];
|
||||
extern const char __pe_data_virtual[];
|
||||
extern const char __pe_data_raw[];
|
||||
|
||||
struct efistub_pe_header {
|
||||
struct pe_dos_header dos;
|
||||
uint32_t signature;
|
||||
struct pe_coff_header coff;
|
||||
struct pe_optional_header64 optional;
|
||||
struct pe_section_header sections[4];
|
||||
};
|
||||
|
||||
static_assert(offsetof(struct efistub_pe_header, optional) % 8 == 0);
|
||||
static_assert(offsetof(struct efistub_pe_header, sections) % 8 == 0);
|
||||
|
||||
[[__used, __section(".header")]]
|
||||
const struct efistub_pe_header efistub_pe_header = {
|
||||
.dos =
|
||||
{
|
||||
.e_magic = PE_DOS_MAGIC,
|
||||
.e_lfanew = offsetof(struct efistub_pe_header, signature),
|
||||
},
|
||||
.signature = PE_SIGNATURE,
|
||||
.coff =
|
||||
{
|
||||
.machine = EFI_ARCH_PE_MACHINE,
|
||||
.number_of_sections = 4,
|
||||
.size_of_optional_header = sizeof(struct pe_optional_header64),
|
||||
.characteristics = PE_FILE_EXECUTABLE_IMAGE | PE_FILE_LINE_NUMS_STRIPPED | PE_FILE_DEBUG_STRIPPED,
|
||||
},
|
||||
.optional =
|
||||
{
|
||||
.magic_and_code_size = (uintptr_t)__pe_code_size + PE_OPTIONAL_MAGIC_PE32_PLUS,
|
||||
.data_sizes = (uintptr_t)__pe_data_sizes,
|
||||
.entry_and_code_base = (uintptr_t)__pe_entry,
|
||||
.section_alignment = 0x1000,
|
||||
.file_alignment = 0x1000,
|
||||
.image_and_header_sizes = (uintptr_t)__pe_image_sizes,
|
||||
.subsystem = PE_SUBSYSTEM_EFI_APPLICATION,
|
||||
.dll_characteristics = PE_DLL_NX_COMPAT,
|
||||
.number_of_rva_and_sizes = PE_NUM_DATA_DIRECTORIES,
|
||||
.data_directories =
|
||||
{
|
||||
[PE_DIRECTORY_BASE_RELOC] = {.layout = (uintptr_t)__pe_base_relocs},
|
||||
},
|
||||
},
|
||||
.sections = {
|
||||
{
|
||||
.name = ".text",
|
||||
.virtual_layout = (uintptr_t)__pe_text_virtual,
|
||||
.raw_layout = (uintptr_t)__pe_text_raw,
|
||||
.characteristics = PE_SCN_CNT_CODE | PE_SCN_MEM_EXECUTE | PE_SCN_MEM_READ,
|
||||
},
|
||||
{
|
||||
.name = ".reloc",
|
||||
.virtual_layout = (uintptr_t)__pe_reloc_virtual,
|
||||
.raw_layout = (uintptr_t)__pe_reloc_raw,
|
||||
.characteristics = PE_SCN_CNT_INITIALIZED_DATA | PE_SCN_MEM_DISCARDABLE | PE_SCN_MEM_READ,
|
||||
},
|
||||
{
|
||||
.name = ".rdata",
|
||||
.virtual_layout = (uintptr_t)__pe_rodata_virtual,
|
||||
.raw_layout = (uintptr_t)__pe_rodata_raw,
|
||||
.characteristics = PE_SCN_CNT_INITIALIZED_DATA | PE_SCN_MEM_READ,
|
||||
},
|
||||
{
|
||||
.name = ".data",
|
||||
.virtual_layout = (uintptr_t)__pe_data_virtual,
|
||||
.raw_layout = (uintptr_t)__pe_data_raw,
|
||||
.characteristics = PE_SCN_CNT_INITIALIZED_DATA | PE_SCN_MEM_READ | PE_SCN_MEM_WRITE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
[[__used, __section(".reloc")]]
|
||||
const struct pe_base_reloc_block efistub_base_relocs = {
|
||||
.page_rva = 0,
|
||||
.block_size = sizeof(struct pe_base_reloc_block),
|
||||
};
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <string.h>
|
||||
#include "stub.h"
|
||||
|
||||
constexpr size_t MEM_MAP_SLACK = 64;
|
||||
|
||||
struct efi_boot_services* bs;
|
||||
|
||||
bool loader_alloc(enum phys_mem_usage usage, size_t size, uint64_t* out) {
|
||||
enum efi_memory_type efi_type = EFI_LOADER_DATA;
|
||||
if (usage == PHYS_STATIC)
|
||||
efi_type = MEM_STATIC;
|
||||
else if (usage == PHYS_RECLAIMABLE)
|
||||
efi_type = MEM_RECLAIMABLE;
|
||||
|
||||
const size_t pages = ALIGN_UP(size, PAGE_SIZE) / PAGE_SIZE;
|
||||
if (bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, efi_type, pages, out))
|
||||
return false;
|
||||
memset((void*)*out, 0, pages * PAGE_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void* loader_ptr(uint64_t phys) {
|
||||
return (void*)phys;
|
||||
}
|
||||
|
||||
static efi_status open_file_on(efi_handle handle, const char16* path, struct efi_file_protocol** out) {
|
||||
struct efi_simple_file_system_protocol* fs;
|
||||
efi_status status = bs->handle_protocol(handle, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, (void**)&fs);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
struct efi_file_protocol* root;
|
||||
status = fs->open_volume(fs, &root);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = root->open(root, out, path, EFI_FILE_MODE_READ, 0);
|
||||
if (status && path[0] == '\\')
|
||||
status = root->open(root, out, path + 1, EFI_FILE_MODE_READ, 0);
|
||||
root->close(root);
|
||||
return status;
|
||||
}
|
||||
|
||||
static efi_status read_file(struct efi_file_protocol* file, enum phys_mem_usage usage, uint64_t* base, uint64_t* size) {
|
||||
uint64_t total;
|
||||
uint64_t done = 0;
|
||||
efi_status status = file->set_position(file, UINT64_MAX);
|
||||
if (!status)
|
||||
status = file->get_position(file, &total);
|
||||
if (!status)
|
||||
status = file->set_position(file, 0);
|
||||
if (!status && !loader_alloc(usage, total ? total : 1, base))
|
||||
status = EFI_OUT_OF_RESOURCES;
|
||||
while (!status && done < total) {
|
||||
size_t chunk = total - done;
|
||||
status = file->read(file, &chunk, (void*)(*base + done));
|
||||
if (!chunk)
|
||||
break;
|
||||
done += chunk;
|
||||
}
|
||||
file->close(file);
|
||||
*size = done;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void color_from_mask(uint32_t mask, struct boot_color* out) {
|
||||
if (!mask)
|
||||
return;
|
||||
out->shift = __builtin_ctz(mask);
|
||||
out->size = __builtin_ctzll(~(uint64_t)(mask >> out->shift));
|
||||
}
|
||||
|
||||
static enum phys_mem_usage convert_mem_type(uint32_t type) {
|
||||
switch (type) {
|
||||
case EFI_LOADER_CODE:
|
||||
case EFI_LOADER_DATA:
|
||||
case EFI_BOOT_SERVICES_CODE:
|
||||
case EFI_BOOT_SERVICES_DATA:
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
return PHYS_USABLE;
|
||||
case EFI_ACPI_RECLAIM_MEMORY:
|
||||
case MEM_RECLAIMABLE:
|
||||
return PHYS_RECLAIMABLE;
|
||||
case MEM_STATIC:
|
||||
return PHYS_STATIC;
|
||||
default:
|
||||
return PHYS_RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
efi_status efi_main(efi_handle image_handle, struct efi_system_table* st) {
|
||||
bs = st->boot_services;
|
||||
|
||||
// Check if we support this configuration.
|
||||
if (!arch_check_cpu())
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
struct efi_loaded_image_protocol* image;
|
||||
efi_status status = bs->handle_protocol(image_handle, &EFI_LOADED_IMAGE_PROTOCOL_GUID, (void**)&image);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
struct kernel_image kernel;
|
||||
if (!loader_load_kernel(&kernel, "EFISTUB"))
|
||||
return EFI_LOAD_ERROR;
|
||||
struct boot_info* info = kernel.info;
|
||||
|
||||
// Reserve room for the memory map.
|
||||
size_t map_size = 0;
|
||||
size_t map_key;
|
||||
size_t desc_size;
|
||||
uint32_t desc_version;
|
||||
status = bs->get_memory_map(&map_size, nullptr, &map_key, &desc_size, &desc_version);
|
||||
if (status != EFI_BUFFER_TOO_SMALL)
|
||||
return status;
|
||||
|
||||
const size_t map_capacity = map_size + MEM_MAP_SLACK * desc_size;
|
||||
uint64_t map;
|
||||
if (!loader_alloc(PHYS_USABLE, map_capacity, &map))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
uint64_t mem;
|
||||
if (!loader_alloc(PHYS_USABLE, map_capacity / desc_size * sizeof(struct phys_mem), &mem))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
// Use the load options as the command line.
|
||||
const char16* opts = image->load_options;
|
||||
const size_t opts_len = image->load_options_size / sizeof(char16);
|
||||
size_t cmdline_len = 0;
|
||||
for (size_t i = 0; opts && i < opts_len && opts[i]; i++) {
|
||||
if (opts[i] < 0x20 || opts[i] > 0x7e || cmdline_len + 1 >= sizeof(info->cmdline)) {
|
||||
cmdline_len = 0;
|
||||
break;
|
||||
}
|
||||
info->cmdline[cmdline_len++] = (char)opts[i];
|
||||
}
|
||||
info->cmdline[cmdline_len] = 0;
|
||||
|
||||
// Without load options, fall back to a command line file on the boot device.
|
||||
struct efi_file_protocol* cmdline_file;
|
||||
if (!cmdline_len && !open_file_on(image->device_handle, u"\\EFI\\zinnia\\cmdline", &cmdline_file)) {
|
||||
uint64_t base;
|
||||
uint64_t size;
|
||||
status = read_file(cmdline_file, PHYS_USABLE, &base, &size);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
const char* text = (const char*)base;
|
||||
while (cmdline_len < size && text[cmdline_len] && text[cmdline_len] != '\n' && text[cmdline_len] != '\r') {
|
||||
if (text[cmdline_len] < 0x20 || text[cmdline_len] > 0x7e)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
if (cmdline_len + 1 >= sizeof(info->cmdline))
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
info->cmdline[cmdline_len] = text[cmdline_len];
|
||||
cmdline_len++;
|
||||
}
|
||||
while (cmdline_len && info->cmdline[cmdline_len - 1] == ' ')
|
||||
cmdline_len--;
|
||||
info->cmdline[cmdline_len] = 0;
|
||||
}
|
||||
|
||||
// Load every initrd= file.
|
||||
const char* cmdline = info->cmdline;
|
||||
for (size_t i = 0; cmdline[i];) {
|
||||
if (cmdline[i] == ' ') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t start = i;
|
||||
bool quoted = false;
|
||||
while (cmdline[i] && (quoted || cmdline[i] != ' ')) {
|
||||
if (cmdline[i] == '"')
|
||||
quoted = !quoted;
|
||||
i++;
|
||||
}
|
||||
const char* token = cmdline + start;
|
||||
const size_t len = i - start;
|
||||
if (len <= 7 || memcmp(token, "initrd=", 7))
|
||||
continue;
|
||||
|
||||
const size_t path_len = len - 7;
|
||||
if (path_len >= BOOT_PATH_MAX)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
char path[BOOT_PATH_MAX];
|
||||
char16 wide[BOOT_PATH_MAX];
|
||||
for (size_t j = 0; j < path_len; j++) {
|
||||
path[j] = token[7 + j];
|
||||
wide[j] = path[j] == '/' ? '\\' : (char16)path[j];
|
||||
}
|
||||
path[path_len] = 0;
|
||||
wide[path_len] = 0;
|
||||
|
||||
// Try the boot device first, then all other file systems.
|
||||
struct efi_file_protocol* file;
|
||||
status = open_file_on(image->device_handle, wide, &file);
|
||||
size_t num_handles;
|
||||
efi_handle* handles;
|
||||
if (status &&
|
||||
!bs->locate_handle_buffer(
|
||||
EFI_LOCATE_BY_PROTOCOL,
|
||||
&EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
|
||||
nullptr,
|
||||
&num_handles,
|
||||
&handles
|
||||
)) {
|
||||
for (size_t j = 0; j < num_handles && status; j++) {
|
||||
if (handles[j] != image->device_handle)
|
||||
status = open_file_on(handles[j], wide, &file);
|
||||
}
|
||||
bs->free_pool(handles);
|
||||
}
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
uint64_t base;
|
||||
uint64_t size;
|
||||
status = read_file(file, PHYS_STATIC, &base, &size);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (!loader_add_file(&kernel, path, base, size))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
// Framebuffer
|
||||
struct efi_graphics_output_protocol* gop;
|
||||
if (!bs->locate_protocol(&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, nullptr, (void**)&gop) && gop && gop->mode &&
|
||||
gop->mode->info) {
|
||||
const struct efi_gop_mode_info* mode = gop->mode->info;
|
||||
struct boot_framebuffer* fb = &info->fb;
|
||||
switch (mode->pixel_format) {
|
||||
case EFI_PIXEL_RGBX_8:
|
||||
fb->bytes_per_pixel = 4;
|
||||
fb->red = (struct boot_color){0, 8};
|
||||
fb->green = (struct boot_color){8, 8};
|
||||
fb->blue = (struct boot_color){16, 8};
|
||||
break;
|
||||
case EFI_PIXEL_BGRX_8:
|
||||
fb->bytes_per_pixel = 4;
|
||||
fb->red = (struct boot_color){16, 8};
|
||||
fb->green = (struct boot_color){8, 8};
|
||||
fb->blue = (struct boot_color){0, 8};
|
||||
break;
|
||||
case EFI_PIXEL_BIT_MASK: {
|
||||
const struct efi_pixel_bitmask* m = &mode->pixel_information;
|
||||
const uint32_t all = m->red | m->green | m->blue | m->reserved;
|
||||
if (!all)
|
||||
break;
|
||||
fb->bytes_per_pixel = (32 - __builtin_clz(all) + 7) / 8;
|
||||
color_from_mask(m->red, &fb->red);
|
||||
color_from_mask(m->green, &fb->green);
|
||||
color_from_mask(m->blue, &fb->blue);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fb->bytes_per_pixel) {
|
||||
fb->base = gop->mode->frame_buffer_base;
|
||||
fb->width = mode->horizontal_resolution;
|
||||
fb->height = mode->vertical_resolution;
|
||||
fb->pitch = mode->pixels_per_scan_line * fb->bytes_per_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the RSDP.
|
||||
for (size_t i = 0; i < st->number_of_table_entries; i++) {
|
||||
const struct efi_configuration_table* table = &st->configuration_table[i];
|
||||
if (!memcmp(&table->vendor_guid, &EFI_ACPI_20_TABLE_GUID, sizeof(struct efi_guid))) {
|
||||
info->rsdp = (uint64_t)table->vendor_table;
|
||||
break;
|
||||
}
|
||||
if (!memcmp(&table->vendor_guid, &EFI_ACPI_10_TABLE_GUID, sizeof(struct efi_guid)))
|
||||
info->rsdp = (uint64_t)table->vendor_table;
|
||||
}
|
||||
|
||||
// Map everything up to the highest non-reserved address.
|
||||
map_size = map_capacity;
|
||||
status = bs->get_memory_map(&map_size, (struct efi_memory_descriptor*)map, &map_key, &desc_size, &desc_version);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
uint64_t top = 0;
|
||||
for (size_t off = 0; off + desc_size <= map_size; off += desc_size) {
|
||||
const struct efi_memory_descriptor* desc = (const struct efi_memory_descriptor*)(map + off);
|
||||
if (convert_mem_type(desc->type) != PHYS_RESERVED)
|
||||
top = MAX(top, desc->physical_start + desc->number_of_pages * PAGE_SIZE);
|
||||
}
|
||||
|
||||
uint64_t root;
|
||||
if (!loader_page_tables(&kernel, top, (uint64_t)arch_enter_kernel, &root))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
// Exit boot services.
|
||||
for (size_t attempt = 0;; attempt++) {
|
||||
map_size = map_capacity;
|
||||
status = bs->get_memory_map(&map_size, (struct efi_memory_descriptor*)map, &map_key, &desc_size, &desc_version);
|
||||
if (status)
|
||||
return status;
|
||||
status = bs->exit_boot_services(image_handle, map_key);
|
||||
if (!status)
|
||||
break;
|
||||
if (attempt == 8)
|
||||
return status;
|
||||
}
|
||||
|
||||
// Hand the final memory map to the kernel.
|
||||
struct phys_mem* mem_map = (struct phys_mem*)mem;
|
||||
size_t num_mem = 0;
|
||||
for (size_t off = 0; off + desc_size <= map_size; off += desc_size) {
|
||||
const struct efi_memory_descriptor* desc = (const struct efi_memory_descriptor*)(map + off);
|
||||
mem_map[num_mem++] = (struct phys_mem){
|
||||
.address = desc->physical_start,
|
||||
.length = desc->number_of_pages * PAGE_SIZE,
|
||||
.usage = convert_mem_type(desc->type),
|
||||
};
|
||||
}
|
||||
loader_set_mem_map(&kernel, mem_map, num_mem);
|
||||
|
||||
arch_enter_kernel(root, kernel.entry);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
efistub_sources = files(
|
||||
'header.c',
|
||||
'main.c',
|
||||
'reloc.c',
|
||||
)
|
||||
|
||||
efistub_lds = meson.current_source_dir() / 'stub.ld'
|
||||
|
||||
subdir(target_machine.cpu_family())
|
||||
|
||||
efistub_elf = executable(
|
||||
'efistub.elf',
|
||||
sources: [loader_sources, efistub_sources, loader_kernel],
|
||||
include_directories: [
|
||||
common_includes,
|
||||
loader_includes,
|
||||
include_directories('.', target_machine.cpu_family()),
|
||||
],
|
||||
c_args: loader_c_args,
|
||||
link_args: [
|
||||
'-nostdlib',
|
||||
'-Wl,-T,' + efistub_lds,
|
||||
'-Wl,--no-dynamic-linker',
|
||||
'-Wl,-z,text',
|
||||
'-Wl,-z,norelro',
|
||||
'-Wl,-z,noexecstack',
|
||||
'-Wl,-z,max-page-size=0x1000',
|
||||
'-Wl,--build-id=none',
|
||||
],
|
||||
link_depends: efistub_lds,
|
||||
pie: true,
|
||||
)
|
||||
|
||||
zinnia_efi = custom_target(
|
||||
'zinnia.efi',
|
||||
input: efistub_elf,
|
||||
output: 'zinnia.efi',
|
||||
command: [objcopy, '-O', 'binary', '@INPUT@', '@OUTPUT@'],
|
||||
build_by_default: true,
|
||||
install: true,
|
||||
install_dir: zinnia_bootdir,
|
||||
)
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr uint16_t PE_DOS_MAGIC = 0x5a4d;
|
||||
constexpr uint32_t PE_SIGNATURE = 0x0000'4550;
|
||||
constexpr uint16_t PE_OPTIONAL_MAGIC_PE32_PLUS = 0x020b;
|
||||
constexpr uint16_t PE_SUBSYSTEM_EFI_APPLICATION = 10;
|
||||
constexpr size_t PE_NUM_DATA_DIRECTORIES = 16;
|
||||
constexpr size_t PE_DIRECTORY_BASE_RELOC = 5;
|
||||
|
||||
enum pe_file_characteristics : uint16_t {
|
||||
PE_FILE_EXECUTABLE_IMAGE = 0x0002,
|
||||
PE_FILE_LINE_NUMS_STRIPPED = 0x0004,
|
||||
PE_FILE_DEBUG_STRIPPED = 0x0200,
|
||||
};
|
||||
|
||||
enum pe_dll_characteristics : uint16_t {
|
||||
PE_DLL_NX_COMPAT = 0x0100,
|
||||
};
|
||||
|
||||
enum pe_section_characteristics : uint32_t {
|
||||
PE_SCN_CNT_CODE = 0x0000'0020,
|
||||
PE_SCN_CNT_INITIALIZED_DATA = 0x0000'0040,
|
||||
PE_SCN_MEM_DISCARDABLE = 0x0200'0000,
|
||||
PE_SCN_MEM_EXECUTE = 0x2000'0000,
|
||||
PE_SCN_MEM_READ = 0x4000'0000,
|
||||
PE_SCN_MEM_WRITE = 0x8000'0000,
|
||||
};
|
||||
|
||||
struct pe_dos_header {
|
||||
uint16_t e_magic;
|
||||
uint16_t e_cblp;
|
||||
uint16_t e_cp;
|
||||
uint16_t e_crlc;
|
||||
uint16_t e_cparhdr;
|
||||
uint16_t e_minalloc;
|
||||
uint16_t e_maxalloc;
|
||||
uint16_t e_ss;
|
||||
uint16_t e_sp;
|
||||
uint16_t e_csum;
|
||||
uint16_t e_ip;
|
||||
uint16_t e_cs;
|
||||
uint16_t e_lfarlc;
|
||||
uint16_t e_ovno;
|
||||
uint16_t e_res[4];
|
||||
uint16_t e_oemid;
|
||||
uint16_t e_oeminfo;
|
||||
uint16_t e_res2[10];
|
||||
uint32_t e_lfanew;
|
||||
};
|
||||
|
||||
struct pe_coff_header {
|
||||
uint16_t machine;
|
||||
uint16_t number_of_sections;
|
||||
uint32_t time_date_stamp;
|
||||
uint32_t pointer_to_symbol_table;
|
||||
uint32_t number_of_symbols;
|
||||
uint16_t size_of_optional_header;
|
||||
uint16_t characteristics;
|
||||
};
|
||||
|
||||
struct pe_data_directory {
|
||||
union {
|
||||
struct {
|
||||
uint32_t virtual_address;
|
||||
uint32_t size;
|
||||
};
|
||||
uint64_t layout;
|
||||
};
|
||||
};
|
||||
|
||||
struct pe_optional_header64 {
|
||||
union {
|
||||
struct {
|
||||
uint16_t magic;
|
||||
uint8_t major_linker_version;
|
||||
uint8_t minor_linker_version;
|
||||
uint32_t size_of_code;
|
||||
};
|
||||
uint64_t magic_and_code_size;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint32_t size_of_initialized_data;
|
||||
uint32_t size_of_uninitialized_data;
|
||||
};
|
||||
uint64_t data_sizes;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint32_t address_of_entry_point;
|
||||
uint32_t base_of_code;
|
||||
};
|
||||
uint64_t entry_and_code_base;
|
||||
};
|
||||
uint64_t image_base;
|
||||
uint32_t section_alignment;
|
||||
uint32_t file_alignment;
|
||||
uint16_t major_operating_system_version;
|
||||
uint16_t minor_operating_system_version;
|
||||
uint16_t major_image_version;
|
||||
uint16_t minor_image_version;
|
||||
uint16_t major_subsystem_version;
|
||||
uint16_t minor_subsystem_version;
|
||||
uint32_t win32_version_value;
|
||||
union {
|
||||
struct {
|
||||
uint32_t size_of_image;
|
||||
uint32_t size_of_headers;
|
||||
};
|
||||
uint64_t image_and_header_sizes;
|
||||
};
|
||||
uint32_t checksum;
|
||||
uint16_t subsystem;
|
||||
uint16_t dll_characteristics;
|
||||
uint64_t size_of_stack_reserve;
|
||||
uint64_t size_of_stack_commit;
|
||||
uint64_t size_of_heap_reserve;
|
||||
uint64_t size_of_heap_commit;
|
||||
uint32_t loader_flags;
|
||||
uint32_t number_of_rva_and_sizes;
|
||||
struct pe_data_directory data_directories[PE_NUM_DATA_DIRECTORIES];
|
||||
};
|
||||
|
||||
struct pe_section_header {
|
||||
char name[8];
|
||||
union {
|
||||
struct {
|
||||
uint32_t virtual_size;
|
||||
uint32_t virtual_address;
|
||||
};
|
||||
uint64_t virtual_layout;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint32_t size_of_raw_data;
|
||||
uint32_t pointer_to_raw_data;
|
||||
};
|
||||
uint64_t raw_layout;
|
||||
};
|
||||
uint32_t pointer_to_relocations;
|
||||
uint32_t pointer_to_line_numbers;
|
||||
uint16_t number_of_relocations;
|
||||
uint16_t number_of_line_numbers;
|
||||
uint32_t characteristics;
|
||||
};
|
||||
|
||||
struct pe_base_reloc_block {
|
||||
uint32_t page_rva;
|
||||
uint32_t block_size;
|
||||
uint16_t entries[2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct pe_dos_header) == 64);
|
||||
static_assert(sizeof(struct pe_coff_header) == 20);
|
||||
static_assert(sizeof(struct pe_optional_header64) == 240);
|
||||
static_assert(offsetof(struct pe_optional_header64, size_of_image) == 56);
|
||||
static_assert(offsetof(struct pe_optional_header64, data_directories) == 112);
|
||||
static_assert(sizeof(struct pe_section_header) == 40);
|
||||
static_assert(sizeof(struct pe_base_reloc_block) == 12);
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <bits/elf.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "efi.h"
|
||||
|
||||
efi_status efistub_relocate(uintptr_t base, const elf64_dyn_t* dynamic) {
|
||||
uintptr_t rela = 0;
|
||||
size_t rela_size = 0;
|
||||
size_t rela_ent = sizeof(elf64_rela_t);
|
||||
uintptr_t relr = 0;
|
||||
size_t relr_size = 0;
|
||||
|
||||
for (const elf64_dyn_t* dyn = dynamic; dyn->d_tag != DT_NULL; dyn++) {
|
||||
switch (dyn->d_tag) {
|
||||
case DT_RELA:
|
||||
rela = base + dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
rela_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
rela_ent = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELR:
|
||||
relr = base + dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELRSZ:
|
||||
relr_size = dyn->d_un.d_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t off = 0; rela && off + rela_ent <= rela_size; off += rela_ent) {
|
||||
const elf64_rela_t* r = (const elf64_rela_t*)(rela + off);
|
||||
const uint32_t type = ELF64_R_TYPE(r->r_info);
|
||||
if (type == R_NONE)
|
||||
continue;
|
||||
if (type != R_RELATIVE)
|
||||
return EFI_LOAD_ERROR;
|
||||
*(uint64_t*)(base + r->r_offset) = base + r->r_addend;
|
||||
}
|
||||
|
||||
uint64_t* where = nullptr;
|
||||
for (size_t off = 0; relr && off + sizeof(uint64_t) <= relr_size; off += sizeof(uint64_t)) {
|
||||
uint64_t entry = *(const uint64_t*)(relr + off);
|
||||
if (!(entry & 1)) {
|
||||
where = (uint64_t*)(base + entry);
|
||||
*where++ += base;
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 0; (entry >>= 1) != 0; i++) {
|
||||
if (entry & 1)
|
||||
where[i] += base;
|
||||
}
|
||||
where += 63;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "efi.h"
|
||||
#include "loader.h"
|
||||
|
||||
constexpr enum efi_memory_type MEM_STATIC = (enum efi_memory_type)0x8000'0000;
|
||||
constexpr enum efi_memory_type MEM_RECLAIMABLE = (enum efi_memory_type)0x8000'0001;
|
||||
|
||||
extern struct efi_boot_services* bs;
|
||||
@@ -0,0 +1,100 @@
|
||||
ENTRY(_start)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
|
||||
.header : {
|
||||
__image_base = .;
|
||||
KEEP(*(.header))
|
||||
} :text
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.text : {
|
||||
__text_start = .;
|
||||
*(.text .text.*)
|
||||
. = ALIGN(0x1000);
|
||||
__text_end = .;
|
||||
} :text
|
||||
|
||||
.reloc : {
|
||||
__reloc_start = .;
|
||||
KEEP(*(.reloc))
|
||||
__reloc_data_end = .;
|
||||
. = ALIGN(0x1000);
|
||||
__reloc_end = .;
|
||||
} :rodata
|
||||
|
||||
.rodata : {
|
||||
__rodata_start = .;
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(16);
|
||||
__kernel_start = .;
|
||||
KEEP(*(.kernel))
|
||||
__kernel_end = .;
|
||||
} :rodata
|
||||
|
||||
.hash : { *(.hash) } :rodata
|
||||
.gnu.hash : { *(.gnu.hash) } :rodata
|
||||
.dynsym : { *(.dynsym) } :rodata
|
||||
.dynstr : { *(.dynstr) } :rodata
|
||||
.rela.dyn : { *(.rela.*) } :rodata
|
||||
.relr.dyn : { *(.relr.dyn) } :rodata
|
||||
|
||||
.rodata.end : {
|
||||
. = ALIGN(0x1000);
|
||||
__rodata_end = .;
|
||||
} :rodata
|
||||
|
||||
.dynamic : {
|
||||
__data_start = .;
|
||||
*(.dynamic)
|
||||
} :data :dynamic
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
*(.got .got.*)
|
||||
. = ALIGN(0x1000);
|
||||
__data_raw_end = .;
|
||||
} :data
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
__bss_start = .;
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(0x1000);
|
||||
__bss_end = .;
|
||||
} :data
|
||||
|
||||
__image_end = .;
|
||||
|
||||
__pe_code_size = ABSOLUTE((__text_end - __text_start) << 32);
|
||||
__pe_data_sizes = ABSOLUTE(((__reloc_end - __reloc_start) + (__rodata_end - __rodata_start) +
|
||||
(__data_raw_end - __data_start)) | ((__bss_end - __bss_start) << 32));
|
||||
__pe_entry = ABSOLUTE(_start | (__text_start << 32));
|
||||
__pe_image_sizes = ABSOLUTE(__image_end | (__text_start << 32));
|
||||
__pe_base_relocs = ABSOLUTE(__reloc_start | ((__reloc_data_end - __reloc_start) << 32));
|
||||
__pe_text_virtual = ABSOLUTE((__text_end - __text_start) | (__text_start << 32));
|
||||
__pe_text_raw = ABSOLUTE((__text_end - __text_start) | (__text_start << 32));
|
||||
__pe_reloc_virtual = ABSOLUTE((__reloc_data_end - __reloc_start) | (__reloc_start << 32));
|
||||
__pe_reloc_raw = ABSOLUTE((__reloc_end - __reloc_start) | (__reloc_start << 32));
|
||||
__pe_rodata_virtual = ABSOLUTE((__rodata_end - __rodata_start) | (__rodata_start << 32));
|
||||
__pe_rodata_raw = ABSOLUTE((__rodata_end - __rodata_start) | (__rodata_start << 32));
|
||||
__pe_data_virtual = ABSOLUTE((__bss_end - __data_start) | (__data_start << 32));
|
||||
__pe_data_raw = ABSOLUTE((__data_raw_end - __data_start) | (__data_start << 32));
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.comment*)
|
||||
*(.eh_frame*)
|
||||
*(.note*)
|
||||
*(.interp)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define EFIAPI __attribute__((ms_abi))
|
||||
|
||||
constexpr uint16_t EFI_ARCH_PE_MACHINE = 0x8664;
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
// Align the stack pointer
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
push rdi
|
||||
push rsi
|
||||
push rbx
|
||||
push r12
|
||||
and rsp, -16
|
||||
cld
|
||||
|
||||
mov rbx, rcx
|
||||
mov r12, rdx
|
||||
|
||||
// Clear .bss
|
||||
lea rdi, [rip + __bss_start]
|
||||
lea rcx, [rip + __bss_end]
|
||||
sub rcx, rdi
|
||||
xor eax, eax
|
||||
rep stosb
|
||||
|
||||
// Self-relocate
|
||||
lea rdi, [rip + __image_base]
|
||||
lea rsi, [rip + _DYNAMIC]
|
||||
call efistub_relocate
|
||||
test rax, rax
|
||||
jnz 1f
|
||||
|
||||
mov rdi, rbx
|
||||
mov rsi, r12
|
||||
call efi_main
|
||||
|
||||
1:
|
||||
lea rsp, [rbp - 32]
|
||||
pop r12
|
||||
pop rbx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
ret
|
||||
.size _start, . - _start
|
||||
@@ -0,0 +1 @@
|
||||
efistub_sources += files('entry.S')
|
||||
@@ -7,6 +7,7 @@ PHDRS {
|
||||
dynamic PT_DYNAMIC FLAGS(6);
|
||||
percpu PT_LOAD FLAGS(6);
|
||||
cmdline 0x60000005;
|
||||
boot 0x60000008;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -62,6 +63,10 @@ SECTIONS
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.boot : {
|
||||
KEEP(*(.boot.info))
|
||||
} :data :boot
|
||||
|
||||
.dynsym : {
|
||||
__ld_dynsym_start = .;
|
||||
KEEP(*(.dynsym*))
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/fdt.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <string.h>
|
||||
#include "limine.h"
|
||||
|
||||
[[__initdata_sorted("limine.0")]]
|
||||
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
|
||||
[[__initdata_sorted("limine.1")]]
|
||||
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(6);
|
||||
[[__initdata_sorted("limine.2")]]
|
||||
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
|
||||
|
||||
#define LIMINE_REQUEST(request, tag, rev) \
|
||||
[[__initdata_sorted("limine.1")]] \
|
||||
static volatile struct limine_##request request = { \
|
||||
.id = tag, \
|
||||
.revision = rev, \
|
||||
.response = nullptr, \
|
||||
}
|
||||
|
||||
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
|
||||
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
|
||||
|
||||
[[__initdata]]
|
||||
static struct phys_mem mem[128];
|
||||
[[__initdata]]
|
||||
static struct boot_file files[8];
|
||||
|
||||
[[__init]]
|
||||
void kernel_entry() {
|
||||
kernel_early_init();
|
||||
|
||||
struct boot_info info = {};
|
||||
|
||||
const struct limine_memmap_response* mm_res = memmap_request.response;
|
||||
const struct limine_hhdm_response* hhdm_res = hhdm_request.response;
|
||||
const struct limine_module_response* module_res = module_request.response;
|
||||
const struct limine_executable_address_response* exec_res = executable_address_request.response;
|
||||
const struct limine_executable_file_response* exec_file_res = executable_file_request.response;
|
||||
const struct limine_rsdp_response* rsdp_res = rsdp_request.response;
|
||||
|
||||
assert(mm_res, "memmap_request returned no response!");
|
||||
assert(hhdm_res, "hhdm_request returned no response!");
|
||||
|
||||
info.cmdline = exec_file_res->executable_file->string;
|
||||
info.kernel_file.data = (phys_t)((uintptr_t)exec_file_res->executable_file->address - hhdm_res->offset);
|
||||
info.kernel_file.length = exec_file_res->executable_file->size;
|
||||
|
||||
info.num_mem_maps = MIN(ARRAY_SIZE(mem), mm_res->entry_count);
|
||||
for (size_t i = 0; i < info.num_mem_maps; i++) {
|
||||
mem[i].address = mm_res->entries[i]->base;
|
||||
mem[i].length = mm_res->entries[i]->length;
|
||||
|
||||
switch (mm_res->entries[i]->type) {
|
||||
case LIMINE_MEMMAP_USABLE:
|
||||
mem[i].usage = PHYS_USABLE;
|
||||
break;
|
||||
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||
mem[i].usage = PHYS_RECLAIMABLE;
|
||||
break;
|
||||
case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES:
|
||||
mem[i].usage = PHYS_STATIC;
|
||||
break;
|
||||
default:
|
||||
mem[i].usage = PHYS_RESERVED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (module_res) {
|
||||
info.files = files;
|
||||
info.num_files = MIN(ARRAY_SIZE(files), module_res->module_count);
|
||||
for (size_t i = 0; i < info.num_files; i++) {
|
||||
files[i].data = (phys_t)module_res->modules[i]->address - hhdm_res->offset;
|
||||
files[i].length = module_res->modules[i]->size;
|
||||
strncpy(files[i].path, module_res->modules[i]->path, sizeof(files[i].path) - 1);
|
||||
files[i].path[sizeof(files[i].path) - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
info.mem_map = mem;
|
||||
info.phys_base = (phys_t)exec_res->physical_base;
|
||||
info.virt_base = (uintptr_t)exec_res->virtual_base;
|
||||
info.hhdm_base = (uintptr_t)hhdm_res->offset;
|
||||
|
||||
const struct limine_dtb_response* dtb_res = dtb_request.response;
|
||||
if (dtb_res && dtb_res->dtb_ptr) {
|
||||
info.dtb = (phys_t)((uintptr_t)dtb_res->dtb_ptr - hhdm_res->offset);
|
||||
info.dtb_size = fdt_read_u32((const uint8_t*)dtb_res->dtb_ptr + 4);
|
||||
}
|
||||
|
||||
const struct limine_framebuffer_response* fb_res = framebuffer_request.response;
|
||||
if (fb_res) {
|
||||
// Only use the first framebuffer.
|
||||
if (fb_res->framebuffer_count >= 1) {
|
||||
const struct limine_framebuffer* fb = framebuffer_request.response->framebuffers[0];
|
||||
info.fb.base = (phys_t)((uintptr_t)fb->address - hhdm_res->offset);
|
||||
info.fb.width = fb->width;
|
||||
info.fb.height = fb->height;
|
||||
info.fb.pitch = fb->pitch;
|
||||
info.fb.bytes_per_pixel = fb->bpp / 8;
|
||||
|
||||
info.fb.red.shift = fb->red_mask_shift;
|
||||
info.fb.red.size = fb->red_mask_size;
|
||||
|
||||
info.fb.green.shift = fb->green_mask_shift;
|
||||
info.fb.green.size = fb->green_mask_size;
|
||||
|
||||
info.fb.blue.shift = fb->blue_mask_shift;
|
||||
info.fb.blue.size = fb->blue_mask_size;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ACPI
|
||||
if (rsdp_request.response) {
|
||||
info.rsdp = (phys_t)rsdp_request.response->address;
|
||||
if (info.rsdp >= info.hhdm_base)
|
||||
info.rsdp -= info.hhdm_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
kernel_main(&info);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
ENTRY(loader_main)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xffffffffc0000000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(16);
|
||||
__kernel_start = .;
|
||||
KEEP(*(.kernel))
|
||||
__kernel_end = .;
|
||||
} :rodata
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.data : {
|
||||
KEEP(*(.limine_requests_start))
|
||||
KEEP(*(.limine_requests))
|
||||
KEEP(*(.limine_requests_end))
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.comment*)
|
||||
*(.eh_frame*)
|
||||
*(.note*)
|
||||
*(.interp)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <string.h>
|
||||
#include "limine.h"
|
||||
#include "loader.h"
|
||||
|
||||
[[__used, __section(".limine_requests_start")]]
|
||||
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
|
||||
[[__used, __section(".limine_requests")]]
|
||||
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(6);
|
||||
[[__used, __section(".limine_requests_end")]]
|
||||
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
|
||||
|
||||
#define LIMINE_REQUEST(request, tag, rev) \
|
||||
[[__used, __section(".limine_requests")]] \
|
||||
static volatile struct limine_##request request = { \
|
||||
.id = tag, \
|
||||
.revision = rev, \
|
||||
.response = nullptr, \
|
||||
}
|
||||
|
||||
LIMINE_REQUEST(bootloader_info_request, LIMINE_BOOTLOADER_INFO_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
|
||||
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
|
||||
|
||||
static uint64_t hhdm_offset;
|
||||
static const struct limine_memmap_entry* pool;
|
||||
static uint64_t pool_static_end;
|
||||
static uint64_t pool_reclaimable_start;
|
||||
|
||||
void* loader_ptr(uint64_t phys) {
|
||||
return (void*)(phys + hhdm_offset);
|
||||
}
|
||||
|
||||
static uint64_t to_phys(const void* ptr) {
|
||||
const uint64_t addr = (uint64_t)ptr;
|
||||
return addr >= hhdm_offset ? addr - hhdm_offset : addr;
|
||||
}
|
||||
|
||||
bool loader_alloc(enum phys_mem_usage usage, size_t size, uint64_t* out) {
|
||||
size = ALIGN_UP(size, PAGE_SIZE);
|
||||
if (pool_reclaimable_start - pool_static_end < size)
|
||||
return false;
|
||||
|
||||
if (usage == PHYS_STATIC) {
|
||||
*out = pool_static_end;
|
||||
pool_static_end += size;
|
||||
} else {
|
||||
pool_reclaimable_start -= size;
|
||||
*out = pool_reclaimable_start;
|
||||
}
|
||||
memset(loader_ptr(*out), 0, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum phys_mem_usage convert_mem_type(uint64_t type) {
|
||||
switch (type) {
|
||||
case LIMINE_MEMMAP_USABLE:
|
||||
return PHYS_USABLE;
|
||||
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||
return PHYS_RECLAIMABLE;
|
||||
case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES:
|
||||
return PHYS_STATIC;
|
||||
default:
|
||||
return PHYS_RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_pool(const struct limine_memmap_response* memmap) {
|
||||
for (size_t i = 0; i < memmap->entry_count; i++) {
|
||||
const struct limine_memmap_entry* entry = memmap->entries[i];
|
||||
if (entry->type == LIMINE_MEMMAP_USABLE && (!pool || entry->length > pool->length))
|
||||
pool = entry;
|
||||
}
|
||||
if (!pool)
|
||||
return false;
|
||||
|
||||
pool_static_end = pool->base;
|
||||
pool_reclaimable_start = pool->base + pool->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t mem_map_top(const struct limine_memmap_response* memmap) {
|
||||
uint64_t top = 0;
|
||||
for (size_t i = 0; i < memmap->entry_count; i++) {
|
||||
const struct limine_memmap_entry* entry = memmap->entries[i];
|
||||
if (convert_mem_type(entry->type) != PHYS_RESERVED)
|
||||
top = MAX(top, entry->base + entry->length);
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
static size_t convert_mem_map(const struct limine_memmap_response* memmap, struct phys_mem* out) {
|
||||
size_t n = 0;
|
||||
for (size_t i = 0; i < memmap->entry_count; i++) {
|
||||
const struct limine_memmap_entry* entry = memmap->entries[i];
|
||||
if (entry != pool) {
|
||||
out[n++] = (struct phys_mem){entry->base, entry->length, convert_mem_type(entry->type)};
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t end = entry->base + entry->length;
|
||||
out[n++] = (struct phys_mem){entry->base, pool_static_end - entry->base, PHYS_STATIC};
|
||||
out[n++] = (struct phys_mem){pool_static_end, pool_reclaimable_start - pool_static_end, PHYS_USABLE};
|
||||
out[n++] = (struct phys_mem){pool_reclaimable_start, end - pool_reclaimable_start, PHYS_RECLAIMABLE};
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void find_framebuffer(struct boot_framebuffer* out) {
|
||||
const struct limine_framebuffer_response* res = framebuffer_request.response;
|
||||
if (!res || !res->framebuffer_count)
|
||||
return;
|
||||
|
||||
const struct limine_framebuffer* fb = res->framebuffers[0];
|
||||
out->base = to_phys(fb->address);
|
||||
out->width = fb->width;
|
||||
out->height = fb->height;
|
||||
out->pitch = fb->pitch;
|
||||
out->bytes_per_pixel = fb->bpp / 8;
|
||||
out->red = (struct boot_color){fb->red_mask_shift, fb->red_mask_size};
|
||||
out->green = (struct boot_color){fb->green_mask_shift, fb->green_mask_size};
|
||||
out->blue = (struct boot_color){fb->blue_mask_shift, fb->blue_mask_size};
|
||||
}
|
||||
|
||||
static bool load() {
|
||||
const struct limine_memmap_response* memmap = memmap_request.response;
|
||||
const struct limine_executable_address_response* exec = executable_address_request.response;
|
||||
if (!LIMINE_BASE_REVISION_SUPPORTED(limine_revision) || !memmap || !hhdm_request.response || !exec)
|
||||
return false;
|
||||
hhdm_offset = hhdm_request.response->offset;
|
||||
|
||||
if (!arch_check_cpu() || !init_pool(memmap))
|
||||
return false;
|
||||
|
||||
const struct limine_bootloader_info_response* info = bootloader_info_request.response;
|
||||
struct kernel_image kernel;
|
||||
if (!loader_load_kernel(&kernel, info ? info->name : "Limine"))
|
||||
return false;
|
||||
|
||||
uint64_t mem;
|
||||
if (!loader_alloc(PHYS_RECLAIMABLE, (memmap->entry_count + 2) * sizeof(struct phys_mem), &mem))
|
||||
return false;
|
||||
|
||||
const struct limine_executable_file_response* file = executable_file_request.response;
|
||||
if (file && file->executable_file->string) {
|
||||
const char* cmdline = file->executable_file->string;
|
||||
memcpy(kernel.info->cmdline, cmdline, MIN(strlen(cmdline), sizeof(kernel.info->cmdline) - 1));
|
||||
}
|
||||
|
||||
const struct limine_module_response* modules = module_request.response;
|
||||
for (size_t i = 0; modules && i < modules->module_count; i++) {
|
||||
const struct limine_file* module = modules->modules[i];
|
||||
if (!loader_add_file(&kernel, module->path, to_phys(module->address), module->size))
|
||||
return false;
|
||||
}
|
||||
|
||||
find_framebuffer(&kernel.info->fb);
|
||||
if (rsdp_request.response)
|
||||
kernel.info->rsdp = to_phys(rsdp_request.response->address);
|
||||
if (dtb_request.response && dtb_request.response->dtb_ptr)
|
||||
kernel.info->dtb = to_phys(dtb_request.response->dtb_ptr);
|
||||
|
||||
const uint64_t trampoline = (uint64_t)arch_enter_kernel;
|
||||
uint64_t root;
|
||||
if (!loader_page_tables(
|
||||
&kernel,
|
||||
mem_map_top(memmap),
|
||||
exec->physical_base + (trampoline - exec->virtual_base),
|
||||
&root
|
||||
))
|
||||
return false;
|
||||
|
||||
struct phys_mem* mem_map = loader_ptr(mem);
|
||||
loader_set_mem_map(&kernel, mem_map, convert_mem_map(memmap, mem_map));
|
||||
arch_enter_kernel(root, kernel.entry);
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void loader_main() {
|
||||
load();
|
||||
for (;;)
|
||||
asm volatile("");
|
||||
}
|
||||
+25
-1
@@ -1 +1,25 @@
|
||||
kernel_sources += files('entry.c')
|
||||
limine_lds = meson.current_source_dir() / 'loader.ld'
|
||||
|
||||
limine_c_args = [loader_c_args, '-fno-pic']
|
||||
if target_machine.cpu_family() == 'x86_64'
|
||||
limine_c_args += '-mcmodel=kernel'
|
||||
endif
|
||||
|
||||
executable(
|
||||
'zinnia-limine',
|
||||
sources: [loader_sources, files('main.c'), loader_kernel],
|
||||
include_directories: [common_includes, loader_includes],
|
||||
c_args: limine_c_args,
|
||||
link_args: [
|
||||
'-nostdlib',
|
||||
'-static',
|
||||
'-Wl,-T,' + limine_lds,
|
||||
'-Wl,-z,noexecstack',
|
||||
'-Wl,-z,max-page-size=0x1000',
|
||||
'-Wl,--build-id=none',
|
||||
],
|
||||
link_depends: limine_lds,
|
||||
pie: false,
|
||||
install: true,
|
||||
install_dir: zinnia_bootdir,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <bits/elf.h>
|
||||
#include <string.h>
|
||||
#include "loader.h"
|
||||
|
||||
static bool in_image(const struct kernel_image* image, uint64_t vaddr, uint64_t len) {
|
||||
return vaddr >= image->virt && len <= image->size && vaddr - image->virt <= image->size - len;
|
||||
}
|
||||
|
||||
void* kernel_ptr(const struct kernel_image* image, uint64_t vaddr) {
|
||||
return loader_ptr(image->phys + (vaddr - image->virt));
|
||||
}
|
||||
|
||||
static bool apply_relocations(const struct kernel_image* image, uint64_t dynamic_vaddr) {
|
||||
if (!in_image(image, dynamic_vaddr, sizeof(elf64_dyn_t)))
|
||||
return false;
|
||||
|
||||
uint64_t rela = 0;
|
||||
uint64_t rela_size = 0;
|
||||
uint64_t rela_ent = sizeof(elf64_rela_t);
|
||||
for (const elf64_dyn_t* dyn = kernel_ptr(image, dynamic_vaddr); dyn->d_tag != DT_NULL; dyn++) {
|
||||
switch (dyn->d_tag) {
|
||||
case DT_RELA:
|
||||
rela = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
rela_size = dyn->d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
rela_ent = dyn->d_un.d_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rela)
|
||||
return true;
|
||||
if (rela_ent < sizeof(elf64_rela_t) || !in_image(image, rela, rela_size))
|
||||
return false;
|
||||
|
||||
for (uint64_t off = 0; off + rela_ent <= rela_size; off += rela_ent) {
|
||||
const elf64_rela_t* r = kernel_ptr(image, rela + off);
|
||||
const uint32_t type = ELF64_R_TYPE(r->r_info);
|
||||
if (type == R_NONE)
|
||||
continue;
|
||||
if (type != R_RELATIVE)
|
||||
return false;
|
||||
if (!in_image(image, r->r_offset, sizeof(uint64_t)))
|
||||
return false;
|
||||
*(uint64_t*)kernel_ptr(image, r->r_offset) = (uint64_t)r->r_addend;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool elf_load(const uint8_t* elf, size_t size, struct kernel_image* out) {
|
||||
elf64_ehdr_t ehdr;
|
||||
if (size < sizeof(ehdr))
|
||||
return false;
|
||||
memcpy(&ehdr, elf, sizeof(ehdr));
|
||||
|
||||
if (memcmp(ehdr.e_ident, ELF_MAG, sizeof(ELF_MAG)) || ehdr.e_ident[EI_CLASS] != ELF_ARCH_CLASS ||
|
||||
ehdr.e_ident[EI_DATA] != ELF_ARCH_DATA)
|
||||
return false;
|
||||
if (ehdr.e_machine != ELF_ARCH_MACHINE)
|
||||
return false;
|
||||
if (ehdr.e_phentsize != sizeof(elf64_phdr_t) || ehdr.e_phoff > size ||
|
||||
(size - ehdr.e_phoff) / sizeof(elf64_phdr_t) < ehdr.e_phnum)
|
||||
return false;
|
||||
|
||||
const elf64_phdr_t* phdrs = (const elf64_phdr_t*)(elf + ehdr.e_phoff);
|
||||
|
||||
uint64_t lo = UINT64_MAX;
|
||||
uint64_t hi = 0;
|
||||
uint64_t dynamic_vaddr = 0;
|
||||
uint64_t info_vaddr = 0;
|
||||
uint64_t info_size = 0;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf64_phdr_t* ph = &phdrs[i];
|
||||
if (ph->p_type == PT_DYNAMIC)
|
||||
dynamic_vaddr = ph->p_vaddr;
|
||||
if (ph->p_type == PT_BOOT_INFO) {
|
||||
info_vaddr = ph->p_vaddr;
|
||||
info_size = ph->p_memsz;
|
||||
}
|
||||
if (ph->p_type != PT_LOAD || ph->p_memsz == 0)
|
||||
continue;
|
||||
if (ph->p_filesz > ph->p_memsz || ph->p_offset > size || size - ph->p_offset < ph->p_filesz)
|
||||
return false;
|
||||
if (ALIGN_DOWN(ph->p_vaddr, PAGE_SIZE) < lo)
|
||||
lo = ALIGN_DOWN(ph->p_vaddr, PAGE_SIZE);
|
||||
if (ALIGN_UP(ph->p_vaddr + ph->p_memsz, PAGE_SIZE) > hi)
|
||||
hi = ALIGN_UP(ph->p_vaddr + ph->p_memsz, PAGE_SIZE);
|
||||
}
|
||||
if (lo >= hi)
|
||||
return false;
|
||||
|
||||
out->virt = lo;
|
||||
out->size = hi - lo;
|
||||
out->entry = ehdr.e_entry;
|
||||
if (!in_image(out, out->entry, 1))
|
||||
return false;
|
||||
if (info_size != sizeof(struct boot_info) || !in_image(out, info_vaddr, info_size))
|
||||
return false;
|
||||
|
||||
if (!loader_alloc(PHYS_STATIC, out->size, &out->phys))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf64_phdr_t* ph = &phdrs[i];
|
||||
if (ph->p_type != PT_LOAD || ph->p_memsz == 0)
|
||||
continue;
|
||||
memcpy(kernel_ptr(out, ph->p_vaddr), elf + ph->p_offset, ph->p_filesz);
|
||||
}
|
||||
out->info = kernel_ptr(out, info_vaddr);
|
||||
|
||||
if (dynamic_vaddr)
|
||||
return apply_relocations(out, dynamic_vaddr);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
size_t strlen(const char* str) {
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
size_t len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
int memcmp(const void* s1, const void* s2, size_t size) {
|
||||
const uint8_t* a = s1;
|
||||
const uint8_t* b = s2;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (a[i] != b[i])
|
||||
return a[i] < b[i] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
|
||||
uint8_t* d = dest;
|
||||
const uint8_t* s = src;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
d[i] = s[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size) {
|
||||
uint8_t* d = dstptr;
|
||||
const uint8_t* s = srcptr;
|
||||
if (d < s) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
d[i] = s[i];
|
||||
} else {
|
||||
for (size_t i = size; i > 0; i--)
|
||||
d[i - 1] = s[i - 1];
|
||||
}
|
||||
return dstptr;
|
||||
}
|
||||
|
||||
void* memset(void* dest, int value, size_t n) {
|
||||
uint8_t* d = dest;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
d[i] = (uint8_t)value;
|
||||
return dest;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include "loader.h"
|
||||
#include <string.h>
|
||||
#include "loader_arch.h"
|
||||
|
||||
extern const uint8_t __kernel_start[];
|
||||
extern const uint8_t __kernel_end[];
|
||||
|
||||
bool loader_load_kernel(struct kernel_image* out, const char* name) {
|
||||
if (!elf_load(__kernel_start, __kernel_end - __kernel_start, out))
|
||||
return false;
|
||||
|
||||
struct boot_info* info = out->info;
|
||||
memcpy(info->loader, name, MIN(strlen(name), sizeof(info->loader) - 1));
|
||||
info->hhdm_base = ARCH_HHDM_BASE;
|
||||
info->phys_base = out->phys;
|
||||
info->virt_base = out->virt;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loader_add_file(const struct kernel_image* kernel, const char* path, uint64_t base, uint64_t length) {
|
||||
struct boot_info* info = kernel->info;
|
||||
if (info->num_files == ARRAY_SIZE(info->files))
|
||||
return false;
|
||||
|
||||
struct boot_file* file = &info->files[info->num_files++];
|
||||
file->data = base;
|
||||
file->length = length;
|
||||
const size_t path_len = MIN(strlen(path), sizeof(file->path) - 1);
|
||||
memcpy(file->path, path, path_len);
|
||||
file->path[path_len] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void loader_set_mem_map(const struct kernel_image* kernel, struct phys_mem* map, size_t len) {
|
||||
for (size_t i = 1; i < len; i++) {
|
||||
const struct phys_mem entry = map[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && map[j - 1].address > entry.address) {
|
||||
map[j] = map[j - 1];
|
||||
j--;
|
||||
}
|
||||
map[j] = entry;
|
||||
}
|
||||
|
||||
size_t merged = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!map[i].length)
|
||||
continue;
|
||||
struct phys_mem* prev = merged ? &map[merged - 1] : nullptr;
|
||||
if (prev && prev->usage == map[i].usage && prev->address + prev->length == map[i].address)
|
||||
prev->length += map[i].length;
|
||||
else
|
||||
map[merged++] = map[i];
|
||||
}
|
||||
|
||||
struct boot_info* info = kernel->info;
|
||||
info->num_mem_maps = MIN(merged, ARRAY_SIZE(info->mem_map));
|
||||
memcpy(info->mem_map, map, info->num_mem_maps * sizeof(*map));
|
||||
}
|
||||
|
||||
bool loader_page_tables(
|
||||
const struct kernel_image* kernel,
|
||||
uint64_t mem_top,
|
||||
uint64_t trampoline_phys,
|
||||
uint64_t* out_root
|
||||
) {
|
||||
uint64_t root;
|
||||
if (!loader_alloc(PHYS_RECLAIMABLE, PAGE_SIZE, &root))
|
||||
return false;
|
||||
|
||||
const uint64_t trampoline_virt = ALIGN_DOWN((uint64_t)arch_enter_kernel, PAGE_SIZE);
|
||||
if (!arch_paging_map(root, ARCH_HHDM_BASE, 0, ALIGN_UP(mem_top, ARCH_HUGE_PAGE_SIZE), ARCH_HUGE_PAGE_SIZE) ||
|
||||
!arch_paging_map(root, kernel->virt, kernel->phys, kernel->size, PAGE_SIZE) ||
|
||||
!arch_paging_map(root, trampoline_virt, ALIGN_DOWN(trampoline_phys, PAGE_SIZE), 2 * PAGE_SIZE, PAGE_SIZE))
|
||||
return false;
|
||||
|
||||
*out_root = root;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/boot.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr uint64_t PAGE_SIZE = 0x1000;
|
||||
|
||||
struct kernel_image {
|
||||
uint64_t phys;
|
||||
uint64_t virt;
|
||||
uint64_t size;
|
||||
uint64_t entry;
|
||||
struct boot_info* info;
|
||||
};
|
||||
|
||||
bool loader_alloc(enum phys_mem_usage usage, size_t size, uint64_t* out);
|
||||
void* loader_ptr(uint64_t phys);
|
||||
|
||||
bool elf_load(const uint8_t* elf, size_t size, struct kernel_image* out);
|
||||
void* kernel_ptr(const struct kernel_image* image, uint64_t vaddr);
|
||||
|
||||
bool loader_load_kernel(struct kernel_image* out, const char* name);
|
||||
bool loader_add_file(const struct kernel_image* kernel, const char* path, uint64_t base, uint64_t length);
|
||||
void loader_set_mem_map(const struct kernel_image* kernel, struct phys_mem* map, size_t len);
|
||||
|
||||
bool loader_page_tables(
|
||||
const struct kernel_image* kernel,
|
||||
uint64_t mem_top,
|
||||
uint64_t trampoline_phys,
|
||||
uint64_t* out_root
|
||||
);
|
||||
|
||||
bool arch_check_cpu();
|
||||
bool arch_paging_map(uint64_t root, uint64_t virt, uint64_t phys, uint64_t size, uint64_t page_size);
|
||||
|
||||
[[noreturn]]
|
||||
void arch_enter_kernel(uint64_t root, uint64_t entry);
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
.intel_syntax noprefix
|
||||
.text
|
||||
|
||||
.globl arch_enter_kernel
|
||||
.type arch_enter_kernel, @function
|
||||
.balign 16
|
||||
arch_enter_kernel:
|
||||
cli
|
||||
mov cr3, rdi
|
||||
jmp rsi
|
||||
.size arch_enter_kernel, . - arch_enter_kernel
|
||||
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr uint64_t ARCH_HHDM_BASE = 0xffff'8000'0000'0000;
|
||||
constexpr uint64_t ARCH_HUGE_PAGE_SIZE = 0x20'0000;
|
||||
@@ -0,0 +1,11 @@
|
||||
loader_sources += files(
|
||||
'enter.S',
|
||||
'paging.c',
|
||||
)
|
||||
|
||||
loader_c_args += [
|
||||
'-mno-red-zone',
|
||||
'-masm=intel',
|
||||
]
|
||||
|
||||
loader_objcopy_format = 'elf64-x86-64'
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <bits/pmap.h>
|
||||
#include <x86_64/defs.h>
|
||||
#include "loader.h"
|
||||
#include "loader_arch.h"
|
||||
|
||||
static bool next_table(uint64_t* table, size_t index, uint64_t** out) {
|
||||
if (!(table[index] & ARCH_FLAG_PRESENT)) {
|
||||
uint64_t page;
|
||||
if (!loader_alloc(PHYS_RECLAIMABLE, PAGE_SIZE, &page))
|
||||
return false;
|
||||
table[index] = page | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
|
||||
}
|
||||
*out = loader_ptr(table[index] & ARCH_PTE_ADDR_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool arch_check_cpu() {
|
||||
uint64_t cr4;
|
||||
asm volatile("mov %0, cr4" : "=r"(cr4));
|
||||
if (cr4 & CR4_LA57)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool arch_paging_map(uint64_t root, uint64_t virt, uint64_t phys, uint64_t size, uint64_t page_size) {
|
||||
const int leaf_level = page_size == ARCH_HUGE_PAGE_SIZE ? 1 : 0;
|
||||
|
||||
for (uint64_t off = 0; off < size; off += page_size) {
|
||||
const uint64_t va = virt + off;
|
||||
uint64_t* table = loader_ptr(root);
|
||||
for (int level = 3; level > leaf_level; level--) {
|
||||
if (!next_table(table, (va >> (12 + 9 * level)) & 0x1ff, &table))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t entry = (phys + off) | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
|
||||
if (leaf_level == 1)
|
||||
entry |= ARCH_FLAG_SIZE;
|
||||
table[(va >> (12 + 9 * leaf_level)) & 0x1ff] = entry;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
+42
-4
@@ -1,6 +1,44 @@
|
||||
kernel_sources += files(
|
||||
'cmdline.c',
|
||||
'main.c',
|
||||
objcopy = find_program('objcopy')
|
||||
zinnia_bootdir = zinnia_datadir / 'boot'
|
||||
|
||||
loader_sources = files(
|
||||
'loader/elf.c',
|
||||
'loader/lib.c',
|
||||
'loader/loader.c',
|
||||
)
|
||||
|
||||
subdir('limine')
|
||||
loader_c_args = [
|
||||
'-ffreestanding',
|
||||
'-nostdinc',
|
||||
'-fno-stack-protector',
|
||||
'-fno-omit-frame-pointer',
|
||||
'-mgeneral-regs-only',
|
||||
]
|
||||
|
||||
loader_includes = include_directories('loader', 'loader' / target_machine.cpu_family())
|
||||
|
||||
loader_objcopy_format = 'unknown'
|
||||
|
||||
subdir('loader' / target_machine.cpu_family())
|
||||
|
||||
loader_kernel = custom_target(
|
||||
'loader-kernel.o',
|
||||
input: kernel,
|
||||
output: 'loader-kernel.o',
|
||||
command: [
|
||||
objcopy,
|
||||
'-I', 'binary',
|
||||
'-O', loader_objcopy_format,
|
||||
'--rename-section', '.data=.kernel,alloc,load,readonly,data,contents',
|
||||
'@INPUT@',
|
||||
'@OUTPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
if kconfig.get('CONFIG_BOOT_EFISTUB', 'n') == 'y'
|
||||
subdir('efi')
|
||||
endif
|
||||
|
||||
if kconfig.get('CONFIG_BOOT_LIMINE', 'n') == 'y'
|
||||
subdir('limine')
|
||||
endif
|
||||
|
||||
+1
-13
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/boot.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/types.h>
|
||||
@@ -14,19 +15,6 @@ enum alloc_flags {
|
||||
ALLOC_MEM20 = 1 << 2, // Allocated memory needs to fit inside 20 bits.
|
||||
};
|
||||
|
||||
enum phys_mem_usage {
|
||||
PHYS_RESERVED,
|
||||
PHYS_USABLE,
|
||||
PHYS_STATIC,
|
||||
PHYS_RECLAIMABLE,
|
||||
};
|
||||
|
||||
struct phys_mem {
|
||||
phys_t address;
|
||||
size_t length;
|
||||
enum phys_mem_usage usage;
|
||||
};
|
||||
|
||||
// Base address of the HHDM.
|
||||
extern uintptr_t mem_hhdm_base;
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum phys_mem_usage {
|
||||
PHYS_RESERVED,
|
||||
PHYS_USABLE,
|
||||
PHYS_STATIC,
|
||||
PHYS_RECLAIMABLE,
|
||||
};
|
||||
|
||||
struct phys_mem {
|
||||
phys_t address;
|
||||
size_t length;
|
||||
enum phys_mem_usage usage;
|
||||
};
|
||||
|
||||
constexpr size_t BOOT_PATH_MAX = 128;
|
||||
|
||||
struct boot_file {
|
||||
phys_t data;
|
||||
size_t length;
|
||||
char path[BOOT_PATH_MAX];
|
||||
};
|
||||
|
||||
struct boot_color {
|
||||
uint8_t shift, size;
|
||||
};
|
||||
|
||||
struct boot_framebuffer {
|
||||
phys_t base;
|
||||
size_t width, height, pitch;
|
||||
uint8_t bytes_per_pixel;
|
||||
struct boot_color red, green, blue;
|
||||
};
|
||||
|
||||
constexpr size_t BOOT_LOADER_NAME_MAX = 64;
|
||||
constexpr size_t BOOT_CMDLINE_MAX = 4096;
|
||||
constexpr size_t BOOT_MEM_MAP_MAX = 128;
|
||||
constexpr size_t BOOT_FILES_MAX = 8;
|
||||
|
||||
struct boot_info {
|
||||
char loader[BOOT_LOADER_NAME_MAX];
|
||||
char cmdline[BOOT_CMDLINE_MAX];
|
||||
struct phys_mem mem_map[BOOT_MEM_MAP_MAX];
|
||||
size_t num_mem_maps;
|
||||
phys_t phys_base;
|
||||
uintptr_t virt_base;
|
||||
uintptr_t hhdm_base;
|
||||
struct boot_file files[BOOT_FILES_MAX];
|
||||
size_t num_files;
|
||||
struct boot_framebuffer fb;
|
||||
phys_t dtb;
|
||||
size_t dtb_size;
|
||||
phys_t rsdp;
|
||||
};
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/vfs.h>
|
||||
#include <arch/elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -84,6 +82,7 @@ enum : uint32_t {
|
||||
PT_MOD_CMDLINE = 0x60000005,
|
||||
PT_MOD_EXIT = 0x60000006,
|
||||
PT_MOD_MATCH = 0x60000007,
|
||||
PT_BOOT_INFO = 0x60000008,
|
||||
};
|
||||
|
||||
enum : uint16_t {
|
||||
|
||||
+2
-34
@@ -2,6 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/boot.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <zinnia/utils.h>
|
||||
@@ -13,40 +14,7 @@
|
||||
#define __initdata __used, __section(".init.data")
|
||||
#define __initdata_sorted(name) __used, __section(".init.data." name)
|
||||
|
||||
struct boot_file {
|
||||
phys_t data;
|
||||
size_t length;
|
||||
char path[128];
|
||||
};
|
||||
|
||||
struct boot_color {
|
||||
uint8_t shift, size;
|
||||
};
|
||||
|
||||
struct boot_framebuffer {
|
||||
phys_t base;
|
||||
size_t width, height, pitch;
|
||||
uint8_t bytes_per_pixel;
|
||||
struct boot_color red, green, blue;
|
||||
};
|
||||
|
||||
struct boot_info {
|
||||
char* cmdline;
|
||||
struct phys_mem* mem_map;
|
||||
size_t num_mem_maps;
|
||||
phys_t phys_base;
|
||||
uintptr_t virt_base;
|
||||
uintptr_t hhdm_base;
|
||||
struct boot_file* files;
|
||||
size_t num_files;
|
||||
struct boot_framebuffer fb;
|
||||
struct boot_file kernel_file;
|
||||
phys_t dtb;
|
||||
size_t dtb_size;
|
||||
#if CONFIG_ACPI
|
||||
phys_t rsdp;
|
||||
#endif
|
||||
};
|
||||
extern struct boot_info boot_info;
|
||||
|
||||
[[__visibility("hidden")]]
|
||||
extern uint8_t __ld_stack_top[];
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/boot.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/fdt.h>
|
||||
#include <zinnia/init.h>
|
||||
|
||||
[[__section(".boot.info")]]
|
||||
struct boot_info boot_info;
|
||||
|
||||
[[__init]]
|
||||
void kernel_entry() {
|
||||
kernel_early_init();
|
||||
|
||||
assert(boot_info.num_mem_maps, "Loader did not provide a memory map");
|
||||
|
||||
if (boot_info.dtb)
|
||||
boot_info.dtb_size = fdt_read_u32((const uint8_t*)(boot_info.hhdm_base + boot_info.dtb) + 4);
|
||||
|
||||
kernel_main(&boot_info);
|
||||
}
|
||||
@@ -75,6 +75,7 @@ void kernel_main(struct boot_info* info) {
|
||||
cmdline_parse(info->cmdline);
|
||||
|
||||
log("%s\n", zinnia_banner); // Say hello!
|
||||
log("Boot loader: %s\n", info->loader);
|
||||
log("Command line: %s\n", info->cmdline);
|
||||
|
||||
initgraph_run(sched_stage, info);
|
||||
@@ -1,13 +1,16 @@
|
||||
kernel_sources += files(
|
||||
'clock.c',
|
||||
'cmdline.c',
|
||||
'condvar.c',
|
||||
'console.c',
|
||||
'entry.c',
|
||||
'event.c',
|
||||
'guid.c',
|
||||
'initgraph.c',
|
||||
'iovec.c',
|
||||
'irq.c',
|
||||
'ksyms.c',
|
||||
'main.c',
|
||||
'module.c',
|
||||
'mutex.c',
|
||||
'panic.c',
|
||||
|
||||
+3
-2
@@ -2,7 +2,7 @@ project(
|
||||
'zinnia',
|
||||
license: 'ISC',
|
||||
version: '0.1.0',
|
||||
meson_version: '>=1.5.0',
|
||||
meson_version: '>=1.6.0',
|
||||
default_options: [
|
||||
'c_std=gnu23',
|
||||
'werror=true',
|
||||
@@ -95,7 +95,6 @@ modules = {}
|
||||
|
||||
# These dirs go first so we can add arch-specific flags to drivers.
|
||||
subdir('arch' / target_machine.cpu_family())
|
||||
subdir('boot')
|
||||
subdir('lib')
|
||||
subdir('mem')
|
||||
subdir('sched')
|
||||
@@ -175,6 +174,8 @@ kernel = executable(
|
||||
install_dir: zinnia_datadir,
|
||||
)
|
||||
|
||||
subdir('boot')
|
||||
|
||||
foreach name, v : modular_modules
|
||||
shared_library(
|
||||
name,
|
||||
|
||||
Reference in New Issue
Block a user