From 6e7a315eb67cb6c113cf37e1d66c4f11a51a2b3e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:29:51 +0200 Subject: Adding upstream version 2.06. Signed-off-by: Daniel Baumann --- grub-core/kern/efi/acpi.c | 59 +++ grub-core/kern/efi/efi.c | 1017 +++++++++++++++++++++++++++++++++++++++++++++ grub-core/kern/efi/fdt.c | 43 ++ grub-core/kern/efi/init.c | 149 +++++++ grub-core/kern/efi/mm.c | 691 ++++++++++++++++++++++++++++++ grub-core/kern/efi/sb.c | 188 +++++++++ 6 files changed, 2147 insertions(+) create mode 100644 grub-core/kern/efi/acpi.c create mode 100644 grub-core/kern/efi/efi.c create mode 100644 grub-core/kern/efi/fdt.c create mode 100644 grub-core/kern/efi/init.c create mode 100644 grub-core/kern/efi/mm.c create mode 100644 grub-core/kern/efi/sb.c (limited to 'grub-core/kern/efi') diff --git a/grub-core/kern/efi/acpi.c b/grub-core/kern/efi/acpi.c new file mode 100644 index 0000000..74f8cd1 --- /dev/null +++ b/grub-core/kern/efi/acpi.c @@ -0,0 +1,59 @@ +/* acpi.c - get acpi tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include + +struct grub_acpi_rsdp_v10 * +grub_machine_acpi_get_rsdpv1 (void) +{ + unsigned i; + static grub_efi_packed_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_acpi_rsdp_v10 *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} + +struct grub_acpi_rsdp_v20 * +grub_machine_acpi_get_rsdpv2 (void) +{ + unsigned i; + static grub_efi_packed_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_efi_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, &acpi20_guid, sizeof (grub_efi_packed_guid_t))) + return (struct grub_acpi_rsdp_v20 *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + return 0; +} diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c new file mode 100644 index 0000000..8cff7be --- /dev/null +++ b/grub-core/kern/efi/efi.c @@ -0,0 +1,1017 @@ +/* efi.c - generic EFI support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The handle of GRUB itself. Filled in by the startup code. */ +grub_efi_handle_t grub_efi_image_handle; + +/* The pointer to a system table. Filled in by the startup code. */ +grub_efi_system_table_t *grub_efi_system_table; + +static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; +static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; +static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; + +void * +grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration) +{ + void *interface; + grub_efi_status_t status; + + status = efi_call_3 (grub_efi_system_table->boot_services->locate_protocol, + protocol, registration, &interface); + if (status != GRUB_EFI_SUCCESS) + return 0; + + return interface; +} + +/* Return the array of handles which meet the requirement. If successful, + the number of handles is stored in NUM_HANDLES. The array is allocated + from the heap. */ +grub_efi_handle_t * +grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, + grub_efi_guid_t *protocol, + void *search_key, + grub_efi_uintn_t *num_handles) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_handle_t *buffer; + grub_efi_uintn_t buffer_size = 8 * sizeof (grub_efi_handle_t); + + buffer = grub_malloc (buffer_size); + if (! buffer) + return 0; + + b = grub_efi_system_table->boot_services; + status = efi_call_5 (b->locate_handle, search_type, protocol, search_key, + &buffer_size, buffer); + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (buffer); + buffer = grub_malloc (buffer_size); + if (! buffer) + return 0; + + status = efi_call_5 (b->locate_handle, search_type, protocol, search_key, + &buffer_size, buffer); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (buffer); + return 0; + } + + *num_handles = buffer_size / sizeof (grub_efi_handle_t); + return buffer; +} + +void * +grub_efi_open_protocol (grub_efi_handle_t handle, + grub_efi_guid_t *protocol, + grub_efi_uint32_t attributes) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + void *interface; + + b = grub_efi_system_table->boot_services; + status = efi_call_6 (b->open_protocol, handle, + protocol, + &interface, + grub_efi_image_handle, + 0, + attributes); + if (status != GRUB_EFI_SUCCESS) + return 0; + + return interface; +} + +int +grub_efi_set_text_mode (int on) +{ + grub_efi_console_control_protocol_t *c; + grub_efi_screen_mode_t mode, new_mode; + + c = grub_efi_locate_protocol (&console_control_guid, 0); + if (! c) + /* No console control protocol instance available, assume it is + already in text mode. */ + return 1; + + if (efi_call_4 (c->get_mode, c, &mode, 0, 0) != GRUB_EFI_SUCCESS) + return 0; + + new_mode = on ? GRUB_EFI_SCREEN_TEXT : GRUB_EFI_SCREEN_GRAPHICS; + if (mode != new_mode) + if (efi_call_2 (c->set_mode, c, new_mode) != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +void +grub_efi_stall (grub_efi_uintn_t microseconds) +{ + efi_call_1 (grub_efi_system_table->boot_services->stall, microseconds); +} + +grub_efi_loaded_image_t * +grub_efi_get_loaded_image (grub_efi_handle_t image_handle) +{ + return grub_efi_open_protocol (image_handle, + &loaded_image_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + +void +grub_reboot (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN | + GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY); + efi_call_4 (grub_efi_system_table->runtime_services->reset_system, + GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL); + for (;;) ; +} + +void +grub_exit (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); + efi_call_4 (grub_efi_system_table->boot_services->exit, + grub_efi_image_handle, GRUB_EFI_SUCCESS, 0, 0); + for (;;) ; +} + +grub_err_t +grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size, + grub_efi_uintn_t descriptor_size, + grub_efi_uint32_t descriptor_version, + grub_efi_memory_descriptor_t *virtual_map) +{ + grub_efi_runtime_services_t *r; + grub_efi_status_t status; + + r = grub_efi_system_table->runtime_services; + status = efi_call_4 (r->set_virtual_address_map, memory_map_size, + descriptor_size, descriptor_version, virtual_map); + + if (status == GRUB_EFI_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_IO, "set_virtual_address_map failed"); +} + +grub_err_t +grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid, + void *data, grub_size_t datasize) +{ + grub_efi_status_t status; + grub_efi_runtime_services_t *r; + grub_efi_char16_t *var16; + grub_size_t len, len16; + + len = grub_strlen (var); + len16 = len * GRUB_MAX_UTF16_PER_UTF8; + var16 = grub_calloc (len16 + 1, sizeof (var16[0])); + if (!var16) + return grub_errno; + len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL); + var16[len16] = 0; + + r = grub_efi_system_table->runtime_services; + + status = efi_call_5 (r->set_variable, var16, guid, + (GRUB_EFI_VARIABLE_NON_VOLATILE + | GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS + | GRUB_EFI_VARIABLE_RUNTIME_ACCESS), + datasize, data); + grub_free (var16); + if (status == GRUB_EFI_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); +} + +grub_efi_status_t +grub_efi_get_variable_with_attributes (const char *var, + const grub_efi_guid_t *guid, + grub_size_t *datasize_out, + void **data_out, + grub_efi_uint32_t *attributes) +{ + grub_efi_status_t status; + grub_efi_uintn_t datasize = 0; + grub_efi_runtime_services_t *r; + grub_efi_char16_t *var16; + void *data; + grub_size_t len, len16; + + *data_out = NULL; + *datasize_out = 0; + + len = grub_strlen (var); + len16 = len * GRUB_MAX_UTF16_PER_UTF8; + var16 = grub_calloc (len16 + 1, sizeof (var16[0])); + if (!var16) + return GRUB_EFI_OUT_OF_RESOURCES; + len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL); + var16[len16] = 0; + + r = grub_efi_system_table->runtime_services; + + status = efi_call_5 (r->get_variable, var16, guid, NULL, &datasize, NULL); + + if (status != GRUB_EFI_BUFFER_TOO_SMALL || !datasize) + { + grub_free (var16); + return status; + } + + data = grub_malloc (datasize); + if (!data) + { + grub_free (var16); + return GRUB_EFI_OUT_OF_RESOURCES; + } + + status = efi_call_5 (r->get_variable, var16, guid, attributes, &datasize, data); + grub_free (var16); + + if (status == GRUB_EFI_SUCCESS) + { + *data_out = data; + *datasize_out = datasize; + return status; + } + + grub_free (data); + return status; +} + +grub_efi_status_t +grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, + grub_size_t *datasize_out, void **data_out) +{ + return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL); +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Search the mods section from the PE32/PE32+ image. This code uses + a PE32 header, but should work with PE32+ as well. */ +grub_addr_t +grub_efi_modules_addr (void) +{ + grub_efi_loaded_image_t *image; + struct grub_pe32_header *header; + struct grub_pe32_coff_header *coff_header; + struct grub_pe32_section_table *sections; + struct grub_pe32_section_table *section; + struct grub_module_info *info; + grub_uint16_t i; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (! image) + return 0; + + header = image->image_base; + coff_header = &(header->coff_header); + sections + = (struct grub_pe32_section_table *) ((char *) coff_header + + sizeof (*coff_header) + + coff_header->optional_header_size); + + for (i = 0, section = sections; + i < coff_header->num_sections; + i++, section++) + { + if (grub_strcmp (section->name, "mods") == 0) + break; + } + + if (i == coff_header->num_sections) + { + grub_dprintf("sections", "section %d is last section; invalid.\n", i); + return 0; + } + + info = (struct grub_module_info *) ((char *) image->image_base + + section->virtual_address); + if (section->name[0] != '.' && info->magic != GRUB_MODULE_MAGIC) + { + grub_dprintf("sections", + "section %d has bad magic %08x, should be %08x\n", + i, info->magic, GRUB_MODULE_MAGIC); + return 0; + } + + grub_dprintf("sections", "returning section info for section %d: \"%s\"\n", + i, section->name); + return (grub_addr_t) info; +} + +#pragma GCC diagnostic error "-Wcast-align" + +char * +grub_efi_get_filename (grub_efi_device_path_t *dp0) +{ + char *name = 0, *p, *pi; + grub_size_t filesize = 0; + grub_efi_device_path_t *dp; + + if (!dp0) + return NULL; + + dp = dp0; + + while (dp) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + { + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%d", len); + return NULL; + } + len = (len - 4) / sizeof (grub_efi_char16_t); + filesize += GRUB_MAX_UTF8_PER_UTF16 * len + 2; + } + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + if (!filesize) + return NULL; + + dp = dp0; + + p = name = grub_malloc (filesize); + if (!name) + return NULL; + + while (dp) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + { + grub_efi_file_path_device_path_t *fp; + grub_efi_uint16_t len; + grub_efi_char16_t *dup_name; + + *p++ = '/'; + + len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%d", len); + grub_free (name); + return NULL; + } + + len = (len - 4) / sizeof (grub_efi_char16_t); + fp = (grub_efi_file_path_device_path_t *) dp; + /* According to EFI spec Path Name is NULL terminated */ + while (len > 0 && fp->path_name[len - 1] == 0) + len--; + + dup_name = grub_calloc (len, sizeof (*dup_name)); + if (!dup_name) + { + grub_free (name); + return NULL; + } + p = (char *) grub_utf16_to_utf8 ((unsigned char *) p, + grub_memcpy (dup_name, fp->path_name, len * sizeof (*dup_name)), + len); + grub_free (dup_name); + } + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + *p = '\0'; + + for (pi = name, p = name; *pi;) + { + /* EFI breaks paths with backslashes. */ + if (*pi == '\\' || *pi == '/') + { + *p++ = '/'; + while (*pi == '\\' || *pi == '/') + pi++; + continue; + } + *p++ = *pi++; + } + *p = '\0'; + + return name; +} + +grub_efi_device_path_t * +grub_efi_get_device_path (grub_efi_handle_t handle) +{ + return grub_efi_open_protocol (handle, &device_path_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + +/* Return the device path node right before the end node. */ +grub_efi_device_path_t * +grub_efi_find_last_device_path (const grub_efi_device_path_t *dp) +{ + grub_efi_device_path_t *next, *p; + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + return 0; + + for (p = (grub_efi_device_path_t *) dp, next = GRUB_EFI_NEXT_DEVICE_PATH (p); + ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next); + p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next)) + ; + + return p; +} + +/* Duplicate a device path. */ +grub_efi_device_path_t * +grub_efi_duplicate_device_path (const grub_efi_device_path_t *dp) +{ + grub_efi_device_path_t *p; + grub_size_t total_size = 0; + + for (p = (grub_efi_device_path_t *) dp; + ; + p = GRUB_EFI_NEXT_DEVICE_PATH (p)) + { + grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (p); + + /* + * In the event that we find a node that's completely garbage, for + * example if we get to 0x7f 0x01 0x02 0x00 ... (EndInstance with a size + * of 2), GRUB_EFI_END_ENTIRE_DEVICE_PATH() will be true and + * GRUB_EFI_NEXT_DEVICE_PATH() will return NULL, so we won't continue, + * and neither should our consumers, but there won't be any error raised + * even though the device path is junk. + * + * This keeps us from passing junk down back to our caller. + */ + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%" PRIuGRUB_SIZE, len); + return NULL; + } + + total_size += len; + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p)) + break; + } + + p = grub_malloc (total_size); + if (! p) + return 0; + + grub_memcpy (p, dp, total_size); + return p; +} + +static void +dump_vendor_path (const char *type, grub_efi_vendor_device_path_t *vendor) +{ + grub_uint32_t vendor_data_len = vendor->header.length - sizeof (*vendor); + grub_printf ("/%sVendor(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)[%x: ", + type, + (unsigned) vendor->vendor_guid.data1, + (unsigned) vendor->vendor_guid.data2, + (unsigned) vendor->vendor_guid.data3, + (unsigned) vendor->vendor_guid.data4[0], + (unsigned) vendor->vendor_guid.data4[1], + (unsigned) vendor->vendor_guid.data4[2], + (unsigned) vendor->vendor_guid.data4[3], + (unsigned) vendor->vendor_guid.data4[4], + (unsigned) vendor->vendor_guid.data4[5], + (unsigned) vendor->vendor_guid.data4[6], + (unsigned) vendor->vendor_guid.data4[7], + vendor_data_len); + if (vendor->header.length > sizeof (*vendor)) + { + grub_uint32_t i; + for (i = 0; i < vendor_data_len; i++) + grub_printf ("%02x ", vendor->vendor_defined_data[i]); + } + grub_printf ("]"); +} + + +/* Print the chain of Device Path nodes. This is mainly for debugging. */ +void +grub_efi_print_device_path (grub_efi_device_path_t *dp) +{ + while (GRUB_EFI_DEVICE_PATH_VALID (dp)) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + switch (type) + { + case GRUB_EFI_END_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE: + grub_printf ("/EndEntire\n"); + //grub_putchar ('\n'); + break; + case GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE: + grub_printf ("/EndThis\n"); + //grub_putchar ('\n'); + break; + default: + grub_printf ("/EndUnknown(%x)\n", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_PCI_DEVICE_PATH_SUBTYPE: + { + grub_efi_pci_device_path_t *pci + = (grub_efi_pci_device_path_t *) dp; + grub_printf ("/PCI(%x,%x)", + (unsigned) pci->function, (unsigned) pci->device); + } + break; + case GRUB_EFI_PCCARD_DEVICE_PATH_SUBTYPE: + { + grub_efi_pccard_device_path_t *pccard + = (grub_efi_pccard_device_path_t *) dp; + grub_printf ("/PCCARD(%x)", + (unsigned) pccard->function); + } + break; + case GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE: + { + grub_efi_memory_mapped_device_path_t *mmapped + = (grub_efi_memory_mapped_device_path_t *) dp; + grub_printf ("/MMap(%x,%llx,%llx)", + (unsigned) mmapped->memory_type, + (unsigned long long) mmapped->start_address, + (unsigned long long) mmapped->end_address); + } + break; + case GRUB_EFI_VENDOR_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Hardware", + (grub_efi_vendor_device_path_t *) dp); + break; + case GRUB_EFI_CONTROLLER_DEVICE_PATH_SUBTYPE: + { + grub_efi_controller_device_path_t *controller + = (grub_efi_controller_device_path_t *) dp; + grub_printf ("/Ctrl(%x)", + (unsigned) controller->controller_number); + } + break; + default: + grub_printf ("/UnknownHW(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_ACPI_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_acpi_device_path_t *acpi + = (grub_efi_acpi_device_path_t *) dp; + grub_printf ("/ACPI(%x,%x)", + (unsigned) acpi->hid, + (unsigned) acpi->uid); + } + break; + case GRUB_EFI_EXPANDED_ACPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_expanded_acpi_device_path_t *eacpi + = (grub_efi_expanded_acpi_device_path_t *) dp; + grub_printf ("/ACPI("); + + if (GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)[0] == '\0') + grub_printf ("%x,", (unsigned) eacpi->hid); + else + grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)); + + if (GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)[0] == '\0') + grub_printf ("%x,", (unsigned) eacpi->uid); + else + grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)); + + if (GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)[0] == '\0') + grub_printf ("%x)", (unsigned) eacpi->cid); + else + grub_printf ("%s)", GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)); + } + break; + default: + grub_printf ("/UnknownACPI(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_ATAPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_atapi_device_path_t *atapi + = (grub_efi_atapi_device_path_t *) dp; + grub_printf ("/ATAPI(%x,%x,%x)", + (unsigned) atapi->primary_secondary, + (unsigned) atapi->slave_master, + (unsigned) atapi->lun); + } + break; + case GRUB_EFI_SCSI_DEVICE_PATH_SUBTYPE: + { + grub_efi_scsi_device_path_t *scsi + = (grub_efi_scsi_device_path_t *) dp; + grub_printf ("/SCSI(%x,%x)", + (unsigned) scsi->pun, + (unsigned) scsi->lun); + } + break; + case GRUB_EFI_FIBRE_CHANNEL_DEVICE_PATH_SUBTYPE: + { + grub_efi_fibre_channel_device_path_t *fc + = (grub_efi_fibre_channel_device_path_t *) dp; + grub_printf ("/FibreChannel(%llx,%llx)", + (unsigned long long) fc->wwn, + (unsigned long long) fc->lun); + } + break; + case GRUB_EFI_1394_DEVICE_PATH_SUBTYPE: + { + grub_efi_1394_device_path_t *firewire + = (grub_efi_1394_device_path_t *) dp; + grub_printf ("/1394(%llx)", + (unsigned long long) firewire->guid); + } + break; + case GRUB_EFI_USB_DEVICE_PATH_SUBTYPE: + { + grub_efi_usb_device_path_t *usb + = (grub_efi_usb_device_path_t *) dp; + grub_printf ("/USB(%x,%x)", + (unsigned) usb->parent_port_number, + (unsigned) usb->usb_interface); + } + break; + case GRUB_EFI_USB_CLASS_DEVICE_PATH_SUBTYPE: + { + grub_efi_usb_class_device_path_t *usb_class + = (grub_efi_usb_class_device_path_t *) dp; + grub_printf ("/USBClass(%x,%x,%x,%x,%x)", + (unsigned) usb_class->vendor_id, + (unsigned) usb_class->product_id, + (unsigned) usb_class->device_class, + (unsigned) usb_class->device_subclass, + (unsigned) usb_class->device_protocol); + } + break; + case GRUB_EFI_I2O_DEVICE_PATH_SUBTYPE: + { + grub_efi_i2o_device_path_t *i2o + = (grub_efi_i2o_device_path_t *) dp; + grub_printf ("/I2O(%x)", (unsigned) i2o->tid); + } + break; + case GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE: + { + grub_efi_mac_address_device_path_t *mac + = (grub_efi_mac_address_device_path_t *) dp; + grub_printf ("/MacAddr(%02x:%02x:%02x:%02x:%02x:%02x,%x)", + (unsigned) mac->mac_address[0], + (unsigned) mac->mac_address[1], + (unsigned) mac->mac_address[2], + (unsigned) mac->mac_address[3], + (unsigned) mac->mac_address[4], + (unsigned) mac->mac_address[5], + (unsigned) mac->if_type); + } + break; + case GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE: + { + grub_efi_ipv4_device_path_t *ipv4 + = (grub_efi_ipv4_device_path_t *) dp; + grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)", + (unsigned) ipv4->local_ip_address[0], + (unsigned) ipv4->local_ip_address[1], + (unsigned) ipv4->local_ip_address[2], + (unsigned) ipv4->local_ip_address[3], + (unsigned) ipv4->remote_ip_address[0], + (unsigned) ipv4->remote_ip_address[1], + (unsigned) ipv4->remote_ip_address[2], + (unsigned) ipv4->remote_ip_address[3], + (unsigned) ipv4->local_port, + (unsigned) ipv4->remote_port, + (unsigned) ipv4->protocol, + (unsigned) ipv4->static_ip_address); + } + break; + case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE: + { + grub_efi_ipv6_device_path_t *ipv6 + = (grub_efi_ipv6_device_path_t *) dp; + grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)", + (unsigned) ipv6->local_ip_address[0], + (unsigned) ipv6->local_ip_address[1], + (unsigned) ipv6->local_ip_address[2], + (unsigned) ipv6->local_ip_address[3], + (unsigned) ipv6->local_ip_address[4], + (unsigned) ipv6->local_ip_address[5], + (unsigned) ipv6->local_ip_address[6], + (unsigned) ipv6->local_ip_address[7], + (unsigned) ipv6->remote_ip_address[0], + (unsigned) ipv6->remote_ip_address[1], + (unsigned) ipv6->remote_ip_address[2], + (unsigned) ipv6->remote_ip_address[3], + (unsigned) ipv6->remote_ip_address[4], + (unsigned) ipv6->remote_ip_address[5], + (unsigned) ipv6->remote_ip_address[6], + (unsigned) ipv6->remote_ip_address[7], + (unsigned) ipv6->local_port, + (unsigned) ipv6->remote_port, + (unsigned) ipv6->protocol, + (unsigned) ipv6->static_ip_address); + } + break; + case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE: + { + grub_efi_infiniband_device_path_t *ib + = (grub_efi_infiniband_device_path_t *) dp; + grub_printf ("/InfiniBand(%x,%llx,%llx,%llx)", + (unsigned) ib->port_gid[0], /* XXX */ + (unsigned long long) ib->remote_id, + (unsigned long long) ib->target_port_id, + (unsigned long long) ib->device_id); + } + break; + case GRUB_EFI_UART_DEVICE_PATH_SUBTYPE: + { + grub_efi_uart_device_path_t *uart + = (grub_efi_uart_device_path_t *) dp; + grub_printf ("/UART(%llu,%u,%x,%x)", + (unsigned long long) uart->baud_rate, + uart->data_bits, + uart->parity, + uart->stop_bits); + } + break; + case GRUB_EFI_SATA_DEVICE_PATH_SUBTYPE: + { + grub_efi_sata_device_path_t *sata; + sata = (grub_efi_sata_device_path_t *) dp; + grub_printf ("/Sata(%x,%x,%x)", + sata->hba_port, + sata->multiplier_port, + sata->lun); + } + break; + + case GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Messaging", + (grub_efi_vendor_device_path_t *) dp); + break; + default: + grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_MEDIA_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE: + { + grub_efi_hard_drive_device_path_t *hd = (grub_efi_hard_drive_device_path_t *) dp; + grub_printf ("/HD(%u,%llx,%llx,%02x%02x%02x%02x%02x%02x%02x%02x,%x,%x)", + hd->partition_number, + (unsigned long long) hd->partition_start, + (unsigned long long) hd->partition_size, + (unsigned) hd->partition_signature[0], + (unsigned) hd->partition_signature[1], + (unsigned) hd->partition_signature[2], + (unsigned) hd->partition_signature[3], + (unsigned) hd->partition_signature[4], + (unsigned) hd->partition_signature[5], + (unsigned) hd->partition_signature[6], + (unsigned) hd->partition_signature[7], + (unsigned) hd->partmap_type, + (unsigned) hd->signature_type); + } + break; + case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE: + { + grub_efi_cdrom_device_path_t *cd + = (grub_efi_cdrom_device_path_t *) dp; + grub_printf ("/CD(%u,%llx,%llx)", + cd->boot_entry, + (unsigned long long) cd->partition_start, + (unsigned long long) cd->partition_size); + } + break; + case GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Media", + (grub_efi_vendor_device_path_t *) dp); + break; + case GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE: + { + grub_efi_file_path_device_path_t *fp; + grub_uint8_t *buf; + fp = (grub_efi_file_path_device_path_t *) dp; + buf = grub_malloc ((len - 4) * 2 + 1); + if (buf) + { + grub_efi_char16_t *dup_name = grub_malloc (len - 4); + if (!dup_name) + { + grub_errno = GRUB_ERR_NONE; + grub_printf ("/File((null))"); + grub_free (buf); + break; + } + *grub_utf16_to_utf8 (buf, grub_memcpy (dup_name, fp->path_name, len - 4), + (len - 4) / sizeof (grub_efi_char16_t)) + = '\0'; + grub_free (dup_name); + } + else + grub_errno = GRUB_ERR_NONE; + grub_printf ("/File(%s)", buf); + grub_free (buf); + } + break; + case GRUB_EFI_PROTOCOL_DEVICE_PATH_SUBTYPE: + { + grub_efi_protocol_device_path_t *proto + = (grub_efi_protocol_device_path_t *) dp; + grub_printf ("/Protocol(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)", + (unsigned) proto->guid.data1, + (unsigned) proto->guid.data2, + (unsigned) proto->guid.data3, + (unsigned) proto->guid.data4[0], + (unsigned) proto->guid.data4[1], + (unsigned) proto->guid.data4[2], + (unsigned) proto->guid.data4[3], + (unsigned) proto->guid.data4[4], + (unsigned) proto->guid.data4[5], + (unsigned) proto->guid.data4[6], + (unsigned) proto->guid.data4[7]); + } + break; + default: + grub_printf ("/UnknownMedia(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_BIOS_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_BIOS_DEVICE_PATH_SUBTYPE: + { + grub_efi_bios_device_path_t *bios + = (grub_efi_bios_device_path_t *) dp; + grub_printf ("/BIOS(%x,%x,%s)", + (unsigned) bios->device_type, + (unsigned) bios->status_flags, + (char *) (dp + 1)); + } + break; + default: + grub_printf ("/UnknownBIOS(%x)", (unsigned) subtype); + break; + } + break; + + default: + grub_printf ("/UnknownType(%x,%x)\n", + (unsigned) type, + (unsigned) subtype); + return; + break; + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } +} + +/* Compare device paths. */ +int +grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1, + const grub_efi_device_path_t *dp2) +{ + if (! dp1 || ! dp2) + /* Return non-zero. */ + return 1; + + if (dp1 == dp2) + return 0; + + while (GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2)) + { + grub_efi_uint8_t type1, type2; + grub_efi_uint8_t subtype1, subtype2; + grub_efi_uint16_t len1, len2; + int ret; + + type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1); + type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2); + + if (type1 != type2) + return (int) type2 - (int) type1; + + subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1); + subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2); + + if (subtype1 != subtype2) + return (int) subtype1 - (int) subtype2; + + len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1); + len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2); + + if (len1 != len2) + return (int) len1 - (int) len2; + + ret = grub_memcmp (dp1, dp2, len1); + if (ret != 0) + return ret; + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1)) + break; + + dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1); + dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2); + } + + /* + * There's no "right" answer here, but we probably don't want to call a valid + * dp and an invalid dp equal, so pick one way or the other. + */ + if (GRUB_EFI_DEVICE_PATH_VALID (dp1) && !GRUB_EFI_DEVICE_PATH_VALID (dp2)) + return 1; + else if (!GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2)) + return -1; + + return 0; +} diff --git a/grub-core/kern/efi/fdt.c b/grub-core/kern/efi/fdt.c new file mode 100644 index 0000000..30100c6 --- /dev/null +++ b/grub-core/kern/efi/fdt.c @@ -0,0 +1,43 @@ +/* fdt.c - EFI Flattened Device Tree interaction */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include + +void * +grub_efi_get_firmware_fdt (void) +{ + grub_efi_configuration_table_t *tables; + grub_efi_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID; + void *firmware_fdt = NULL; + unsigned int i; + + /* Look for FDT in UEFI config tables. */ + tables = grub_efi_system_table->configuration_table; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + if (grub_memcmp (&tables[i].vendor_guid, &fdt_guid, sizeof (fdt_guid)) == 0) + { + firmware_fdt = tables[i].vendor_table; + grub_dprintf ("linux", "found registered FDT @ %p\n", firmware_fdt); + break; + } + + return firmware_fdt; +} diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c new file mode 100644 index 0000000..7facacf --- /dev/null +++ b/grub-core/kern/efi/init.c @@ -0,0 +1,149 @@ +/* init.c - generic EFI initialization and finalization */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_STACK_PROTECTOR + +static grub_efi_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; + +/* + * Don't put this on grub_efi_init()'s local stack to avoid it + * getting a stack check. + */ +static grub_efi_uint8_t stack_chk_guard_buf[32]; + +grub_addr_t __stack_chk_guard; + +void __attribute__ ((noreturn)) +__stack_chk_fail (void) +{ + /* + * Assume it's not safe to call into EFI Boot Services. Sorry, that + * means no console message here. + */ + do + { + /* Do not optimize out the loop. */ + asm volatile (""); + } + while (1); +} + +static void +stack_protector_init (void) +{ + grub_efi_rng_protocol_t *rng; + + /* Set up the stack canary. Make errors here non-fatal for now. */ + rng = grub_efi_locate_protocol (&rng_protocol_guid, NULL); + if (rng != NULL) + { + grub_efi_status_t status; + + status = efi_call_4 (rng->get_rng, rng, NULL, sizeof (stack_chk_guard_buf), + stack_chk_guard_buf); + if (status == GRUB_EFI_SUCCESS) + grub_memcpy (&__stack_chk_guard, stack_chk_guard_buf, sizeof (__stack_chk_guard)); + } +} +#else +static void +stack_protector_init (void) +{ +} +#endif + +grub_addr_t grub_modbase; + +void +grub_efi_init (void) +{ + grub_modbase = grub_efi_modules_addr (); + /* First of all, initialize the console so that GRUB can display + messages. */ + grub_console_init (); + + stack_protector_init (); + + /* Initialize the memory management system. */ + grub_efi_mm_init (); + + /* + * Lockdown the GRUB and register the shim_lock verifier + * if the UEFI Secure Boot is enabled. + */ + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + { + grub_lockdown (); + grub_shim_lock_verifier_setup (); + } + + efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, + 0, 0, 0, NULL); + + grub_efidisk_init (); +} + +void (*grub_efi_net_config) (grub_efi_handle_t hnd, + char **device, + char **path); + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + grub_efi_loaded_image_t *image = NULL; + char *p; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return; + *device = grub_efidisk_get_device_name (image->device_handle); + if (!*device && grub_efi_net_config) + { + grub_efi_net_config (image->device_handle, device, path); + return; + } + + *path = grub_efi_get_filename (image->file_path); + if (*path) + { + /* Get the directory. */ + p = grub_strrchr (*path, '/'); + if (p) + *p = '\0'; + } +} + +void +grub_efi_fini (void) +{ + grub_efidisk_fini (); + grub_console_fini (); +} diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c new file mode 100644 index 0000000..9838fb2 --- /dev/null +++ b/grub-core/kern/efi/mm.c @@ -0,0 +1,691 @@ +/* mm.c - generic EFI memory management */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include + +#if defined (__i386__) || defined (__x86_64__) +#include +#endif + +#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) + +#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) +#define BYTES_TO_PAGES_DOWN(bytes) ((bytes) >> 12) +#define PAGES_TO_BYTES(pages) ((pages) << 12) + +/* The size of a memory map obtained from the firmware. This must be + a multiplier of 4KB. */ +#define MEMORY_MAP_SIZE 0x3000 + +/* The minimum and maximum heap size for GRUB itself. */ +#define MIN_HEAP_SIZE 0x100000 +#define MAX_HEAP_SIZE (1600 * 0x100000) + +static void *finish_mmap_buf = 0; +static grub_efi_uintn_t finish_mmap_size = 0; +static grub_efi_uintn_t finish_key = 0; +static grub_efi_uintn_t finish_desc_size; +static grub_efi_uint32_t finish_desc_version; +int grub_efi_is_finished = 0; + +/* + * We need to roll back EFI allocations on exit. Remember allocations that + * we'll free on exit. + */ +struct efi_allocation; +struct efi_allocation { + grub_efi_physical_address_t address; + grub_efi_uint64_t pages; + struct efi_allocation *next; +}; +static struct efi_allocation *efi_allocated_memory; + +static void +grub_efi_store_alloc (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_boot_services_t *b; + struct efi_allocation *alloc; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA, + sizeof(*alloc), (void**)&alloc); + + if (status == GRUB_EFI_SUCCESS) + { + alloc->next = efi_allocated_memory; + alloc->address = address; + alloc->pages = pages; + efi_allocated_memory = alloc; + } + else + grub_printf ("Could not malloc memory to remember EFI allocation. " + "Exiting GRUB won't free all memory.\n"); +} + +static void +grub_efi_drop_alloc (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + struct efi_allocation *ea, *eap; + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + + for (eap = NULL, ea = efi_allocated_memory; ea; eap = ea, ea = ea->next) + { + if (ea->address != address || ea->pages != pages) + continue; + + /* Remove the current entry from the list. */ + if (eap) + eap->next = ea->next; + else + efi_allocated_memory = ea->next; + + /* Then free the memory backing it. */ + efi_call_1 (b->free_pool, ea); + + /* And leave, we're done. */ + break; + } +} + +/* Allocate pages. Return the pointer to the first of allocated pages. */ +void * +grub_efi_allocate_pages_real (grub_efi_physical_address_t address, + grub_efi_uintn_t pages, + grub_efi_allocate_type_t alloctype, + grub_efi_memory_type_t memtype) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + + /* Limit the memory access to less than 4GB for 32-bit platforms. */ + if (address > GRUB_EFI_MAX_USABLE_ADDRESS) + { + char inv_addr[17], max_addr[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (inv_addr, sizeof (inv_addr) - 1, "%" PRIxGRUB_UINT64_T, + address); + grub_snprintf (max_addr, sizeof (max_addr) - 1, "%" PRIxGRUB_UINT64_T, + (grub_efi_uint64_t) GRUB_EFI_MAX_USABLE_ADDRESS); + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid memory address (0x%s > 0x%s)"), inv_addr, max_addr); + return NULL; + } + + b = grub_efi_system_table->boot_services; + status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return NULL; + } + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = GRUB_EFI_MAX_USABLE_ADDRESS; + status = efi_call_4 (b->allocate_pages, alloctype, memtype, pages, &address); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return NULL; + } + } + + grub_efi_store_alloc (address, pages); + + return (void *) ((grub_addr_t) address); +} + +void * +grub_efi_allocate_any_pages (grub_efi_uintn_t pages) +{ + return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS, + pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_LOADER_DATA); +} + +void * +grub_efi_allocate_fixed (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + return grub_efi_allocate_pages_real (address, pages, + GRUB_EFI_ALLOCATE_ADDRESS, + GRUB_EFI_LOADER_DATA); +} + +/* Free pages starting from ADDRESS. */ +void +grub_efi_free_pages (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + efi_call_2 (b->free_pages, address, pages); + + grub_efi_drop_alloc (address, pages); +} + +#if defined (__i386__) || defined (__x86_64__) + +/* Helper for stop_broadcom. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_pci_address_t addr; + grub_uint8_t cap; + grub_uint16_t pm_state; + + if ((pciid & 0xffff) != GRUB_PCI_VENDOR_BROADCOM) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + if (grub_pci_read (addr) >> 24 != GRUB_PCI_CLASS_NETWORK) + return 0; + cap = grub_pci_find_capability (dev, GRUB_PCI_CAP_POWER_MANAGEMENT); + if (!cap) + return 0; + addr = grub_pci_make_address (dev, cap + 4); + pm_state = grub_pci_read_word (addr); + pm_state = pm_state | 0x03; + grub_pci_write_word (addr, pm_state); + grub_pci_read_word (addr); + return 0; +} + +static void +stop_broadcom (void) +{ + grub_pci_iterate (find_card, NULL); +} + +#endif + +grub_err_t +grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, + grub_efi_uintn_t *map_key, + grub_efi_uintn_t *efi_desc_size, + grub_efi_uint32_t *efi_desc_version) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + +#if defined (__i386__) || defined (__x86_64__) + const grub_uint16_t apple[] = { 'A', 'p', 'p', 'l', 'e' }; + int is_apple; + + is_apple = (grub_memcmp (grub_efi_system_table->firmware_vendor, + apple, sizeof (apple)) == 0); +#endif + + while (1) + { + if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key, + &finish_desc_size, &finish_desc_version) < 0) + return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); + + if (outbuf && *outbuf_size < finish_mmap_size) + return grub_error (GRUB_ERR_IO, "memory map buffer is too small"); + + finish_mmap_buf = grub_malloc (finish_mmap_size); + if (!finish_mmap_buf) + return grub_errno; + + if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key, + &finish_desc_size, &finish_desc_version) <= 0) + { + grub_free (finish_mmap_buf); + return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); + } + + b = grub_efi_system_table->boot_services; + status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle, + finish_key); + if (status == GRUB_EFI_SUCCESS) + break; + + if (status != GRUB_EFI_INVALID_PARAMETER) + { + grub_free (finish_mmap_buf); + return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services"); + } + + grub_free (finish_mmap_buf); + grub_printf ("Trying to terminate EFI services again\n"); + } + grub_efi_is_finished = 1; + if (outbuf_size) + *outbuf_size = finish_mmap_size; + if (outbuf) + grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size); + if (map_key) + *map_key = finish_key; + if (efi_desc_size) + *efi_desc_size = finish_desc_size; + if (efi_desc_version) + *efi_desc_version = finish_desc_version; + +#if defined (__i386__) || defined (__x86_64__) + if (is_apple) + stop_broadcom (); +#endif + + return GRUB_ERR_NONE; +} + +/* + * To obtain the UEFI memory map, we must pass a buffer of sufficient size + * to hold the entire map. This function returns a sane start value for + * buffer size. + */ +grub_efi_uintn_t +grub_efi_find_mmap_size (void) +{ + grub_efi_uintn_t mmap_size = 0; + grub_efi_uintn_t desc_size; + + if (grub_efi_get_memory_map (&mmap_size, NULL, NULL, &desc_size, 0) < 0) + { + grub_error (GRUB_ERR_IO, "cannot get EFI memory map size"); + return 0; + } + + /* + * Add an extra page, since UEFI can alter the memory map itself on + * callbacks or explicit calls, including console output. + */ + return ALIGN_UP (mmap_size + GRUB_EFI_PAGE_SIZE, GRUB_EFI_PAGE_SIZE); +} + +/* Get the memory map as defined in the EFI spec. Return 1 if successful, + return 0 if partial, or return -1 if an error occurs. */ +int +grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size, + grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t *map_key, + grub_efi_uintn_t *descriptor_size, + grub_efi_uint32_t *descriptor_version) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_uintn_t key; + grub_efi_uint32_t version; + grub_efi_uintn_t size; + + if (grub_efi_is_finished) + { + int ret = 1; + + if (memory_map != NULL) + { + if (*memory_map_size < finish_mmap_size) + { + grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size); + ret = 0; + } + else + grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size); + } + else + { + /* + * Incomplete, no buffer to copy into, same as + * GRUB_EFI_BUFFER_TOO_SMALL below. + */ + ret = 0; + } + *memory_map_size = finish_mmap_size; + if (map_key) + *map_key = finish_key; + if (descriptor_size) + *descriptor_size = finish_desc_size; + if (descriptor_version) + *descriptor_version = finish_desc_version; + return ret; + } + + /* Allow some parameters to be missing. */ + if (! map_key) + map_key = &key; + if (! descriptor_version) + descriptor_version = &version; + if (! descriptor_size) + descriptor_size = &size; + + b = grub_efi_system_table->boot_services; + status = efi_call_5 (b->get_memory_map, memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version); + if (*descriptor_size == 0) + *descriptor_size = sizeof (grub_efi_memory_descriptor_t); + if (status == GRUB_EFI_SUCCESS) + return 1; + else if (status == GRUB_EFI_BUFFER_TOO_SMALL) + return 0; + else + return -1; +} + +/* Sort the memory map in place. */ +static void +sort_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *d1; + grub_efi_memory_descriptor_t *d2; + + for (d1 = memory_map; + d1 < memory_map_end; + d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size)) + { + grub_efi_memory_descriptor_t *max_desc = d1; + + for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size); + d2 < memory_map_end; + d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size)) + { + if (max_desc->num_pages < d2->num_pages) + max_desc = d2; + } + + if (max_desc != d1) + { + grub_efi_memory_descriptor_t tmp; + + tmp = *d1; + *d1 = *max_desc; + *max_desc = tmp; + } + } +} + +/* Filter the descriptors. GRUB needs only available memory. */ +static grub_efi_memory_descriptor_t * +filter_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_memory_descriptor_t *filtered_memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + grub_efi_memory_descriptor_t *filtered_desc; + + for (desc = memory_map, filtered_desc = filtered_memory_map; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY +#if 1 + && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS +#endif + && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 + && desc->num_pages != 0) + { + grub_memcpy (filtered_desc, desc, desc_size); + + /* Avoid less than 1MB, because some loaders seem to be confused. */ + if (desc->physical_start < 0x100000) + { + desc->num_pages -= BYTES_TO_PAGES (0x100000 + - desc->physical_start); + desc->physical_start = 0x100000; + } + +#if 1 + if (BYTES_TO_PAGES (filtered_desc->physical_start) + + filtered_desc->num_pages + > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)) + filtered_desc->num_pages + = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS) + - BYTES_TO_PAGES (filtered_desc->physical_start)); +#endif + + if (filtered_desc->num_pages == 0) + continue; + + filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size); + } + } + + return filtered_desc; +} + +/* Return the total number of pages. */ +static grub_efi_uint64_t +get_total_pages (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + grub_efi_uint64_t total = 0; + + for (desc = memory_map; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + total += desc->num_pages; + + return total; +} + +/* Add memory regions. */ +static void +add_memory_regions (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end, + grub_efi_uint64_t required_pages) +{ + grub_efi_memory_descriptor_t *desc; + + for (desc = memory_map; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + grub_efi_uint64_t pages; + grub_efi_physical_address_t start; + void *addr; + + start = desc->physical_start; + pages = desc->num_pages; + if (pages > required_pages) + { + start += PAGES_TO_BYTES (pages - required_pages); + pages = required_pages; + } + + addr = grub_efi_allocate_pages_real (start, pages, + GRUB_EFI_ALLOCATE_ADDRESS, + GRUB_EFI_LOADER_CODE); + if (! addr) + grub_fatal ("cannot allocate conventional memory %p with %u pages", + (void *) ((grub_addr_t) start), + (unsigned) pages); + + grub_mm_init_region (addr, PAGES_TO_BYTES (pages)); + + required_pages -= pages; + if (required_pages == 0) + break; + } + + if (required_pages > 0) + grub_fatal ("too little memory"); +} + +void +grub_efi_memory_fini (void) +{ + /* + * Free all stale allocations. grub_efi_free_pages() will remove + * the found entry from the list and it will always find the first + * list entry (efi_allocated_memory is the list start). Hence we + * remove all entries from the list until none is left altogether. + */ + while (efi_allocated_memory) + grub_efi_free_pages (efi_allocated_memory->address, + efi_allocated_memory->pages); +} + +#if 0 +/* Print the memory map. */ +static void +print_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + int i; + + for (desc = memory_map, i = 0; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++) + { + grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n", + desc->type, desc->physical_start, desc->virtual_start, + desc->num_pages, desc->attribute); + } +} +#endif + +void +grub_efi_mm_init (void) +{ + grub_efi_memory_descriptor_t *memory_map; + grub_efi_memory_descriptor_t *memory_map_end; + grub_efi_memory_descriptor_t *filtered_memory_map; + grub_efi_memory_descriptor_t *filtered_memory_map_end; + grub_efi_uintn_t map_size; + grub_efi_uintn_t desc_size; + grub_efi_uint64_t total_pages; + grub_efi_uint64_t required_pages; + int mm_status; + + /* Prepare a memory region to store two memory maps. */ + memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + if (! memory_map) + grub_fatal ("cannot allocate memory"); + + /* Obtain descriptors for available memory. */ + map_size = MEMORY_MAP_SIZE; + + mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0); + + if (mm_status == 0) + { + grub_efi_free_pages + ((grub_efi_physical_address_t) ((grub_addr_t) memory_map), + 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); + + /* Freeing/allocating operations may increase memory map size. */ + map_size += desc_size * 32; + + memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size)); + if (! memory_map) + grub_fatal ("cannot allocate memory"); + + mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, + &desc_size, 0); + } + + if (mm_status < 0) + grub_fatal ("cannot get memory map"); + + memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size); + + filtered_memory_map = memory_map_end; + + filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map, + desc_size, memory_map_end); + + /* By default, request a quarter of the available memory. */ + total_pages = get_total_pages (filtered_memory_map, desc_size, + filtered_memory_map_end); + required_pages = (total_pages >> 2); + if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE)) + required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE); + else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE)) + required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE); + + /* Sort the filtered descriptors, so that GRUB can allocate pages + from smaller regions. */ + sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end); + + /* Allocate memory regions for GRUB's memory management. */ + add_memory_regions (filtered_memory_map, desc_size, + filtered_memory_map_end, required_pages); + +#if 0 + /* For debug. */ + map_size = MEMORY_MAP_SIZE; + + if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0) + grub_fatal ("cannot get memory map"); + + grub_printf ("printing memory map\n"); + print_memory_map (memory_map, desc_size, + NEXT_MEMORY_DESCRIPTOR (memory_map, map_size)); + grub_fatal ("Debug. "); +#endif + + /* Release the memory maps. */ + grub_efi_free_pages ((grub_addr_t) memory_map, + 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); +} + +#if defined (__aarch64__) || defined (__arm__) || defined (__riscv) +grub_err_t +grub_efi_get_ram_base(grub_addr_t *base_addr) +{ + grub_efi_memory_descriptor_t *memory_map, *desc; + grub_efi_uintn_t memory_map_size, desc_size; + int ret; + + memory_map_size = grub_efi_find_mmap_size(); + + memory_map = grub_malloc (memory_map_size); + if (! memory_map) + return GRUB_ERR_OUT_OF_MEMORY; + ret = grub_efi_get_memory_map (&memory_map_size, memory_map, NULL, + &desc_size, NULL); + + if (ret < 1) + return GRUB_ERR_BUG; + + for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS; + (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size); + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + if (desc->attribute & GRUB_EFI_MEMORY_WB) + *base_addr = grub_min (*base_addr, desc->physical_start); + + grub_free(memory_map); + + return GRUB_ERR_NONE; +} +#endif diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c new file mode 100644 index 0000000..c52ec62 --- /dev/null +++ b/grub-core/kern/efi/sb.c @@ -0,0 +1,188 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + * + * UEFI Secure Boot related checkings. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_efi_guid_t shim_lock_guid = GRUB_EFI_SHIM_LOCK_GUID; + +/* + * Determine whether we're in secure boot mode. + * + * Please keep the logic in sync with the Linux kernel, + * drivers/firmware/efi/libstub/secureboot.c:efi_get_secureboot(). + */ +grub_uint8_t +grub_efi_get_secureboot (void) +{ + static grub_efi_guid_t efi_variable_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_efi_status_t status; + grub_efi_uint32_t attr = 0; + grub_size_t size = 0; + grub_uint8_t *secboot = NULL; + grub_uint8_t *setupmode = NULL; + grub_uint8_t *moksbstate = NULL; + grub_uint8_t secureboot = GRUB_EFI_SECUREBOOT_MODE_UNKNOWN; + const char *secureboot_str = "UNKNOWN"; + + status = grub_efi_get_variable ("SecureBoot", &efi_variable_guid, + &size, (void **) &secboot); + + if (status == GRUB_EFI_NOT_FOUND) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + goto out; + } + + if (status != GRUB_EFI_SUCCESS) + goto out; + + status = grub_efi_get_variable ("SetupMode", &efi_variable_guid, + &size, (void **) &setupmode); + + if (status != GRUB_EFI_SUCCESS) + goto out; + + if ((*secboot == 0) || (*setupmode == 1)) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + goto out; + } + + /* + * See if a user has put the shim into insecure mode. If so, and if the + * variable doesn't have the runtime attribute set, we might as well + * honor that. + */ + status = grub_efi_get_variable_with_attributes ("MokSBState", &shim_lock_guid, + &size, (void **) &moksbstate, &attr); + + /* If it fails, we don't care why. Default to secure. */ + if (status != GRUB_EFI_SUCCESS) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_ENABLED; + goto out; + } + + if (!(attr & GRUB_EFI_VARIABLE_RUNTIME_ACCESS) && *moksbstate == 1) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + goto out; + } + + secureboot = GRUB_EFI_SECUREBOOT_MODE_ENABLED; + + out: + grub_free (moksbstate); + grub_free (setupmode); + grub_free (secboot); + + if (secureboot == GRUB_EFI_SECUREBOOT_MODE_DISABLED) + secureboot_str = "Disabled"; + else if (secureboot == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + secureboot_str = "Enabled"; + + grub_dprintf ("efi", "UEFI Secure Boot state: %s\n", secureboot_str); + + return secureboot; +} + +static grub_err_t +shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), + enum grub_file_type type, + void **context __attribute__ ((unused)), + enum grub_verify_flags *flags) +{ + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: + case GRUB_FILE_TYPE_BSD_KERNEL: + case GRUB_FILE_TYPE_XNU_KERNEL: + case GRUB_FILE_TYPE_PLAN9_KERNEL: + case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + + /* Fall through. */ + + default: + return GRUB_ERR_NONE; + } +} + +static grub_err_t +shim_lock_verifier_write (void *context __attribute__ ((unused)), void *buf, grub_size_t size) +{ + grub_efi_shim_lock_protocol_t *sl = grub_efi_locate_protocol (&shim_lock_guid, 0); + + if (!sl) + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("shim_lock protocol not found")); + + if (sl->verify (buf, size) != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad shim signature")); + + return GRUB_ERR_NONE; +} + +struct grub_file_verifier shim_lock_verifier = + { + .name = "shim_lock_verifier", + .init = shim_lock_verifier_init, + .write = shim_lock_verifier_write + }; + +void +grub_shim_lock_verifier_setup (void) +{ + struct grub_module_header *header; + grub_efi_shim_lock_protocol_t *sl = + grub_efi_locate_protocol (&shim_lock_guid, 0); + + /* shim_lock is missing, check if GRUB image is built with --disable-shim-lock. */ + if (!sl) + { + FOR_MODULES (header) + { + if (header->type == OBJ_TYPE_DISABLE_SHIM_LOCK) + return; + } + } + + /* Secure Boot is off. Do not load shim_lock. */ + if (grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + + /* Enforce shim_lock_verifier. */ + grub_verifier_register (&shim_lock_verifier); + + grub_env_set ("shim_lock", "y"); + grub_env_export ("shim_lock"); +} -- cgit v1.2.3