The Pedigree Project 0.1
loader.c
1/*
2 * Minimal UEFI loader for the native x86_64 kernel.
3 *
4 * This file intentionally uses only UEFI protocols and freestanding C. The
5 * PE/COFF wrapper is generated by scripts/uefi_pe.py so the loader remains
6 * buildable with the host clang without adding a second C runtime.
7 */
8
9#include <stdint.h>
10
11#include "exit_boot_services.h"
12#include "framebuffer.h"
13#include "load_options.h"
14
15typedef uint16_t efi_char16_t;
16typedef struct efi_guid {
17 uint32_t a;
18 uint16_t b;
19 uint16_t c;
20 uint8_t d[8];
22
25typedef struct efi_file efi_file_t;
26
27typedef efi_status_t (*efi_handle_protocol_t)(efi_handle_t, efi_guid_t*, void**);
28typedef efi_status_t (*efi_allocate_pages_t)(uint32_t, uint32_t, uint64_t, uint64_t*);
29typedef efi_status_t (*efi_locate_device_path_t)(efi_guid_t*, void**, efi_handle_t*);
30typedef efi_status_t (*efi_locate_handle_buffer_t)(uint32_t, efi_guid_t*, void*, uint64_t*,
31 efi_handle_t**);
32typedef efi_status_t (*efi_locate_protocol_t)(efi_guid_t*, void*, void**);
33
35 uint8_t header[24];
36 void* raise_tpl;
37 void* restore_tpl;
38 void* allocate_pages;
39 void* free_pages;
40 void* get_memory_map;
41 void* allocate_pool;
42 efi_free_pool_t free_pool;
43 uint8_t before_handle_protocol[9 * 8];
44 efi_handle_protocol_t handle_protocol;
45 void* reserved_after_handle_protocol;
46 void* register_protocol_notify;
47 void* locate_handle;
48 efi_locate_device_path_t locate_device_path;
49 void* install_configuration_table;
50 void* load_image;
51 void* start_image;
52 void* exit;
53 void* unload_image;
54 efi_exit_boot_services_t exit_boot_services;
55 uint8_t between_exit_and_open[5 * 8];
56 void* open_protocol;
57 void* close_protocol;
58 void* open_protocol_information;
59 void* protocols_per_handle;
60 efi_locate_handle_buffer_t locate_handle_buffer;
61 efi_locate_protocol_t locate_protocol;
62};
63
64typedef efi_status_t (*efi_output_string_t)(void*, efi_char16_t*);
65typedef struct efi_simple_text_output {
66 void* reset;
67 efi_output_string_t output_string;
69
70struct efi_system_table {
71 uint8_t header[24];
72 efi_char16_t* firmware_vendor;
73 uint32_t firmware_revision;
74 efi_handle_t console_in_handle;
75 void* con_in;
76 efi_handle_t console_out_handle;
78 efi_handle_t standard_error_handle;
79 void* std_err;
80 void* runtime_services;
81 efi_boot_services_t* boot_services;
82 uint64_t configuration_table_entries;
83 void* configuration_table;
84};
85
86typedef efi_status_t (*efi_file_open_t)(efi_file_t*, efi_file_t**, efi_char16_t*, uint64_t,
87 uint64_t);
88typedef efi_status_t (*efi_file_close_t)(efi_file_t*);
89typedef efi_status_t (*efi_file_read_t)(efi_file_t*, uint64_t*, void*);
90typedef efi_status_t (*efi_file_get_info_t)(efi_file_t*, efi_guid_t*, uint64_t*, void*);
91
92struct efi_file {
93 uint64_t revision;
94 efi_file_open_t open;
95 efi_file_close_t close;
96 void* delete_file;
97 efi_file_read_t read;
98 void* write;
99 void* get_position;
100 void* set_position;
101 efi_file_get_info_t get_info;
102};
103
105 uint64_t revision;
106 efi_status_t (*open_volume)(struct efi_simple_file_system*, efi_file_t**);
108
109typedef struct efi_loaded_image {
110 uint32_t revision;
111 uint32_t reserved;
112 efi_handle_t parent_handle;
113 efi_system_table_t* system_table;
114 efi_handle_t device_handle;
115 void* file_path;
116 void* reserved2;
117 uint32_t load_options_size;
118 void* load_options;
120
121typedef struct efi_device_path {
122 uint8_t type;
123 uint8_t subtype;
124 uint16_t length;
125} __attribute__((packed)) efi_device_path_t;
126
127typedef struct efi_configuration_table {
128 efi_guid_t vendor_guid;
129 void* vendor_table;
131
132typedef struct bootstrap_module {
133 uint64_t base;
134 uint64_t end;
135 uint64_t name;
136 uint64_t pad;
138
139typedef struct bootstrap_info {
140 uint32_t flags;
141 uint32_t reserved;
142 uint32_t module_count;
143 uint64_t modules;
144 uint32_t section_count;
145 uint32_t section_entry_size;
146 uint32_t section_string_index;
147 uint64_t section_headers;
148 uint64_t memory_map;
149 uint32_t memory_map_length;
150 uint32_t memory_map_entry_size;
151 uint64_t command_line;
152 uint64_t acpi_rsdp;
153 uint64_t smbios;
154 uint64_t framebuffer;
155 uint32_t framebuffer_width;
156 uint32_t framebuffer_height;
157 uint32_t framebuffer_pitch;
158 uint32_t framebuffer_bpp;
159 uint32_t framebuffer_format;
160} __attribute__((packed)) bootstrap_info_t;
161
162_Static_assert(sizeof(bootstrap_info_t) == 108, "bootstrap wire layout must match the kernel");
163
164typedef struct elf64_header {
165 uint8_t ident[16];
166 uint16_t type;
167 uint16_t machine;
168 uint32_t version;
169 uint64_t entry;
170 uint64_t program_header_offset;
171 uint64_t section_header_offset;
172 uint32_t flags;
173 uint16_t header_size;
174 uint16_t program_header_size;
175 uint16_t program_header_count;
176 uint16_t section_header_size;
177 uint16_t section_header_count;
178 uint16_t section_string_index;
179} __attribute__((packed)) elf64_header_t;
180
181typedef struct elf64_program_header {
182 uint32_t type;
183 uint32_t flags;
184 uint64_t offset;
185 uint64_t virtual_address;
186 uint64_t physical_address;
187 uint64_t file_size;
188 uint64_t memory_size;
189 uint64_t alignment;
190} __attribute__((packed)) elf64_program_header_t;
191
192typedef struct elf64_section_header {
193 uint32_t name;
194 uint32_t type;
195 uint64_t flags;
196 uint64_t address;
197 uint64_t offset;
198 uint64_t size;
199 uint32_t link;
200 uint32_t info;
201 uint64_t alignment;
202 uint64_t entry_size;
203} __attribute__((packed)) elf64_section_header_t;
204
205#define EFI_ALLOCATE_MAX_ADDRESS 1
206#define EFI_ALLOCATE_ADDRESS 2
207#define EFI_LOADER_DATA 4
208#define EFI_FILE_MODE_READ 1
209#define EFI_CONVENTIONAL_MEMORY 7
210#define EFI_ACPI_RECLAIM_MEMORY 9
211#define EFI_ACPI_MEMORY_NVS 10
212#define EFI_LOCATE_BY_PROTOCOL 2
213#define EFI_DEVICE_PATH_TYPE_END 0x7f
214#define EFI_DEVICE_PATH_TYPE_MEDIA 4
215#define EFI_DEVICE_PATH_SUBTYPE_FILE_PATH 4
216#define BOOTSTRAP_FLAG_CMDLINE 0x001
217#define BOOTSTRAP_FLAG_UEFI 0x080
218#define EFI_PAGE_SIZE 4096
219#define BOOTSTRAP_FLAG_MODULES 0x002
220#define BOOTSTRAP_FLAG_ELF 0x004
221#define BOOTSTRAP_FLAG_MEMORY_MAP 0x008
222#define BOOTSTRAP_FLAG_ACPI 0x010
223#define BOOTSTRAP_FLAG_SMBIOS 0x020
224#define BOOTSTRAP_FLAG_FRAMEBUFFER 0x040
225#define PHYS_ALIAS 0xffff800000000000ULL
226#define KERNEL_BASE 0xffffffff7ff00000ULL
227#define PT_LOAD 1
228#define SHF_ALLOC 2
229
230static const efi_guid_t loaded_image_guid = {
231 0x5b1b31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
232static const efi_guid_t simple_file_system_guid = {
233 0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
234static const efi_guid_t file_info_guid = {
235 0x09576e92, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
236static const efi_guid_t acpi20_guid = {
237 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81}};
238static const efi_guid_t smbios_guid = {
239 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}};
240static const efi_guid_t graphics_output_guid = {
241 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}};
242static const efi_guid_t edid_active_guid = {
243 0xbd8c1056, 0x9f36, 0x44ec, {0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86}};
244static const efi_guid_t device_path_guid = {
245 0x09576e91, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}};
246static const efi_char16_t current_prefix[] = L"\\EFI\\PEDIGREE\\current\\";
247static const efi_char16_t known_good_prefix[] = L"\\EFI\\PEDIGREE\\known-good\\";
248static const efi_char16_t debug_prefix[] = L"\\EFI\\PEDIGREE\\debug\\";
249static const efi_char16_t removable_prefix[] = L"\\EFI\\BOOT\\";
250
251static efi_system_table_t* g_system_table;
252
253static efi_edid_active_t* framebuffer_edid(efi_handle_t graphics_handle) {
254 efi_boot_services_t* services = g_system_table->boot_services;
255 efi_edid_active_t* edid = 0;
256 if (services->handle_protocol(graphics_handle, (efi_guid_t*)&edid_active_guid, (void**)&edid) ==
257 EFI_SUCCESS &&
258 edid)
259 return edid;
260 // EDID may be attached to an output child below the framebuffer's GOP handle.
261 efi_handle_t* outputs = 0;
262 uint64_t count = 0;
263 edid = 0;
264 if (services->locate_handle_buffer(EFI_LOCATE_BY_PROTOCOL, (efi_guid_t*)&edid_active_guid, 0,
265 &count, &outputs) == EFI_SUCCESS &&
266 outputs) {
267 for (uint64_t i = 0; i < count && !edid; ++i) {
268 void* path = 0;
269 efi_handle_t owner = 0;
270 if (services->handle_protocol(outputs[i], (efi_guid_t*)&device_path_guid, &path) !=
271 EFI_SUCCESS ||
272 !path ||
273 services->locate_device_path((efi_guid_t*)&graphics_output_guid, &path, &owner) !=
274 EFI_SUCCESS ||
275 owner != graphics_handle)
276 continue;
277 if (services->handle_protocol(outputs[i], (efi_guid_t*)&edid_active_guid, (void**)&edid) !=
278 EFI_SUCCESS)
279 edid = 0;
280 }
281 }
282 if (outputs)
283 services->free_pool(outputs);
284 return edid;
285}
286
287static void prepare_framebuffer(bootstrap_info_t* info) {
288 efi_boot_services_t* services = g_system_table->boot_services;
289 efi_graphics_output_t* console = 0;
290 boot_framebuffer_t framebuffer;
291 if (services->handle_protocol(g_system_table->console_out_handle,
292 (efi_guid_t*)&graphics_output_guid,
293 (void**)&console) != EFI_SUCCESS)
294 console = 0;
295 const uint64_t console_address = console && console->mode ? console->mode->framebuffer : 0;
296 efi_handle_t* handles = 0;
297 uint64_t count = 0;
298 int selected = 0;
299 if (services->locate_handle_buffer(EFI_LOCATE_BY_PROTOCOL, (efi_guid_t*)&graphics_output_guid, 0,
300 &count, &handles) == EFI_SUCCESS &&
301 handles) {
302 // Console splitters can retain stale mode descriptions. Prefer a hardware
303 // GOP, first matching the console, then an active display with EDID.
304 for (unsigned pass = 0; pass < 3 && !selected; ++pass) {
305 for (uint64_t i = 0; i < count && !selected; ++i) {
306 efi_graphics_output_t* graphics = 0;
307 void* path = 0;
308 if (services->handle_protocol(handles[i], (efi_guid_t*)&device_path_guid, &path) !=
309 EFI_SUCCESS ||
310 !path ||
311 services->handle_protocol(handles[i], (efi_guid_t*)&graphics_output_guid,
312 (void**)&graphics) != EFI_SUCCESS ||
313 !graphics)
314 continue;
315 efi_edid_active_t* edid = framebuffer_edid(handles[i]);
316 uint32_t width, height;
317 const int associated =
318 handles[i] == g_system_table->console_out_handle ||
319 (console_address && graphics->mode && graphics->mode->framebuffer == console_address);
320 unsigned priority = 2;
321 if (associated)
322 priority = 0;
323 else if (preferred_framebuffer_size(edid, &width, &height))
324 priority = 1;
325 if (priority == pass)
326 selected = select_framebuffer_mode(graphics, services->free_pool, edid, &framebuffer);
327 }
328 }
329 }
330 if (handles)
331 services->free_pool(handles);
332 if (!selected &&
333 !select_framebuffer_mode(console, services->free_pool,
334 framebuffer_edid(g_system_table->console_out_handle), &framebuffer))
335 return;
336 // No console output or mode changes may follow this handoff before firmware exit.
337 info->framebuffer = framebuffer.address;
338 info->framebuffer_width = framebuffer.width;
339 info->framebuffer_height = framebuffer.height;
340 info->framebuffer_pitch = framebuffer.pitch;
341 info->framebuffer_bpp = framebuffer.bpp;
342 info->framebuffer_format = framebuffer.format;
343 info->flags |= BOOTSTRAP_FLAG_FRAMEBUFFER;
344}
345
346static void zero(void* p, uint64_t size) {
347 uint8_t* bytes = (uint8_t*)p;
348 while (size--)
349 *bytes++ = 0;
350}
351
352static void copy(void* destination, const void* source, uint64_t size) {
353 uint8_t* d = (uint8_t*)destination;
354 const uint8_t* s = (const uint8_t*)source;
355 while (size--)
356 *d++ = *s++;
357}
358
359static int build_artifact_path(efi_loaded_image_t* loaded_image, const uefi_load_options_t* options,
360 const efi_char16_t* name, efi_char16_t* path,
361 uint64_t path_capacity) {
362 const efi_char16_t* prefix = current_prefix;
363 uint64_t prefix_length = sizeof(current_prefix) / sizeof(current_prefix[0]) - 1;
364 efi_char16_t* file_path = 0;
365 uint64_t file_path_length = 0;
366 efi_device_path_t* node = loaded_image ? (efi_device_path_t*)loaded_image->file_path : 0;
367
368 while (node && node->length >= sizeof(*node) && node->type != EFI_DEVICE_PATH_TYPE_END) {
369 if (node->type == EFI_DEVICE_PATH_TYPE_MEDIA &&
370 node->subtype == EFI_DEVICE_PATH_SUBTYPE_FILE_PATH) {
371 file_path = (efi_char16_t*)(node + 1);
372 file_path_length = (node->length - sizeof(*node)) / sizeof(efi_char16_t);
373 }
374 node = (efi_device_path_t*)((uint8_t*)node + node->length);
375 }
376
377 uint64_t length = 0;
378 uint64_t separator = 0;
379 int has_separator = 0;
380 if (file_path) {
381 while (length < file_path_length && file_path[length]) {
382 if (file_path[length] == '\\') {
383 separator = length;
384 has_separator = 1;
385 }
386 ++length;
387 }
388 }
389
390 if (file_path && has_separator) {
391 prefix = file_path;
392 prefix_length = separator + 1;
393 uint64_t removable_media_length = sizeof(removable_prefix) / sizeof(removable_prefix[0]) - 1;
394 if (prefix_length == removable_media_length) {
395 int is_removable_media_path = 1;
396 for (uint64_t i = 0; i < removable_media_length; ++i) {
397 if (prefix[i] != removable_prefix[i])
398 is_removable_media_path = 0;
399 }
400 if (is_removable_media_path) {
401 prefix = current_prefix;
402 prefix_length = sizeof(current_prefix) / sizeof(current_prefix[0]) - 1;
403 }
404 }
405 }
406
407 if (options->directory == UEFI_BOOT_DIRECTORY_KNOWN_GOOD) {
408 prefix = known_good_prefix;
409 prefix_length = sizeof(known_good_prefix) / sizeof(known_good_prefix[0]) - 1;
410 } else if (options->directory == UEFI_BOOT_DIRECTORY_CURRENT) {
411 prefix = current_prefix;
412 prefix_length = sizeof(current_prefix) / sizeof(current_prefix[0]) - 1;
413 } else if (options->directory == UEFI_BOOT_DIRECTORY_DEBUG) {
414 prefix = debug_prefix;
415 prefix_length = sizeof(debug_prefix) / sizeof(debug_prefix[0]) - 1;
416 }
417
418 uint64_t name_length = 0;
419 while (name[name_length])
420 ++name_length;
421 if (prefix_length + name_length + 1 > path_capacity)
422 return 0;
423
424 for (uint64_t i = 0; i < prefix_length; ++i)
425 path[i] = prefix[i];
426 for (uint64_t i = 0; i < name_length; ++i)
427 path[prefix_length + i] = name[i];
428 path[prefix_length + name_length] = 0;
429 return 1;
430}
431
432static void print(const efi_char16_t* message) {
433 if (g_system_table && g_system_table->con_out)
434 g_system_table->con_out->output_string(g_system_table->con_out, (efi_char16_t*)message);
435}
436
437static uint64_t pages_for(uint64_t size) {
438 return (size + EFI_PAGE_SIZE - 1) >> 12;
439}
440
441static void* allocate_pages(uint64_t size, uint64_t max_address) {
442 uint64_t address = max_address;
443 efi_status_t status = ((efi_allocate_pages_t)g_system_table->boot_services->allocate_pages)(
444 EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA, pages_for(size), &address);
445 return status == EFI_SUCCESS ? (void*)address : 0;
446}
447
448static void* allocate_at(uint64_t address, uint64_t size) {
449 uint64_t location = address;
450 efi_status_t status = ((efi_allocate_pages_t)g_system_table->boot_services->allocate_pages)(
451 EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, pages_for(size), &location);
452 return status == EFI_SUCCESS ? (void*)location : 0;
453}
454
455static uint64_t file_size(efi_file_t* file) {
456 uint8_t info[256];
457 uint64_t size = sizeof(info);
458 if (file->get_info(file, (efi_guid_t*)&file_info_guid, &size, info) != EFI_SUCCESS)
459 return 0;
460 return *(uint64_t*)(info + 8);
461}
462
463static void* read_file(efi_file_t* root, efi_char16_t* path, uint64_t* length) {
464 efi_file_t* file = 0;
465 if (root->open(root, &file, path, EFI_FILE_MODE_READ, 0) != EFI_SUCCESS)
466 return 0;
467 uint64_t size = file_size(file);
468 void* buffer = size ? allocate_pages(size + 1, 0xffffffffULL) : 0;
469 if (!buffer || file->read(file, &size, buffer) != EFI_SUCCESS) {
470 file->close(file);
471 return 0;
472 }
473 ((uint8_t*)buffer)[size] = 0;
474 file->close(file);
475 *length = size;
476 return buffer;
477}
478
479static int open_boot_filesystem(efi_loaded_image_t* loaded_image, efi_char16_t* probe_path,
480 efi_simple_file_system_t** filesystem_out, efi_file_t** root_out) {
481 efi_handle_t filesystem_handle = loaded_image->device_handle;
482 efi_simple_file_system_t* filesystem = 0;
483 efi_file_t* root = 0;
484 efi_file_t* probe = 0;
485
486 if (g_system_table->boot_services->handle_protocol(filesystem_handle,
487 (efi_guid_t*)&simple_file_system_guid,
488 (void**)&filesystem) == EFI_SUCCESS &&
489 filesystem->open_volume(filesystem, &root) == EFI_SUCCESS &&
490 root->open(root, &probe, probe_path, EFI_FILE_MODE_READ, 0) == EFI_SUCCESS) {
491 probe->close(probe);
492 *filesystem_out = filesystem;
493 *root_out = root;
494 return 1;
495 }
496 if (root)
497 root->close(root);
498
499 void* device_path = loaded_image->file_path;
500 if (g_system_table->boot_services->locate_device_path &&
501 g_system_table->boot_services->locate_device_path(
502 (efi_guid_t*)&simple_file_system_guid, &device_path, &filesystem_handle) == EFI_SUCCESS &&
503 g_system_table->boot_services->handle_protocol(filesystem_handle,
504 (efi_guid_t*)&simple_file_system_guid,
505 (void**)&filesystem) == EFI_SUCCESS &&
506 filesystem->open_volume(filesystem, &root) == EFI_SUCCESS &&
507 root->open(root, &probe, probe_path, EFI_FILE_MODE_READ, 0) == EFI_SUCCESS) {
508 probe->close(probe);
509 *filesystem_out = filesystem;
510 *root_out = root;
511 return 1;
512 }
513 if (root)
514 root->close(root);
515
516 // GRUB can chainload an EFI image without giving it the ESP's filesystem handle.
517 if (!g_system_table->boot_services->locate_handle_buffer)
518 return 0;
519 efi_handle_t* handles = 0;
520 uint64_t handle_count = 0;
521 if (g_system_table->boot_services->locate_handle_buffer(EFI_LOCATE_BY_PROTOCOL,
522 (efi_guid_t*)&simple_file_system_guid, 0,
523 &handle_count, &handles) != EFI_SUCCESS)
524 return 0;
525 for (uint64_t i = 0; i < handle_count; ++i) {
526 filesystem = 0;
527 root = 0;
528 if (g_system_table->boot_services->handle_protocol(handles[i],
529 (efi_guid_t*)&simple_file_system_guid,
530 (void**)&filesystem) != EFI_SUCCESS ||
531 filesystem->open_volume(filesystem, &root) != EFI_SUCCESS)
532 continue;
533 if (root->open(root, &probe, probe_path, EFI_FILE_MODE_READ, 0) == EFI_SUCCESS) {
534 probe->close(probe);
535 *filesystem_out = filesystem;
536 *root_out = root;
537 return 1;
538 }
539 root->close(root);
540 }
541 return 0;
542}
543
544static void enter_kernel(uint64_t entry, uint64_t info) __attribute__((noreturn));
545static void enter_kernel(uint64_t entry, uint64_t info) {
546 __asm__ volatile("cli\n\tmov %0, %%rbx\n\tjmp *%1" : : "r"(info), "r"(entry) : "memory");
547 for (;;)
548 __asm__ volatile("hlt");
549}
550
551efi_status_t efi_main(efi_handle_t image, efi_system_table_t* system_table) {
552 g_system_table = system_table;
553 print((efi_char16_t*)L"Pedigree UEFI loader\r\n");
554
555 efi_loaded_image_t* loaded_image = 0;
556 if (system_table->boot_services->handle_protocol(image, (efi_guid_t*)&loaded_image_guid,
557 (void**)&loaded_image) != EFI_SUCCESS) {
558 print((efi_char16_t*)L"UEFI: loaded-image protocol failed\r\n");
559 return 1;
560 }
561
563 if (!parse_load_options(loaded_image->load_options, loaded_image->load_options_size, &options)) {
564 print((efi_char16_t*)L"UEFI: invalid load options\r\n");
565 return 1;
566 }
567
568 static const efi_char16_t kernel_name[] = L"kernel";
569 static const efi_char16_t initrd_name[] = L"initrd.tar";
570 static const efi_char16_t cmdline_name[] = L"cmdline";
571 efi_char16_t kernel_path[256];
572 efi_char16_t initrd_path[256];
573 efi_char16_t cmdline_path[256];
574 if (!build_artifact_path(loaded_image, &options, kernel_name, kernel_path, 256) ||
575 !build_artifact_path(loaded_image, &options, initrd_name, initrd_path, 256) ||
576 !build_artifact_path(loaded_image, &options, cmdline_name, cmdline_path, 256)) {
577 print((efi_char16_t*)L"UEFI: loader path is too long\r\n");
578 return 1;
579 }
580 efi_simple_file_system_t* filesystem = 0;
581 efi_file_t* root = 0;
582 if (!open_boot_filesystem(loaded_image, kernel_path, &filesystem, &root)) {
583 print((efi_char16_t*)L"UEFI: filesystem protocol failed\r\n");
584 return 1;
585 }
586 uint64_t kernel_length = 0;
587 uint64_t initrd_length = 0;
588 uint64_t cmdline_length = 0;
589 uint8_t* kernel_file = (uint8_t*)read_file(root, kernel_path, &kernel_length);
590 uint8_t* initrd = (uint8_t*)read_file(root, initrd_path, &initrd_length);
591 uint8_t* cmdline = (uint8_t*)read_file(root, cmdline_path, &cmdline_length);
592 if (!kernel_file || !initrd || !cmdline) {
593 print((efi_char16_t*)L"UEFI: file read failed\r\n");
594 return 1;
595 }
596 if (options.arguments_length) {
597 char* combined = (char*)allocate_pages(UEFI_COMMAND_LINE_CAPACITY, 0xffffffffULL);
598 if (!combined || !append_load_options(cmdline, cmdline_length, &options, combined,
599 UEFI_COMMAND_LINE_CAPACITY)) {
600 print((efi_char16_t*)L"UEFI: invalid combined command line\r\n");
601 return 1;
602 }
603 cmdline = (uint8_t*)combined;
604 }
605 elf64_header_t* header = (elf64_header_t*)kernel_file;
606 if (kernel_length < sizeof(*header) || header->ident[0] != 0x7f || header->ident[1] != 'E' ||
607 header->ident[2] != 'L' || header->ident[3] != 'F' || header->ident[4] != 2 ||
608 header->machine != 0x3e) {
609 print((efi_char16_t*)L"UEFI: invalid ELF\r\n");
610 return 1;
611 }
612 print((efi_char16_t*)L"UEFI: loading kernel\r\n");
613
614 for (uint16_t i = 0; i < header->program_header_count; ++i) {
615 elf64_program_header_t* program =
616 (elf64_program_header_t*)(kernel_file + header->program_header_offset +
617 i * header->program_header_size);
618 if (program->type != PT_LOAD)
619 continue;
620 uint8_t* destination = (uint8_t*)allocate_at(program->physical_address, program->memory_size);
621 if (!destination) {
622 print((efi_char16_t*)L"UEFI: kernel allocation failed\r\n");
623 return 1;
624 }
625 copy(destination, kernel_file + program->offset, program->file_size);
626 if (program->memory_size > program->file_size)
627 zero(destination + program->file_size, program->memory_size - program->file_size);
628 }
629
630 if (header->section_header_offset && header->section_header_count) {
631 for (uint16_t i = 0; i < header->section_header_count; ++i) {
632 elf64_section_header_t* section =
633 (elf64_section_header_t*)(kernel_file + header->section_header_offset +
634 i * header->section_header_size);
635 if (!(section->flags & SHF_ALLOC) && section->size)
636 section->address = PHYS_ALIAS + (uint64_t)kernel_file + section->offset;
637 }
638 }
639
640 bootstrap_info_t* info = (bootstrap_info_t*)allocate_pages(4 * EFI_PAGE_SIZE, 0xffffffffULL);
641 void* memory_map = allocate_pages(128 * EFI_PAGE_SIZE, 0xffffffffULL);
642 void* normalized_memory_map = allocate_pages(128 * EFI_PAGE_SIZE, 0xffffffffULL);
643 if (!info || !memory_map || !normalized_memory_map) {
644 print((efi_char16_t*)L"UEFI: metadata allocation failed\r\n");
645 return 1;
646 }
647 bootstrap_module_t* modules = (bootstrap_module_t*)((uint8_t*)info + EFI_PAGE_SIZE);
648 zero(info, 4 * EFI_PAGE_SIZE);
649 modules[0].base = (uint64_t)initrd;
650 modules[0].end = modules[0].base + initrd_length;
651 info->flags =
652 BOOTSTRAP_FLAG_CMDLINE | BOOTSTRAP_FLAG_MODULES | BOOTSTRAP_FLAG_ELF | BOOTSTRAP_FLAG_UEFI;
653 info->module_count = 1;
654 info->modules = PHYS_ALIAS + (uint64_t)modules;
655 info->section_count = header->section_header_count;
656 info->section_entry_size = header->section_header_size;
657 info->section_string_index = header->section_string_index;
658 info->section_headers = PHYS_ALIAS + (uint64_t)kernel_file + header->section_header_offset;
659 info->command_line = PHYS_ALIAS + (uint64_t)cmdline;
660
661 for (uint64_t i = 0; i < system_table->configuration_table_entries; ++i) {
663 (efi_configuration_table_t*)system_table->configuration_table + i;
664 if (table->vendor_guid.a == acpi20_guid.a && table->vendor_guid.b == acpi20_guid.b &&
665 table->vendor_guid.c == acpi20_guid.c) {
666 info->flags |= BOOTSTRAP_FLAG_ACPI;
667 info->acpi_rsdp = PHYS_ALIAS + (uint64_t)table->vendor_table;
668 }
669 if (table->vendor_guid.a == smbios_guid.a && table->vendor_guid.b == smbios_guid.b &&
670 table->vendor_guid.c == smbios_guid.c) {
671 info->flags |= BOOTSTRAP_FLAG_SMBIOS;
672 info->smbios = PHYS_ALIAS + (uint64_t)table->vendor_table;
673 }
674 }
675
676 prepare_framebuffer(info);
677
678 uint32_t normalized_bytes = 0;
679 int exit_attempted = 0;
680 const efi_get_memory_map_t get_map =
681 (efi_get_memory_map_t)system_table->boot_services->get_memory_map;
682 const efi_exit_boot_services_t exit_services = system_table->boot_services->exit_boot_services;
683 const efi_status_t exit_status =
684 exit_boot_services_with_map(get_map, exit_services, image, memory_map, 128 * EFI_PAGE_SIZE,
685 (bootstrap_memory_map_entry_t*)normalized_memory_map,
686 128 * EFI_PAGE_SIZE, &normalized_bytes, &exit_attempted);
687 if (exit_status != EFI_SUCCESS) {
688 if (!exit_attempted) {
689 print((efi_char16_t*)L"UEFI: invalid or unavailable memory map\r\n");
690 return exit_status;
691 }
692 // Returning or using console protocols is unsafe after a partial firmware exit.
693 __asm__ volatile("cli" ::: "memory");
694 for (;;)
695 __asm__ volatile("hlt");
696 }
697 info->flags |= BOOTSTRAP_FLAG_MEMORY_MAP;
698 info->memory_map = PHYS_ALIAS + (uint64_t)normalized_memory_map;
699 info->memory_map_length = normalized_bytes;
700 info->memory_map_entry_size = sizeof(bootstrap_memory_map_entry_t);
701
702 enter_kernel(header->entry - KERNEL_BASE, PHYS_ALIAS + (uint64_t)info);
703}