4#include "exit_boot_services.h"
6typedef uint16_t efi_char16_t;
15typedef efi_status_t (*efi_handle_protocol_t)(efi_handle_t,
efi_guid_t*,
void**);
16typedef efi_status_t (*efi_allocate_pages_t)(uint32_t, uint32_t, uint64_t, uint64_t*);
22 efi_allocate_pages_t allocate_pages;
24 efi_get_memory_map_t get_memory_map;
27 uint8_t before_handle_protocol[9 * 8];
28 efi_handle_protocol_t handle_protocol;
29 void* reserved_after_handle_protocol;
30 void* register_protocol_notify;
32 void* locate_device_path;
33 void* install_configuration_table;
38 efi_exit_boot_services_t exit_boot_services;
43 efi_status_t (*output_string)(
void*, efi_char16_t*);
48 efi_char16_t* firmware_vendor;
49 uint32_t firmware_revision;
50 efi_handle_t console_in_handle;
52 efi_handle_t console_out_handle;
54 efi_handle_t standard_error_handle;
56 void* runtime_services;
58 uint64_t configuration_table_entries;
59 void* configuration_table;
67 efi_status_t (*read)(
efi_file_t*, uint64_t*,
void*);
76 efi_status_t (*open_volume)(
void*,
efi_file_t**);
82 efi_handle_t parent_handle;
84 efi_handle_t device_handle;
94 uint16_t type, machine;
96 uint64_t entry, program_offset, section_offset;
98 uint16_t header_size, program_size, program_count;
99 uint16_t section_size, section_count, section_string_index;
103 uint32_t type, flags;
104 uint64_t offset, virtual_address, physical_address;
105 uint64_t file_size, memory_size, alignment;
108#define EFI_LOADER_DATA 2
109#define EFI_ALLOCATE_MAX_ADDRESS 1
110#define EFI_ALLOCATE_ADDRESS 2
111#define EFI_FILE_MODE_READ 1
112#define MAX_PHYSICAL_ADDRESS 0xffffffffULL
113#define MAX_KERNEL_FILE_SIZE (64ULL * 1024 * 1024)
114#define MAX_ROOTFS_SIZE (512ULL * 1024 * 1024)
115#define KERNEL_HANDOFF_MAGIC 0x5045444947524545ULL
118 0x5b1b31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
120 0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
122 0x09576e92, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
124 0xb1b621d5, 0xf19c, 0x41a5, {0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}};
126 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81}};
129 if (table && table->con_out) {
130 table->con_out->output_string(table->con_out,
message);
135 if (a->a != b->a || a->b != b->b || a->c != b->c) {
138 for (
unsigned i = 0; i < 8; ++i) {
139 if (a->d[i] != b->d[i]) {
147 if (!bytes || bytes > MAX_PHYSICAL_ADDRESS - 4095) {
150 uint64_t address = MAX_PHYSICAL_ADDRESS;
151 const efi_status_t status = table->boot_services->allocate_pages(
152 EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA, (bytes + 4095) / 4096, &address);
153 return status == EFI_SUCCESS ? (
void*)address : 0;
156static void copy(
void* destination,
const void* source, uint64_t count) {
157 uint8_t* to = destination;
158 const uint8_t* from = source;
159 for (uint64_t i = 0; i < count; ++i) {
164static void zero(
void* destination, uint64_t count) {
165 uint8_t* bytes = destination;
166 for (uint64_t i = 0; i < count; ++i) {
171static void clean_kernel_segment(uint64_t address, uint64_t size) {
173 __asm__
volatile(
"mrs %0, ctr_el0" :
"=r"(ctr));
174 const uint64_t line_size = 4ULL << ((ctr >> 16) & 15);
175 const uint64_t end = address + size;
176 for (uint64_t cursor = address & ~(line_size - 1); cursor < end; cursor += line_size) {
177 __asm__
volatile(
"dc cvac, %0" : :
"r"(cursor) :
"memory");
182 uint64_t maximum, uint64_t* length) {
184 if (root->open(root, &file, path, EFI_FILE_MODE_READ, 0) != EFI_SUCCESS) {
188 uint64_t info_size =
sizeof(info);
189 if (file->get_info(file, (
efi_guid_t*)&file_info_guid, &info_size, info) != EFI_SUCCESS ||
190 info[1] == 0 || info[1] > maximum) {
194 void* buffer = allocate(table, info[1] + 1);
199 uint64_t position = 0;
200 while (position < info[1]) {
201 uint64_t requested = info[1] - position;
202 if (file->read(file, &requested, (uint8_t*)buffer + position) != EFI_SUCCESS || !requested) {
206 position += requested;
208 ((uint8_t*)buffer)[position] = 0;
214static int load_kernel(
efi_system_table_t* table,
const uint8_t* data, uint64_t length,
220 if (header->ident[0] != 0x7f || header->ident[1] !=
'E' || header->ident[2] !=
'L' ||
221 header->ident[3] !=
'F' || header->ident[4] != 2 || header->ident[5] != 1 ||
222 header->machine != 183 || header->type != 2 ||
223 header->program_size !=
sizeof(
elf64_program_t) || !header->program_count ||
224 header->program_offset > length ||
225 header->program_count > (length - header->program_offset) / header->program_size) {
228 int entry_loaded = 0;
229 for (uint16_t i = 0; i < header->program_count; ++i) {
232 if (program->type != 1) {
235 if (!program->memory_size || program->file_size > program->memory_size ||
236 program->offset > length || program->file_size > length - program->offset ||
237 (program->physical_address & 4095) || program->physical_address > MAX_PHYSICAL_ADDRESS ||
238 program->memory_size > MAX_PHYSICAL_ADDRESS - program->physical_address) {
241 uint64_t address = program->physical_address;
242 if (table->boot_services->allocate_pages(EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
243 (program->memory_size + 4095) / 4096,
244 &address) != EFI_SUCCESS) {
247 copy((
void*)address, data + program->offset, program->file_size);
248 zero((uint8_t*)address + program->file_size, program->memory_size - program->file_size);
249 clean_kernel_segment(address, program->memory_size);
250 if (header->entry >= address && header->entry - address < program->memory_size) {
262 *entry = header->entry;
266static void enter_kernel(uint64_t entry, uint64_t fdt, uint64_t initrd_start, uint64_t initrd_end,
267 uint64_t command_line, uint64_t memory_map, uint64_t memory_map_bytes,
269static void enter_kernel(uint64_t entry, uint64_t fdt, uint64_t initrd_start, uint64_t initrd_end,
270 uint64_t command_line, uint64_t memory_map, uint64_t memory_map_bytes,
271 uint64_t acpi_rsdp) {
273 "msr daifset, #0xf\n"
275 "mrs x7, sctlr_el1\n"
277 "msr sctlr_el1, x7\n"
289 :
"r"(fdt),
"r"(initrd_start),
"r"(initrd_end),
"r"(command_line),
"r"(KERNEL_HANDOFF_MAGIC),
290 "r"(memory_map),
"r"(memory_map_bytes),
"r"(acpi_rsdp),
"r"(entry)
291 :
"x0",
"x1",
"x2",
"x3",
"x4",
"x5",
"x6",
"x7",
"memory");
293 __asm__
volatile(
"wfe");
298 print(table, (efi_char16_t*)L
"Pedigree ARM64 UEFI loader\r\n");
300 uint64_t fdt = 0, acpi_rsdp = 0;
302 for (uint64_t i = 0; i < table->configuration_table_entries; ++i) {
303 if (same_guid(&tables[i].guid, &fdt_guid)) {
304 fdt = (uint64_t)tables[i].table;
305 }
else if (same_guid(&tables[i].guid, &acpi_guid)) {
306 acpi_rsdp = (uint64_t)tables[i].table;
309 if (!fdt && !acpi_rsdp) {
310 print(table, (efi_char16_t*)L
"UEFI: no FDT or ACPI table\r\n");
311 return EFI_LOAD_ERROR;
314 fdt ? (efi_char16_t*)L
"UEFI: FDT platform\r\n" : (efi_char16_t*)L
"UEFI: ACPI platform\r\n");
318 if (table->boot_services->handle_protocol(image, (
efi_guid_t*)&loaded_image_guid,
319 (
void**)&loaded) != EFI_SUCCESS ||
320 table->boot_services->handle_protocol(loaded->device_handle, (
efi_guid_t*)&file_system_guid,
321 (
void**)&filesystem) != EFI_SUCCESS) {
322 print(table, (efi_char16_t*)L
"UEFI: filesystem unavailable\r\n");
323 return EFI_LOAD_ERROR;
326 if (filesystem->open_volume(filesystem, &root) != EFI_SUCCESS) {
327 print(table, (efi_char16_t*)L
"UEFI: cannot open ESP\r\n");
328 return EFI_LOAD_ERROR;
331 uint64_t kernel_length = 0, rootfs_length = 0, cmdline_length = 0;
332 uint8_t* kernel_file = read_file(table, root, (efi_char16_t*)L
"\\EFI\\PEDIGREE\\current\\kernel",
333 MAX_KERNEL_FILE_SIZE, &kernel_length);
334 uint8_t* rootfs = read_file(table, root, (efi_char16_t*)L
"\\EFI\\PEDIGREE\\current\\rootfs.img",
335 MAX_ROOTFS_SIZE, &rootfs_length);
336 char* cmdline = read_file(table, root, (efi_char16_t*)L
"\\EFI\\PEDIGREE\\current\\cmdline", 4095,
339 if (!kernel_file || !cmdline) {
340 print(table, (efi_char16_t*)L
"UEFI: kernel or command line missing\r\n");
341 return EFI_LOAD_ERROR;
343 for (uint64_t i = 0; i < cmdline_length; ++i) {
344 if (!cmdline[i] || cmdline[i] ==
'\r' || cmdline[i] ==
'\n') {
351 if (!load_kernel(table, kernel_file, kernel_length, &entry)) {
352 print(table, (efi_char16_t*)L
"UEFI: cannot load ARM64 kernel\r\n");
353 return EFI_LOAD_ERROR;
356 void* raw_map = allocate(table, 16 * 4096);
358 if (!raw_map || !normalized) {
359 print(table, (efi_char16_t*)L
"UEFI: memory map allocation failed\r\n");
360 return EFI_LOAD_ERROR;
362 print(table, (efi_char16_t*)L
"UEFI: entering kernel\r\n");
363 uint32_t normalized_bytes = 0;
364 int exit_attempted = 0;
365 const efi_status_t status = exit_boot_services_with_map(
366 table->boot_services->get_memory_map, table->boot_services->exit_boot_services, image,
367 raw_map, 16 * 4096, normalized, 16 * 4096, &normalized_bytes, &exit_attempted);
368 if (status != EFI_SUCCESS) {
369 if (!exit_attempted) {
370 print(table, (efi_char16_t*)L
"UEFI: ExitBootServices failed\r\n");
374 const uint64_t initrd_start = (uint64_t)rootfs;
375 enter_kernel(entry, fdt, initrd_start, initrd_start + rootfs_length, (uint64_t)cmdline,
376 (uint64_t)normalized, normalized_bytes, acpi_rsdp);