boot: Add EFISTUB and refactor boot protocols
This commit is contained in:
+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')
|
||||
Reference in New Issue
Block a user