diff --git a/boot/efi.h b/boot/efi.h new file mode 100644 index 0000000..69fcebf --- /dev/null +++ b/boot/efi.h @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef EFI_H +#define EFI_H +/* + * Provides definitions for accessing the UEFI boot services and configuration + * tables. + * + * Copyright (C) 2020 Martin Whitaker. + */ + +#include + +#ifdef __x86_64__ +#define NATIVE_MSB UINT64_C(0x8000000000000000) +#else +#define NATIVE_MSB 0x80000000 +#endif + +/* + * EFI_STATUS values. + */ +#define EFI_SUCCESS 0 +#define EFI_INVALID_PARAMETER (NATIVE_MSB | 2) +#define EFI_UNSUPPORTED (NATIVE_MSB | 3) +#define EFI_BUFFER_TOO_SMALL (NATIVE_MSB | 5) +#define EFI_NOT_FOUND (NATIVE_MSB | 14) +#define EFI_ABORTED (NATIVE_MSB | 21) + +/* + * EFI_LOCATE_SEARCH_TYPE values. + */ +#define EFI_LOCATE_BY_PROTOCOL 2 + +/* + * EFI_ALLOCATE_TYPE values. + */ +#define EFI_ALLOCATE_ADDRESS 2 + +/* + * EFI_MEMORY_TYPE values. + */ +#define EFI_LOADER_CODE 1 +#define EFI_LOADER_DATA 2 +#define EFI_BOOT_SERVICES_CODE 3 +#define EFI_BOOT_SERVICES_DATA 4 +#define EFI_CONVENTIONAL_MEMORY 7 +#define EFI_ACPI_RECLAIM_MEMORY 9 + +/* + * EFI_GRAPHICS_PIXEL_FORMAT values. + */ +#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 +#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 +#define PIXEL_BIT_MASK 2 +#define PIXEL_BLT_ONLY 3 + +#define EFI_SYSTEM_TABLE_SIGNATURE UINT64_C(0x5453595320494249) + +#define efiapi __attribute__((ms_abi)) + +#ifdef __x86_64__ +typedef uint64_t uintn_t; +#else +typedef uint32_t uintn_t; +#endif + +typedef void * efi_handle_t; +typedef uintn_t efi_status_t; +typedef uint64_t efi_phys_addr_t; +typedef uint64_t efi_virt_addr_t; +typedef uint16_t efi_char16_t; + +typedef struct { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[8]; +} efi_guid_t; + +typedef struct { + uint32_t type; + uint32_t pad; + efi_phys_addr_t phys_addr; + efi_virt_addr_t virt_addr; + uint64_t num_pages; + uint64_t attribute; +} efi_memory_desc_t; + +typedef struct { + uint32_t red_mask; + uint32_t green_mask; + uint32_t blue_mask; + uint32_t rsvd_mask; +} efi_pixel_bitmask_t; + +typedef struct { + uint32_t version; + uint32_t h_resolution; + uint32_t v_resolution; + int pixel_format; + efi_pixel_bitmask_t pixel_info; + uint32_t pixels_per_scan_line; +} efi_gop_mode_info_t; + +typedef struct { + uint32_t max_mode; + uint32_t mode; + efi_gop_mode_info_t *info; + uintn_t info_size; + efi_phys_addr_t frame_buffer_base; + uintn_t frame_buffer_size; +} efi_gop_mode_t; + +typedef struct { + void *query_mode; + void *set_mode; + void *blt; + efi_gop_mode_t *mode; +} efi_graphics_output_protocol_t; + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t header_size; + uint32_t crc32; + uint32_t reserved; +} efi_table_header_t; + +typedef struct efi_simple_text_out_s { + void *reset; + efi_status_t (efiapi *output_string)(struct efi_simple_text_out_s *, efi_char16_t *); + void *test_string; +} efi_simple_text_out_t; + +typedef struct { + efi_table_header_t header; + void *raise_tpl; + void *restore_tpl; + efi_status_t (efiapi *allocate_pages)(int, int, uintn_t, efi_phys_addr_t *); + efi_status_t (efiapi *free_pages)(efi_phys_addr_t, uintn_t); + efi_status_t (efiapi *get_memory_map)(uintn_t *, void *, uintn_t *, uintn_t *, uint32_t *); + efi_status_t (efiapi *allocate_pool)(int, uintn_t, void **); + efi_status_t (efiapi *free_pool)(void *); + 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_t (efiapi *handle_protocol)(efi_handle_t, efi_guid_t *, void **); + void *reserved; + void *register_protocol_notify; + efi_status_t (efiapi *locate_handle)(int, efi_guid_t *, void *, uintn_t *, efi_handle_t *); + void *locate_device_path; + efi_status_t (efiapi *install_configuration_table)(efi_guid_t *, void *); + void *load_image; + void *start_image; + void *exit; + void *unload_image; + efi_status_t (efiapi *exit_boot_services)(efi_handle_t, uintn_t); + void *get_next_monotonic_count; + void *stall; + void *set_watchdog_timer; + void *connect_controller; + efi_status_t (efiapi *disconnect_controller)(efi_handle_t, efi_handle_t, efi_handle_t); + void *open_protocol; + void *close_protocol; + void *open_protocol_information; + void *protocols_per_handle; + void *locate_handle_buffer; + efi_status_t (efiapi *locate_protocol)(efi_guid_t *, void *, void **); + void *install_multiple_protocol_interfaces; + void *uninstall_multiple_protocol_interfaces; + void *calculate_crc32; + void *copy_mem; + void *set_mem; + void *create_event_ex; +} efi_boot_services_t; + +typedef struct { + efi_guid_t guid; + uint32_t table; +} efi32_config_table_t; + +typedef struct { + efi_guid_t guid; + uint64_t table; +} efi64_config_table_t; + +typedef struct { + efi_guid_t guid; + void *table; +} efi_config_table_t; + +typedef struct { + efi_table_header_t header; + uint32_t fw_vendor; + uint32_t fw_revision; + uint32_t con_in_handle; + uint32_t con_in; + uint32_t con_out_handle; + uint32_t con_out; + uint32_t std_err_handle; + uint32_t std_err; + uint32_t runtime_services; + uint32_t boot_services; + uint32_t num_config_tables; + uint32_t config_tables; +} efi32_system_table_t; + +typedef struct { + efi_table_header_t header; + uint64_t fw_vendor; + uint32_t fw_revision; + uint32_t pad; + uint64_t con_in_handle; + uint64_t con_in; + uint64_t con_out_handle; + uint64_t con_out; + uint64_t std_err_handle; + uint64_t std_err; + uint64_t runtime_services; + uint64_t boot_services; + uint64_t num_config_tables; + uint64_t config_tables; +} efi64_system_table_t; + +typedef struct { + efi_table_header_t header; + efi_char16_t *fw_vendor; + uint32_t fw_revision; + efi_handle_t con_in_handle; + void *con_in; + efi_handle_t con_out_handle; + efi_simple_text_out_t *con_out; + efi_handle_t std_err_handle; + efi_simple_text_out_t *std_err; + void *runtime_services; + efi_boot_services_t *boot_services; + uintn_t num_config_tables; + efi_config_table_t *config_tables; +} efi_system_table_t; + +#endif /* EFI_H */ diff --git a/boot/efiboot.c b/boot/efiboot.c new file mode 100644 index 0000000..67d9a20 --- /dev/null +++ b/boot/efiboot.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2020 Martin Whitaker. +// +// Derived from Linux 5.6 arch/x86/boot/compressed/eboot.c and extracts +// from drivers/firmware/efi/libstub: +// +// Copyright 2011 Intel Corporation; author Matt Fleming + +#include + +#include "boot.h" +#include "bootparams.h" +#include "efi.h" + +#include "memsize.h" + +#include "string.h" + +//------------------------------------------------------------------------------ +// Constants +//------------------------------------------------------------------------------ + +#define MAP_BUFFER_HEADROOM 8 // number of descriptors + +//------------------------------------------------------------------------------ +// Private Variables +//------------------------------------------------------------------------------ + +static efi_guid_t EFI_CONSOLE_OUT_DEVICE_GUID = { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} }; +static efi_guid_t EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID = { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a} }; + +static efi_system_table_t *sys_table; + +//------------------------------------------------------------------------------ +// Macro Functions +//------------------------------------------------------------------------------ + +#define round_up(value, align) \ + (((value) + (align) - 1) & ~((align) - 1)) + +// The following macros are used in Linux to hide differences in mixed mode. +// For now, just support native mode. + +#define efi_table_attr(table, attr) \ + table->attr + +#define efi_call_proto(proto, func, ...) \ + proto->func(proto, ##__VA_ARGS__) + +#define efi_call_bs(func, ...) \ + sys_table->boot_services->func(__VA_ARGS__) + +#define efi_get_num_handles(size) \ + (int)((size) / sizeof(efi_handle_t)) + +#define efi_get_handle_at(array, index) \ + (array)[index] + +//------------------------------------------------------------------------------ +// Private Functions +//------------------------------------------------------------------------------ + +static void print_unicode_string(efi_char16_t *str) +{ + efi_call_proto(efi_table_attr(sys_table, con_out), output_string, str); +} + +static void print_string(char *str) +{ + char *s8; + + for (s8 = str; *s8; s8++) { + efi_char16_t ch[2] = { 0 }; + + ch[0] = *s8; + if (*s8 == '\n') { + efi_char16_t cr[2] = { '\r', 0 }; + print_unicode_string(cr); + } + + print_unicode_string(ch); + } +} + +#ifdef DEBUG +static void print_dec(unsigned value) +{ + char buffer[16]; + char *str = &buffer[15]; + *str = '\0'; + do { + str--; + *str = '0' + value % 10; + value /= 10; + } while (value > 0); + print_string(str); +} + +static void print_hex(uintptr_t value) +{ + char buffer[32]; + char *str = &buffer[31]; + *str = '\0'; + do { + str--; + *str = '0' + value % 16; + if (*str > '9') *str += 'a' - '0' - 10; + value /= 16; + } while (value > 0); + print_string(str); +} +#endif + +static efi_memory_desc_t *get_memory_desc(uintptr_t map_addr, size_t desc_size, size_t n) +{ + return (efi_memory_desc_t *)(map_addr + n * desc_size); +} + +static bool map_buffer_has_headroom(size_t buffer_size, size_t map_size, size_t desc_size) +{ + size_t slack = buffer_size - map_size; + + return slack / desc_size >= MAP_BUFFER_HEADROOM; +} + +static efi_status_t get_memory_map( + efi_memory_desc_t **mem_map, + uintn_t *mem_map_size, + uintn_t *mem_desc_size, + uint32_t *mem_desc_version, + uintn_t *mem_map_key, + uintn_t *map_buffer_size +) +{ + efi_status_t status; + + *mem_map = NULL; + + *map_buffer_size = *mem_map_size = 32 * sizeof(efi_memory_desc_t); // for first try + +again: + status = efi_call_bs(allocate_pool, EFI_LOADER_DATA, *map_buffer_size, (void **)mem_map); + if (status != EFI_SUCCESS) { + goto fail; + } + + status = efi_call_bs(get_memory_map, mem_map_size, *mem_map, mem_map_key, mem_desc_size, mem_desc_version); + if (status == EFI_BUFFER_TOO_SMALL || !map_buffer_has_headroom(*map_buffer_size, *mem_map_size, *mem_desc_size)) { + efi_call_bs(free_pool, *mem_map); + // Make sure there is some headroom so that the buffer can be reused + // for a new map after allocations are no longer permitted. It's + // unlikely that the map will grow to exceed this headroom once we + // are ready to trigger ExitBootServices(). + *mem_map_size += *mem_desc_size * MAP_BUFFER_HEADROOM; + *map_buffer_size = *mem_map_size; + goto again; + } + if (status != EFI_SUCCESS) { + efi_call_bs(free_pool, *mem_map); + goto fail; + } + +fail: + return status; +} + +static efi_status_t alloc_low_memory(void **ptr, size_t size, efi_phys_addr_t min_addr) +{ + efi_status_t status; + + efi_memory_desc_t *mem_map = NULL; + uintn_t mem_map_size = 0; + uintn_t mem_desc_size = 0; + uint32_t mem_desc_version = 0; + uintn_t mem_map_key = 0; + uintn_t map_buffer_size = 0; + + status = get_memory_map(&mem_map, &mem_map_size, &mem_desc_size, &mem_desc_version, &mem_map_key, &map_buffer_size); + if (status != EFI_SUCCESS) { + goto fail; + } + + size_t num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + size_t num_descs = mem_map_size / mem_desc_size; + + for (size_t i = 0; i < num_descs; i++) { + efi_memory_desc_t *desc = get_memory_desc((uintptr_t)mem_map, mem_desc_size, i); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) { + continue; + } + if (desc->num_pages < num_pages) { + continue; + } + + efi_phys_addr_t start = desc->phys_addr; + efi_phys_addr_t end = start + desc->num_pages * PAGE_SIZE; + + if (start < min_addr) { + start = min_addr; + } + start = round_up(start, PAGE_SIZE); + if ((start + size) > end) { + continue; + } + + status = efi_call_bs(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, num_pages, &start); + if (status == EFI_SUCCESS) { + *ptr = (void *)start; + efi_call_bs(free_pool, mem_map); + return EFI_SUCCESS; + } + } + efi_call_bs(free_pool, mem_map); + status = EFI_NOT_FOUND; + +fail: + return status; +} + +static void get_bit_range(uint32_t mask, uint8_t *pos, uint8_t *size) +{ + int first = 0; + int length = 0; + + if (mask) { + while (!(mask & 0x1)) { + mask >>= 1; + first++; + } + while (mask & 0x1) { + mask >>= 1; + length++; + } + } + *pos = first; + *size = length; +} + +static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *handles, size_t handles_size) +{ + efi_status_t status; + + efi_graphics_output_protocol_t *gop = NULL; + for (int i = 0; i < efi_get_num_handles(handles_size); i++) { + efi_handle_t handle = efi_get_handle_at(handles, i); + + efi_graphics_output_protocol_t *current_gop = NULL; + status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)¤t_gop); + if (status != EFI_SUCCESS) { + continue; + } + + void *con_out = NULL; + status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out); + + efi_gop_mode_t *current_mode = efi_table_attr(current_gop, mode); + efi_gop_mode_info_t *current_info = efi_table_attr(current_mode, info); + + // Systems that use the UEFI Console Splitter may provide multiple GOP + // devices, not all of which are backed by real hardware. The workaround + // is to search for a GOP implementing the ConOut protocol, and if one + // isn't found, to just fall back to the first GOP. + if ((!gop || con_out) && current_info->pixel_format != PIXEL_BLT_ONLY) { + gop = current_gop; + if (con_out) { + break; + } + } + } + if (!gop) { + return EFI_NOT_FOUND; + } + + efi_gop_mode_t *mode = efi_table_attr(gop, mode); + efi_gop_mode_info_t *info = efi_table_attr(mode, info); + + efi_phys_addr_t lfb_base = efi_table_attr(mode, frame_buffer_base); + + si->orig_video_isVGA = VIDEO_TYPE_EFI; + + si->lfb_width = info->h_resolution; + si->lfb_height = info->v_resolution; + si->lfb_base = lfb_base; +#ifdef __x86_64__ + if (lfb_base >> 32) { + si->capabilities |= LFB_CAPABILITY_64BIT_BASE; + si->ext_lfb_base = lfb_base >> 32; + } +#endif + + switch (info->pixel_format) { + case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: + si->lfb_depth = 32; + si->lfb_linelength = info->pixels_per_scan_line * 4; + si->red_size = 8; + si->red_pos = 0; + si->green_size = 8; + si->green_pos = 8; + si->blue_size = 8; + si->blue_pos = 16; + si->rsvd_size = 8; + si->rsvd_pos = 24; + break; + case PIXEL_BGR_RESERVED_8BIT_PER_COLOR: + si->lfb_depth = 32; + si->lfb_linelength = info->pixels_per_scan_line * 4; + si->red_size = 8; + si->red_pos = 16; + si->green_size = 8; + si->green_pos = 8; + si->blue_size = 8; + si->blue_pos = 0; + si->rsvd_size = 8; + si->rsvd_pos = 24; + break; + case PIXEL_BIT_MASK: + si->lfb_depth = si->red_size + si->green_size + si->blue_size + si->rsvd_size; + si->lfb_linelength = (info->pixels_per_scan_line * si->lfb_depth) / 8; + get_bit_range(info->pixel_info.red_mask, &si->red_pos, &si->red_size); + get_bit_range(info->pixel_info.green_mask, &si->green_pos, &si->green_size); + get_bit_range(info->pixel_info.blue_mask, &si->blue_pos, &si->blue_size); + get_bit_range(info->pixel_info.rsvd_mask, &si->rsvd_pos, &si->rsvd_size); + break; + default: + si->lfb_depth = 4; + si->lfb_linelength = si->lfb_width / 2; + si->red_size = 0; + si->red_pos = 0; + si->green_size = 0; + si->green_pos = 0; + si->blue_size = 0; + si->blue_pos = 0; + si->rsvd_size = 0; + si->rsvd_pos = 0; + break; + } + si->lfb_size = si->lfb_linelength * si->lfb_height; + + return EFI_SUCCESS; +} + +static efi_status_t set_screen_info(boot_params_t *boot_params) +{ + efi_status_t status; + + uintn_t handles_size = 0; + efi_handle_t *handles = NULL; + status = efi_call_bs(locate_handle, EFI_LOCATE_BY_PROTOCOL, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, + &handles_size, handles); + if (status == EFI_BUFFER_TOO_SMALL) { + status = efi_call_bs(allocate_pool, EFI_LOADER_DATA, handles_size, (void **)&handles); + if (status != EFI_SUCCESS) { + return status; + } + status = efi_call_bs(locate_handle, EFI_LOCATE_BY_PROTOCOL, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, + &handles_size, handles); + if (status == EFI_SUCCESS) { + status = set_screen_info_from_gop(&boot_params->screen_info, handles, handles_size); + } + efi_call_bs(free_pool, handles); + } + + return status; +} + +static efi_status_t set_efi_info_and_exit_boot_services(efi_handle_t handle, boot_params_t *boot_params) +{ + efi_status_t status; + + efi_memory_desc_t *mem_map = NULL; + uintn_t mem_map_size = 0; + uintn_t mem_desc_size = 0; + uint32_t mem_desc_version = 0; + uintn_t mem_map_key = 0; + uintn_t map_buffer_size = 0; + + status = get_memory_map(&mem_map, &mem_map_size, &mem_desc_size, &mem_desc_version, &mem_map_key, &map_buffer_size); + if (status != EFI_SUCCESS) { + goto fail; + } + + status = efi_call_bs(exit_boot_services, handle, mem_map_key); + if (status == EFI_INVALID_PARAMETER) { + // The memory map changed between efi_get_memory_map() and + // exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: + // EFI_BOOT_SERVICES.ExitBootServices, we need to get the + // updated map, and try again. The spec implies one retry + // should be sufficent, which is confirmed against the EDK2 + // implementation. Per the spec, we can only invoke + // get_memory_map() and exit_boot_services() - we cannot alloc + // so efi_get_memory_map() cannot be used, and we must reuse + // the buffer. For all practical purposes, the headroom in the + // buffer should account for any changes in the map so the call + // to get_memory_map() is expected to succeed here. + mem_map_size = map_buffer_size; + status = efi_call_bs(get_memory_map, &mem_map_size, mem_map, &mem_map_key, &mem_desc_size, &mem_desc_version); + if (status != EFI_SUCCESS) { + goto fail; + } + + status = efi_call_bs(exit_boot_services, handle, mem_map_key); + } + if (status != EFI_SUCCESS) { + goto fail; + } + +#ifdef __x86_64 + boot_params->efi_info.loader_signature = EFI64_LOADER_SIGNATURE; +#else + boot_params->efi_info.loader_signature = EFI32_LOADER_SIGNATURE; +#endif + boot_params->efi_info.sys_tab = (uintptr_t)sys_table; + boot_params->efi_info.mem_desc_size = mem_desc_size; + boot_params->efi_info.mem_desc_version = mem_desc_version; + boot_params->efi_info.mem_map = (uintptr_t)mem_map; + boot_params->efi_info.mem_map_size = mem_map_size; +#ifdef __X86_64__ + boot_params->efi_info.sys_tab_hi = (uintptr_t)sys_table >> 32; + boot_params->efi_info.mem_map_hi = (uintptr_t)mem_map >> 32; +#endif + +fail: + return status; +} + +static void set_e820_map(boot_params_t *params) +{ + uintptr_t mem_map_addr = params->efi_info.mem_map; +#ifdef __X86_64__ + mem_map_addr |= (uintptr_t)params->efi_info.mem_map_hi << 32; +#endif + size_t mem_map_size = params->efi_info.mem_map_size; + size_t mem_desc_size = params->efi_info.mem_desc_size; + size_t num_descs = mem_map_size /mem_desc_size; + + e820_entry_t *prev = NULL; + e820_entry_t *next = params->e820_map; + + int num_entries = 0; + for (size_t i = 0; i < num_descs && num_entries < E820_MAP_SIZE; i++) { + efi_memory_desc_t *mem_desc = get_memory_desc(mem_map_addr, mem_desc_size, i); + + e820_type_t e820_type = E820_NONE; + switch (mem_desc->type) { + case EFI_ACPI_RECLAIM_MEMORY: + e820_type = E820_ACPI; + break; + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: + e820_type = E820_RAM; + break; + default: + continue; + } + + // Merge adjacent mappings. + if (prev && prev->type == e820_type && (prev->addr + prev->size) == mem_desc->phys_addr) { + prev->size += mem_desc->num_pages << PAGE_SHIFT; + continue; + } + + next->addr = mem_desc->phys_addr; + next->size = mem_desc->num_pages << PAGE_SHIFT; + next->type = e820_type; + prev = next++; + num_entries++; + } + params->e820_entries = num_entries; +} + +//------------------------------------------------------------------------------ +// Public Functions +//------------------------------------------------------------------------------ + +void efi_handover(efi_handle_t handle, efi_system_table_t *sys_table, boot_params_t *boot_params) __attribute__((noreturn)); + +efi_status_t efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) +{ + // Check if we were booted by the EFI firmware. + if (sys_table->header.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + print_string("bad system table signature\n"); + return EFI_INVALID_PARAMETER; + } + + sys_table = sys_table_arg; + + boot_params_t *boot_params = NULL; + efi_status_t status = alloc_low_memory((void **)&boot_params, sizeof(boot_params_t), 0); + if (status != EFI_SUCCESS) { + print_string("failed to allocate low memory for boot params\n"); + return status; + } + + memset(boot_params, 0, sizeof(boot_params_t)); + + efi_handover(handle, sys_table, boot_params); + // should never return + + return EFI_ABORTED; +} + +boot_params_t *efi_main(efi_handle_t handle, efi_system_table_t *sys_table_arg, boot_params_t *boot_params) +{ + efi_status_t status; + + sys_table = sys_table_arg; + if (sys_table->header.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + print_string("bad system table signature\n"); + goto fail; + } + + status = set_screen_info(boot_params); + if (status != EFI_SUCCESS) { + print_string("set_screen_info() failed\n"); + goto fail; + } + + status = set_efi_info_and_exit_boot_services(handle, boot_params); + if (status != EFI_SUCCESS) { + print_string("set_efi_info_and_exit_boot_services() failed\n"); + goto fail; + } + + set_e820_map(boot_params); + + return boot_params; + +fail: + print_string("efi_main() failed\n"); + + while (1) { + __asm__("hlt"); + } +} diff --git a/boot/setup.S b/boot/setup.S index 03f7c33..5b6e319 100644 --- a/boot/setup.S +++ b/boot/setup.S @@ -74,8 +74,8 @@ relocatable_kernel: min_alignment: .byte 0 xload_flags: -#ifdef __X86_64__ - .word 1 # XLF_KERNEL_64 +#ifdef __x86_64__ + .word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64 #else .word 0 #endif @@ -96,7 +96,7 @@ pref_address: init_size: .long _initsize handover_offset: - .long 0 + .long 0x10 do_setup: # Reload the segment registers, except for the stack. diff --git a/boot/startup64.S b/boot/startup64.S index 0d4443a..1d7e981 100644 --- a/boot/startup64.S +++ b/boot/startup64.S @@ -101,11 +101,11 @@ startup32: movl %eax, -6(%esp) ljmp *-6(%esp) - .org 0x200 .code64 # The Linux 64-bit boot entry point. + .org 0x200 .globl startup64 startup64: cld @@ -115,6 +115,20 @@ startup64: movq %rsi, boot_params_addr(%rip) + jmp startup + +# The Linux 64-bit EFI handover point. + + .org 0x210 + .globl efi_handover +efi_handover: + andq $~0xf, %rsp + call efi_main + + # Save the boot params pointer. + + movq %rax, boot_params_addr(%rip) + # The 64-bit entry point for AP boot and for restart after relocation. .globl startup diff --git a/build64/Makefile b/build64/Makefile index e58ad2a..3bff39e 100644 --- a/build64/Makefile +++ b/build64/Makefile @@ -44,7 +44,7 @@ APP_OBJS = app/badram.o \ app/interrupt.o \ app/main.o -OBJS = boot/startup.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) +OBJS = boot/startup.o boot/efiboot.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin @@ -64,6 +64,10 @@ boot/%.s: ../boot/%.S ../boot/boot.h @mkdir -p boot $(CC) -E -traditional -I../boot -o $@ $< +boot/efiboot.o: ../boot/efiboot.c + @mkdir -p boot + $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) + system/reloc.o: ../system/reloc64.c @mkdir -p system $(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)