mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2025-02-25 18:55:23 -06:00
Add support for 64-bit EFI handover boot protocol.
This commit is contained in:
parent
9492c086fe
commit
a2110cb97f
247
boot/efi.h
Normal file
247
boot/efi.h
Normal file
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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 */
|
538
boot/efiboot.c
Normal file
538
boot/efiboot.c
Normal file
@ -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 <stdbool.h>
|
||||||
|
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
}
|
@ -74,8 +74,8 @@ relocatable_kernel:
|
|||||||
min_alignment:
|
min_alignment:
|
||||||
.byte 0
|
.byte 0
|
||||||
xload_flags:
|
xload_flags:
|
||||||
#ifdef __X86_64__
|
#ifdef __x86_64__
|
||||||
.word 1 # XLF_KERNEL_64
|
.word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64
|
||||||
#else
|
#else
|
||||||
.word 0
|
.word 0
|
||||||
#endif
|
#endif
|
||||||
@ -96,7 +96,7 @@ pref_address:
|
|||||||
init_size:
|
init_size:
|
||||||
.long _initsize
|
.long _initsize
|
||||||
handover_offset:
|
handover_offset:
|
||||||
.long 0
|
.long 0x10
|
||||||
|
|
||||||
do_setup:
|
do_setup:
|
||||||
# Reload the segment registers, except for the stack.
|
# Reload the segment registers, except for the stack.
|
||||||
|
@ -101,11 +101,11 @@ startup32:
|
|||||||
movl %eax, -6(%esp)
|
movl %eax, -6(%esp)
|
||||||
ljmp *-6(%esp)
|
ljmp *-6(%esp)
|
||||||
|
|
||||||
.org 0x200
|
|
||||||
.code64
|
.code64
|
||||||
|
|
||||||
# The Linux 64-bit boot entry point.
|
# The Linux 64-bit boot entry point.
|
||||||
|
|
||||||
|
.org 0x200
|
||||||
.globl startup64
|
.globl startup64
|
||||||
startup64:
|
startup64:
|
||||||
cld
|
cld
|
||||||
@ -115,6 +115,20 @@ startup64:
|
|||||||
|
|
||||||
movq %rsi, boot_params_addr(%rip)
|
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.
|
# The 64-bit entry point for AP boot and for restart after relocation.
|
||||||
|
|
||||||
.globl startup
|
.globl startup
|
||||||
|
@ -44,7 +44,7 @@ APP_OBJS = app/badram.o \
|
|||||||
app/interrupt.o \
|
app/interrupt.o \
|
||||||
app/main.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
|
all: memtest.bin
|
||||||
|
|
||||||
@ -64,6 +64,10 @@ boot/%.s: ../boot/%.S ../boot/boot.h
|
|||||||
@mkdir -p boot
|
@mkdir -p boot
|
||||||
$(CC) -E -traditional -I../boot -o $@ $<
|
$(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
|
system/reloc.o: ../system/reloc64.c
|
||||||
@mkdir -p system
|
@mkdir -p system
|
||||||
$(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
$(CC) -c $(CFLAGS) -fno-strict-aliasing -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||||
|
Loading…
Reference in New Issue
Block a user