boot: Add EFISTUB and refactor boot protocols

This commit is contained in:
2026-10-27 22:50:43 +01:00
parent 6827dc309e
commit 196543c3d3
40 changed files with 1923 additions and 208 deletions
+1
View File
@@ -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"
+1
View File
@@ -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
+17
View File
@@ -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,
};
+1
View File
@@ -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,
-17
View File
@@ -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,
};
+19
View File
@@ -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
View File
@@ -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}};
+97
View File
@@ -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
View File
@@ -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);
}
+42
View File
@@ -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
View File
@@ -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);
+61
View File
@@ -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;
}
+13
View File
@@ -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;
+100
View File
@@ -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)
}
}
+9
View File
@@ -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;
+48
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
efistub_sources += files('entry.S')
+5
View File
@@ -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*))
-135
View File
@@ -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);
}
+46
View File
@@ -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)
}
}
+192
View File
@@ -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
View File
@@ -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,
)
+120
View File
@@ -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;
}
+53
View File
@@ -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;
}
+81
View File
@@ -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;
}
+41
View File
@@ -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);
+13
View File
@@ -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
+8
View File
@@ -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;
+11
View File
@@ -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'
+45
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+60
View File
@@ -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;
};
+1 -2
View File
@@ -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
View File
@@ -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[];
View File
+22
View File
@@ -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);
}
+1
View File
@@ -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);
+3
View File
@@ -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
View File
@@ -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,