14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 2f4f75ad5SArd Biesheuvel /* 3f4f75ad5SArd Biesheuvel * Helper functions used by the EFI stub on multiple 4f4f75ad5SArd Biesheuvel * architectures. This should be #included by the EFI stub 5f4f75ad5SArd Biesheuvel * implementation files. 6f4f75ad5SArd Biesheuvel * 7f4f75ad5SArd Biesheuvel * Copyright 2011 Intel Corporation; author Matt Fleming 8f4f75ad5SArd Biesheuvel */ 9f4f75ad5SArd Biesheuvel 10c0891ac1SAlexey Dobriyan #include <linux/stdarg.h> 112c7d1e30SArvind Sankar 12f4f75ad5SArd Biesheuvel #include <linux/efi.h> 13fd0528a2SArvind Sankar #include <linux/kernel.h> 14f4f75ad5SArd Biesheuvel #include <asm/efi.h> 1580b1bfe1SArvind Sankar #include <asm/setup.h> 16f4f75ad5SArd Biesheuvel 17f4f75ad5SArd Biesheuvel #include "efistub.h" 18f4f75ad5SArd Biesheuvel 19980771f6SArd Biesheuvel bool efi_nochunk; 207c116db2SWill Deacon bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); 21980771f6SArd Biesheuvel bool efi_novamap; 22980771f6SArd Biesheuvel 2320287d56SArd Biesheuvel static bool efi_noinitrd; 2454439370SArvind Sankar static bool efi_nosoftreserve; 2554439370SArvind Sankar static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); 2660f38de7SArd Biesheuvel 27b617c526SDan Williams bool __pure __efi_soft_reserve_enabled(void) 28b617c526SDan Williams { 29b617c526SDan Williams return !efi_nosoftreserve; 30b617c526SDan Williams } 3160f38de7SArd Biesheuvel 328c0a839cSHeinrich Schuchardt /** 338c0a839cSHeinrich Schuchardt * efi_parse_options() - Parse EFI command line options 348c0a839cSHeinrich Schuchardt * @cmdline: kernel command line 358c0a839cSHeinrich Schuchardt * 368c0a839cSHeinrich Schuchardt * Parse the ASCII string @cmdline for EFI options, denoted by the efi= 375a17dae4SMatt Fleming * option, e.g. efi=nochunk. 385a17dae4SMatt Fleming * 395a17dae4SMatt Fleming * It should be noted that efi= is parsed in two very different 405a17dae4SMatt Fleming * environments, first in the early boot environment of the EFI boot 415a17dae4SMatt Fleming * stub, and subsequently during the kernel boot. 428c0a839cSHeinrich Schuchardt * 438c0a839cSHeinrich Schuchardt * Return: status code 445a17dae4SMatt Fleming */ 4560f38de7SArd Biesheuvel efi_status_t efi_parse_options(char const *cmdline) 465a17dae4SMatt Fleming { 47a37ca6a2SArvind Sankar size_t len; 4891d150c0SArd Biesheuvel efi_status_t status; 4991d150c0SArd Biesheuvel char *str, *buf; 505a17dae4SMatt Fleming 51a37ca6a2SArvind Sankar if (!cmdline) 52a37ca6a2SArvind Sankar return EFI_SUCCESS; 53a37ca6a2SArvind Sankar 548a8a3237SArvind Sankar len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; 5591d150c0SArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf); 5691d150c0SArd Biesheuvel if (status != EFI_SUCCESS) 5791d150c0SArd Biesheuvel return status; 5891d150c0SArd Biesheuvel 598a8a3237SArvind Sankar memcpy(buf, cmdline, len - 1); 608a8a3237SArvind Sankar buf[len - 1] = '\0'; 618a8a3237SArvind Sankar str = skip_spaces(buf); 6291d150c0SArd Biesheuvel 6391d150c0SArd Biesheuvel while (*str) { 6491d150c0SArd Biesheuvel char *param, *val; 6591d150c0SArd Biesheuvel 6691d150c0SArd Biesheuvel str = next_arg(str, ¶m, &val); 671fd9717dSArvind Sankar if (!val && !strcmp(param, "--")) 681fd9717dSArvind Sankar break; 6991d150c0SArd Biesheuvel 7091d150c0SArd Biesheuvel if (!strcmp(param, "nokaslr")) { 717d4e323dSArd Biesheuvel efi_nokaslr = true; 7291d150c0SArd Biesheuvel } else if (!strcmp(param, "quiet")) { 7323d5b73fSArvind Sankar efi_loglevel = CONSOLE_LOGLEVEL_QUIET; 7479d3219dSArd Biesheuvel } else if (!strcmp(param, "noinitrd")) { 7579d3219dSArd Biesheuvel efi_noinitrd = true; 7691d150c0SArd Biesheuvel } else if (!strcmp(param, "efi") && val) { 7791d150c0SArd Biesheuvel efi_nochunk = parse_option_str(val, "nochunk"); 78d3549a93SArd Biesheuvel efi_novamap |= parse_option_str(val, "novamap"); 79eeff7d63SArd Biesheuvel 8091d150c0SArd Biesheuvel efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && 8191d150c0SArd Biesheuvel parse_option_str(val, "nosoftreserve"); 825a17dae4SMatt Fleming 8391d150c0SArd Biesheuvel if (parse_option_str(val, "disable_early_pci_dma")) 844444f854SMatthew Garrett efi_disable_pci_dma = true; 8591d150c0SArd Biesheuvel if (parse_option_str(val, "no_disable_early_pci_dma")) 864444f854SMatthew Garrett efi_disable_pci_dma = false; 8723d5b73fSArvind Sankar if (parse_option_str(val, "debug")) 8823d5b73fSArvind Sankar efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; 89fffb6804SArvind Sankar } else if (!strcmp(param, "video") && 90fffb6804SArvind Sankar val && strstarts(val, "efifb:")) { 91fffb6804SArvind Sankar efi_parse_option_graphics(val + strlen("efifb:")); 924444f854SMatthew Garrett } 935a17dae4SMatt Fleming } 9491d150c0SArd Biesheuvel efi_bs_call(free_pool, buf); 955a17dae4SMatt Fleming return EFI_SUCCESS; 965a17dae4SMatt Fleming } 97f4f75ad5SArd Biesheuvel 98f4f75ad5SArd Biesheuvel /* 994a568ce2SArvind Sankar * The EFI_LOAD_OPTION descriptor has the following layout: 1004a568ce2SArvind Sankar * u32 Attributes; 1014a568ce2SArvind Sankar * u16 FilePathListLength; 1024a568ce2SArvind Sankar * u16 Description[]; 1034a568ce2SArvind Sankar * efi_device_path_protocol_t FilePathList[]; 1044a568ce2SArvind Sankar * u8 OptionalData[]; 1054a568ce2SArvind Sankar * 1064a568ce2SArvind Sankar * This function validates and unpacks the variable-size data fields. 1074a568ce2SArvind Sankar */ 1084a568ce2SArvind Sankar static 1094a568ce2SArvind Sankar bool efi_load_option_unpack(efi_load_option_unpacked_t *dest, 1104a568ce2SArvind Sankar const efi_load_option_t *src, size_t size) 1114a568ce2SArvind Sankar { 1124a568ce2SArvind Sankar const void *pos; 1134a568ce2SArvind Sankar u16 c; 1144a568ce2SArvind Sankar efi_device_path_protocol_t header; 1154a568ce2SArvind Sankar const efi_char16_t *description; 1164a568ce2SArvind Sankar const efi_device_path_protocol_t *file_path_list; 1174a568ce2SArvind Sankar 1184a568ce2SArvind Sankar if (size < offsetof(efi_load_option_t, variable_data)) 1194a568ce2SArvind Sankar return false; 1204a568ce2SArvind Sankar pos = src->variable_data; 1214a568ce2SArvind Sankar size -= offsetof(efi_load_option_t, variable_data); 1224a568ce2SArvind Sankar 1234a568ce2SArvind Sankar if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0) 1244a568ce2SArvind Sankar return false; 1254a568ce2SArvind Sankar 1264a568ce2SArvind Sankar /* Scan description. */ 1274a568ce2SArvind Sankar description = pos; 1284a568ce2SArvind Sankar do { 1294a568ce2SArvind Sankar if (size < sizeof(c)) 1304a568ce2SArvind Sankar return false; 1314a568ce2SArvind Sankar c = *(const u16 *)pos; 1324a568ce2SArvind Sankar pos += sizeof(c); 1334a568ce2SArvind Sankar size -= sizeof(c); 1344a568ce2SArvind Sankar } while (c != L'\0'); 1354a568ce2SArvind Sankar 1364a568ce2SArvind Sankar /* Scan file_path_list. */ 1374a568ce2SArvind Sankar file_path_list = pos; 1384a568ce2SArvind Sankar do { 1394a568ce2SArvind Sankar if (size < sizeof(header)) 1404a568ce2SArvind Sankar return false; 1414a568ce2SArvind Sankar header = *(const efi_device_path_protocol_t *)pos; 1424a568ce2SArvind Sankar if (header.length < sizeof(header)) 1434a568ce2SArvind Sankar return false; 1444a568ce2SArvind Sankar if (size < header.length) 1454a568ce2SArvind Sankar return false; 1464a568ce2SArvind Sankar pos += header.length; 1474a568ce2SArvind Sankar size -= header.length; 1484a568ce2SArvind Sankar } while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) || 1494a568ce2SArvind Sankar (header.sub_type != EFI_DEV_END_ENTIRE)); 1504a568ce2SArvind Sankar if (pos != (const void *)file_path_list + src->file_path_list_length) 1514a568ce2SArvind Sankar return false; 1524a568ce2SArvind Sankar 1534a568ce2SArvind Sankar dest->attributes = src->attributes; 1544a568ce2SArvind Sankar dest->file_path_list_length = src->file_path_list_length; 1554a568ce2SArvind Sankar dest->description = description; 1564a568ce2SArvind Sankar dest->file_path_list = file_path_list; 1574a568ce2SArvind Sankar dest->optional_data_size = size; 1584a568ce2SArvind Sankar dest->optional_data = size ? pos : NULL; 1594a568ce2SArvind Sankar 1604a568ce2SArvind Sankar return true; 1614a568ce2SArvind Sankar } 1624a568ce2SArvind Sankar 1634a568ce2SArvind Sankar /* 1644a568ce2SArvind Sankar * At least some versions of Dell firmware pass the entire contents of the 1654a568ce2SArvind Sankar * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the 1664a568ce2SArvind Sankar * OptionalData field. 1674a568ce2SArvind Sankar * 1684a568ce2SArvind Sankar * Detect this case and extract OptionalData. 1694a568ce2SArvind Sankar */ 170a241d94bSArd Biesheuvel void efi_apply_loadoptions_quirk(const void **load_options, u32 *load_options_size) 1714a568ce2SArvind Sankar { 1724a568ce2SArvind Sankar const efi_load_option_t *load_option = *load_options; 1734a568ce2SArvind Sankar efi_load_option_unpacked_t load_option_unpacked; 1744a568ce2SArvind Sankar 1754a568ce2SArvind Sankar if (!IS_ENABLED(CONFIG_X86)) 1764a568ce2SArvind Sankar return; 1774a568ce2SArvind Sankar if (!load_option) 1784a568ce2SArvind Sankar return; 1794a568ce2SArvind Sankar if (*load_options_size < sizeof(*load_option)) 1804a568ce2SArvind Sankar return; 1814a568ce2SArvind Sankar if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) 1824a568ce2SArvind Sankar return; 1834a568ce2SArvind Sankar 1844a568ce2SArvind Sankar if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size)) 1854a568ce2SArvind Sankar return; 1864a568ce2SArvind Sankar 1874a568ce2SArvind Sankar efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); 1884a568ce2SArvind Sankar efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); 1894a568ce2SArvind Sankar 1904a568ce2SArvind Sankar *load_options = load_option_unpacked.optional_data; 1914a568ce2SArvind Sankar *load_options_size = load_option_unpacked.optional_data_size; 1924a568ce2SArvind Sankar } 1934a568ce2SArvind Sankar 19456633169SIlias Apalodimas enum efistub_event { 19556633169SIlias Apalodimas EFISTUB_EVT_INITRD, 19671c7adc9SIlias Apalodimas EFISTUB_EVT_LOAD_OPTIONS, 19756633169SIlias Apalodimas EFISTUB_EVT_COUNT, 19856633169SIlias Apalodimas }; 19956633169SIlias Apalodimas 20056633169SIlias Apalodimas #define STR_WITH_SIZE(s) sizeof(s), s 20156633169SIlias Apalodimas 20256633169SIlias Apalodimas static const struct { 20356633169SIlias Apalodimas u32 pcr_index; 20456633169SIlias Apalodimas u32 event_id; 20556633169SIlias Apalodimas u32 event_data_len; 20656633169SIlias Apalodimas u8 event_data[52]; 20756633169SIlias Apalodimas } events[] = { 20856633169SIlias Apalodimas [EFISTUB_EVT_INITRD] = { 20956633169SIlias Apalodimas 9, 21056633169SIlias Apalodimas INITRD_EVENT_TAG_ID, 21156633169SIlias Apalodimas STR_WITH_SIZE("Linux initrd") 21256633169SIlias Apalodimas }, 21371c7adc9SIlias Apalodimas [EFISTUB_EVT_LOAD_OPTIONS] = { 21471c7adc9SIlias Apalodimas 9, 21571c7adc9SIlias Apalodimas LOAD_OPTIONS_EVENT_TAG_ID, 21671c7adc9SIlias Apalodimas STR_WITH_SIZE("LOADED_IMAGE::LoadOptions") 21771c7adc9SIlias Apalodimas }, 21856633169SIlias Apalodimas }; 21956633169SIlias Apalodimas 22056633169SIlias Apalodimas static efi_status_t efi_measure_tagged_event(unsigned long load_addr, 22156633169SIlias Apalodimas unsigned long load_size, 22256633169SIlias Apalodimas enum efistub_event event) 22356633169SIlias Apalodimas { 22456633169SIlias Apalodimas efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; 22556633169SIlias Apalodimas efi_tcg2_protocol_t *tcg2 = NULL; 22656633169SIlias Apalodimas efi_status_t status; 22756633169SIlias Apalodimas 22856633169SIlias Apalodimas efi_bs_call(locate_protocol, &tcg2_guid, NULL, (void **)&tcg2); 22956633169SIlias Apalodimas if (tcg2) { 23056633169SIlias Apalodimas struct efi_measured_event { 23156633169SIlias Apalodimas efi_tcg2_event_t event_data; 23256633169SIlias Apalodimas efi_tcg2_tagged_event_t tagged_event; 23356633169SIlias Apalodimas u8 tagged_event_data[]; 23456633169SIlias Apalodimas } *evt; 23556633169SIlias Apalodimas int size = sizeof(*evt) + events[event].event_data_len; 23656633169SIlias Apalodimas 23756633169SIlias Apalodimas status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, 23856633169SIlias Apalodimas (void **)&evt); 23956633169SIlias Apalodimas if (status != EFI_SUCCESS) 24056633169SIlias Apalodimas goto fail; 24156633169SIlias Apalodimas 24256633169SIlias Apalodimas evt->event_data = (struct efi_tcg2_event){ 24356633169SIlias Apalodimas .event_size = size, 24456633169SIlias Apalodimas .event_header.header_size = sizeof(evt->event_data.event_header), 24556633169SIlias Apalodimas .event_header.header_version = EFI_TCG2_EVENT_HEADER_VERSION, 24656633169SIlias Apalodimas .event_header.pcr_index = events[event].pcr_index, 24756633169SIlias Apalodimas .event_header.event_type = EV_EVENT_TAG, 24856633169SIlias Apalodimas }; 24956633169SIlias Apalodimas 25056633169SIlias Apalodimas evt->tagged_event = (struct efi_tcg2_tagged_event){ 25156633169SIlias Apalodimas .tagged_event_id = events[event].event_id, 25256633169SIlias Apalodimas .tagged_event_data_size = events[event].event_data_len, 25356633169SIlias Apalodimas }; 25456633169SIlias Apalodimas 25556633169SIlias Apalodimas memcpy(evt->tagged_event_data, events[event].event_data, 25656633169SIlias Apalodimas events[event].event_data_len); 25756633169SIlias Apalodimas 25856633169SIlias Apalodimas status = efi_call_proto(tcg2, hash_log_extend_event, 0, 25956633169SIlias Apalodimas load_addr, load_size, &evt->event_data); 26056633169SIlias Apalodimas efi_bs_call(free_pool, evt); 26156633169SIlias Apalodimas 26256633169SIlias Apalodimas if (status != EFI_SUCCESS) 26356633169SIlias Apalodimas goto fail; 26456633169SIlias Apalodimas return EFI_SUCCESS; 26556633169SIlias Apalodimas } 26656633169SIlias Apalodimas 26756633169SIlias Apalodimas return EFI_UNSUPPORTED; 26856633169SIlias Apalodimas fail: 26956633169SIlias Apalodimas efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status); 27056633169SIlias Apalodimas return status; 27156633169SIlias Apalodimas } 27256633169SIlias Apalodimas 2734a568ce2SArvind Sankar /* 274f4f75ad5SArd Biesheuvel * Convert the unicode UEFI command line to ASCII to pass to kernel. 275f4f75ad5SArd Biesheuvel * Size of memory allocated return in *cmd_line_len. 276f4f75ad5SArd Biesheuvel * Returns NULL on error. 277f4f75ad5SArd Biesheuvel */ 27827cd5511SArd Biesheuvel char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) 279f4f75ad5SArd Biesheuvel { 280a241d94bSArd Biesheuvel const efi_char16_t *options = efi_table_attr(image, load_options); 281a241d94bSArd Biesheuvel u32 options_size = efi_table_attr(image, load_options_size); 28280b1bfe1SArvind Sankar int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ 283a241d94bSArd Biesheuvel unsigned long cmdline_addr = 0; 284a241d94bSArd Biesheuvel const efi_char16_t *s2; 28580b1bfe1SArvind Sankar bool in_quote = false; 286f4f75ad5SArd Biesheuvel efi_status_t status; 287a241d94bSArd Biesheuvel u32 options_chars; 288f4f75ad5SArd Biesheuvel 28971c7adc9SIlias Apalodimas if (options_size > 0) 29071c7adc9SIlias Apalodimas efi_measure_tagged_event((unsigned long)options, options_size, 29171c7adc9SIlias Apalodimas EFISTUB_EVT_LOAD_OPTIONS); 29271c7adc9SIlias Apalodimas 293a241d94bSArd Biesheuvel efi_apply_loadoptions_quirk((const void **)&options, &options_size); 294a241d94bSArd Biesheuvel options_chars = options_size / sizeof(efi_char16_t); 2954a568ce2SArvind Sankar 296f4f75ad5SArd Biesheuvel if (options) { 297f4f75ad5SArd Biesheuvel s2 = options; 29880b1bfe1SArvind Sankar while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { 299a241d94bSArd Biesheuvel efi_char16_t c = *s2++; 30015c316bcSArvind Sankar 30180b1bfe1SArvind Sankar if (c < 0x80) { 30215c316bcSArvind Sankar if (c == L'\0' || c == L'\n') 30315c316bcSArvind Sankar break; 30480b1bfe1SArvind Sankar if (c == L'"') 30580b1bfe1SArvind Sankar in_quote = !in_quote; 30680b1bfe1SArvind Sankar else if (!in_quote && isspace((char)c)) 30780b1bfe1SArvind Sankar safe_options_bytes = options_bytes; 30880b1bfe1SArvind Sankar 30980b1bfe1SArvind Sankar options_bytes++; 31080b1bfe1SArvind Sankar continue; 31180b1bfe1SArvind Sankar } 31280b1bfe1SArvind Sankar 31315c316bcSArvind Sankar /* 31415c316bcSArvind Sankar * Get the number of UTF-8 bytes corresponding to a 31515c316bcSArvind Sankar * UTF-16 character. 31615c316bcSArvind Sankar * The first part handles everything in the BMP. 31715c316bcSArvind Sankar */ 31880b1bfe1SArvind Sankar options_bytes += 2 + (c >= 0x800); 31915c316bcSArvind Sankar /* 32015c316bcSArvind Sankar * Add one more byte for valid surrogate pairs. Invalid 32115c316bcSArvind Sankar * surrogates will be replaced with 0xfffd and take up 32215c316bcSArvind Sankar * only 3 bytes. 32315c316bcSArvind Sankar */ 32415c316bcSArvind Sankar if ((c & 0xfc00) == 0xd800) { 32515c316bcSArvind Sankar /* 32615c316bcSArvind Sankar * If the very last word is a high surrogate, 32715c316bcSArvind Sankar * we must ignore it since we can't access the 32815c316bcSArvind Sankar * low surrogate. 32915c316bcSArvind Sankar */ 33004b24409SArvind Sankar if (!options_chars) { 33115c316bcSArvind Sankar options_bytes -= 3; 33215c316bcSArvind Sankar } else if ((*s2 & 0xfc00) == 0xdc00) { 33315c316bcSArvind Sankar options_bytes++; 33404b24409SArvind Sankar options_chars--; 33515c316bcSArvind Sankar s2++; 33615c316bcSArvind Sankar } 33715c316bcSArvind Sankar } 338f4f75ad5SArd Biesheuvel } 33980b1bfe1SArvind Sankar if (options_bytes >= COMMAND_LINE_SIZE) { 34080b1bfe1SArvind Sankar options_bytes = safe_options_bytes; 34180b1bfe1SArvind Sankar efi_err("Command line is too long: truncated to %d bytes\n", 34280b1bfe1SArvind Sankar options_bytes); 34380b1bfe1SArvind Sankar } 344f4f75ad5SArd Biesheuvel } 345f4f75ad5SArd Biesheuvel 346f4f75ad5SArd Biesheuvel options_bytes++; /* NUL termination */ 347f4f75ad5SArd Biesheuvel 34827cd5511SArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, 34927cd5511SArd Biesheuvel (void **)&cmdline_addr); 350f4f75ad5SArd Biesheuvel if (status != EFI_SUCCESS) 351f4f75ad5SArd Biesheuvel return NULL; 352f4f75ad5SArd Biesheuvel 35304b24409SArvind Sankar snprintf((char *)cmdline_addr, options_bytes, "%.*ls", 35404b24409SArvind Sankar options_bytes - 1, options); 355f4f75ad5SArd Biesheuvel 356f4f75ad5SArd Biesheuvel *cmd_line_len = options_bytes; 357f4f75ad5SArd Biesheuvel return (char *)cmdline_addr; 358f4f75ad5SArd Biesheuvel } 359fc07716bSJeffrey Hugo 3608c0a839cSHeinrich Schuchardt /** 3618c0a839cSHeinrich Schuchardt * efi_exit_boot_services() - Exit boot services 3628c0a839cSHeinrich Schuchardt * @handle: handle of the exiting image 3638c0a839cSHeinrich Schuchardt * @priv: argument to be passed to @priv_func 3648c0a839cSHeinrich Schuchardt * @priv_func: function to process the memory map before exiting boot services 3658c0a839cSHeinrich Schuchardt * 366fc07716bSJeffrey Hugo * Handle calling ExitBootServices according to the requirements set out by the 367fc07716bSJeffrey Hugo * spec. Obtains the current memory map, and returns that info after calling 368fc07716bSJeffrey Hugo * ExitBootServices. The client must specify a function to perform any 369fc07716bSJeffrey Hugo * processing of the memory map data prior to ExitBootServices. A client 370fc07716bSJeffrey Hugo * specific structure may be passed to the function via priv. The client 371fc07716bSJeffrey Hugo * function may be called multiple times. 3728c0a839cSHeinrich Schuchardt * 3738c0a839cSHeinrich Schuchardt * Return: status code 374fc07716bSJeffrey Hugo */ 375eab31265SArd Biesheuvel efi_status_t efi_exit_boot_services(void *handle, void *priv, 376fc07716bSJeffrey Hugo efi_exit_boot_map_processing priv_func) 377fc07716bSJeffrey Hugo { 378eab31265SArd Biesheuvel struct efi_boot_memmap *map; 379fc07716bSJeffrey Hugo efi_status_t status; 380fc07716bSJeffrey Hugo 381171539f5SArd Biesheuvel status = efi_get_memory_map(&map, true); 382fc07716bSJeffrey Hugo if (status != EFI_SUCCESS) 383a12b78b5SArd Biesheuvel return status; 384fc07716bSJeffrey Hugo 385cd33a5c1SArd Biesheuvel status = priv_func(map, priv); 386a12b78b5SArd Biesheuvel if (status != EFI_SUCCESS) { 387a12b78b5SArd Biesheuvel efi_bs_call(free_pool, map); 388a12b78b5SArd Biesheuvel return status; 389a12b78b5SArd Biesheuvel } 390fc07716bSJeffrey Hugo 3914444f854SMatthew Garrett if (efi_disable_pci_dma) 3924444f854SMatthew Garrett efi_pci_disable_bridge_busmaster(); 3934444f854SMatthew Garrett 394eab31265SArd Biesheuvel status = efi_bs_call(exit_boot_services, handle, map->map_key); 395fc07716bSJeffrey Hugo 396fc07716bSJeffrey Hugo if (status == EFI_INVALID_PARAMETER) { 397fc07716bSJeffrey Hugo /* 398fc07716bSJeffrey Hugo * The memory map changed between efi_get_memory_map() and 399fc07716bSJeffrey Hugo * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: 400fc07716bSJeffrey Hugo * EFI_BOOT_SERVICES.ExitBootServices we need to get the 401fc07716bSJeffrey Hugo * updated map, and try again. The spec implies one retry 402fc07716bSJeffrey Hugo * should be sufficent, which is confirmed against the EDK2 403fc07716bSJeffrey Hugo * implementation. Per the spec, we can only invoke 404fc07716bSJeffrey Hugo * get_memory_map() and exit_boot_services() - we cannot alloc 405fc07716bSJeffrey Hugo * so efi_get_memory_map() cannot be used, and we must reuse 406fc07716bSJeffrey Hugo * the buffer. For all practical purposes, the headroom in the 407fc07716bSJeffrey Hugo * buffer should account for any changes in the map so the call 408fc07716bSJeffrey Hugo * to get_memory_map() is expected to succeed here. 409fc07716bSJeffrey Hugo */ 410eab31265SArd Biesheuvel map->map_size = map->buff_size; 411966291f6SArd Biesheuvel status = efi_bs_call(get_memory_map, 412eab31265SArd Biesheuvel &map->map_size, 413eab31265SArd Biesheuvel &map->map, 414eab31265SArd Biesheuvel &map->map_key, 415eab31265SArd Biesheuvel &map->desc_size, 416eab31265SArd Biesheuvel &map->desc_ver); 417fc07716bSJeffrey Hugo 418fc07716bSJeffrey Hugo /* exit_boot_services() was called, thus cannot free */ 419fc07716bSJeffrey Hugo if (status != EFI_SUCCESS) 420a12b78b5SArd Biesheuvel return status; 421fc07716bSJeffrey Hugo 422cd33a5c1SArd Biesheuvel status = priv_func(map, priv); 423fc07716bSJeffrey Hugo /* exit_boot_services() was called, thus cannot free */ 424fc07716bSJeffrey Hugo if (status != EFI_SUCCESS) 425a12b78b5SArd Biesheuvel return status; 426fc07716bSJeffrey Hugo 427eab31265SArd Biesheuvel status = efi_bs_call(exit_boot_services, handle, map->map_key); 428fc07716bSJeffrey Hugo } 429fc07716bSJeffrey Hugo 430fc07716bSJeffrey Hugo return status; 431fc07716bSJeffrey Hugo } 43282d736acSMatthew Garrett 4338c0a839cSHeinrich Schuchardt /** 4348c0a839cSHeinrich Schuchardt * get_efi_config_table() - retrieve UEFI configuration table 4358c0a839cSHeinrich Schuchardt * @guid: GUID of the configuration table to be retrieved 4368c0a839cSHeinrich Schuchardt * Return: pointer to the configuration table or NULL 4378c0a839cSHeinrich Schuchardt */ 438cd33a5c1SArd Biesheuvel void *get_efi_config_table(efi_guid_t guid) 43982d736acSMatthew Garrett { 440ccc27ae7SArd Biesheuvel unsigned long tables = efi_table_attr(efi_system_table, tables); 441ccc27ae7SArd Biesheuvel int nr_tables = efi_table_attr(efi_system_table, nr_tables); 442f958efe9SArd Biesheuvel int i; 443f958efe9SArd Biesheuvel 444f958efe9SArd Biesheuvel for (i = 0; i < nr_tables; i++) { 445f958efe9SArd Biesheuvel efi_config_table_t *t = (void *)tables; 446f958efe9SArd Biesheuvel 447f958efe9SArd Biesheuvel if (efi_guidcmp(t->guid, guid) == 0) 44899ea8b1dSArd Biesheuvel return efi_table_attr(t, table); 449f958efe9SArd Biesheuvel 450f958efe9SArd Biesheuvel tables += efi_is_native() ? sizeof(efi_config_table_t) 451f958efe9SArd Biesheuvel : sizeof(efi_config_table_32_t); 452f958efe9SArd Biesheuvel } 453f958efe9SArd Biesheuvel return NULL; 45482d736acSMatthew Garrett } 455dc29da14SArd Biesheuvel 456ec93fc37SArd Biesheuvel /* 457ec93fc37SArd Biesheuvel * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way 458ec93fc37SArd Biesheuvel * for the firmware or bootloader to expose the initrd data directly to the stub 459ec93fc37SArd Biesheuvel * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is 460ec93fc37SArd Biesheuvel * very easy to implement. It is a simple Linux initrd specific conduit between 461ec93fc37SArd Biesheuvel * kernel and firmware, allowing us to put the EFI stub (being part of the 462ec93fc37SArd Biesheuvel * kernel) in charge of where and when to load the initrd, while leaving it up 463ec93fc37SArd Biesheuvel * to the firmware to decide whether it needs to expose its filesystem hierarchy 464ec93fc37SArd Biesheuvel * via EFI protocols. 465ec93fc37SArd Biesheuvel */ 466ec93fc37SArd Biesheuvel static const struct { 467ec93fc37SArd Biesheuvel struct efi_vendor_dev_path vendor; 468ec93fc37SArd Biesheuvel struct efi_generic_dev_path end; 469ec93fc37SArd Biesheuvel } __packed initrd_dev_path = { 470ec93fc37SArd Biesheuvel { 471ec93fc37SArd Biesheuvel { 472ec93fc37SArd Biesheuvel EFI_DEV_MEDIA, 473ec93fc37SArd Biesheuvel EFI_DEV_MEDIA_VENDOR, 474ec93fc37SArd Biesheuvel sizeof(struct efi_vendor_dev_path), 475ec93fc37SArd Biesheuvel }, 476ec93fc37SArd Biesheuvel LINUX_EFI_INITRD_MEDIA_GUID 477ec93fc37SArd Biesheuvel }, { 478ec93fc37SArd Biesheuvel EFI_DEV_END_PATH, 479ec93fc37SArd Biesheuvel EFI_DEV_END_ENTIRE, 480ec93fc37SArd Biesheuvel sizeof(struct efi_generic_dev_path) 481ec93fc37SArd Biesheuvel } 482ec93fc37SArd Biesheuvel }; 483ec93fc37SArd Biesheuvel 484ec93fc37SArd Biesheuvel /** 4858c0a839cSHeinrich Schuchardt * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path 486d981a88cSJialin Zhang * @initrd: pointer of struct to store the address where the initrd was loaded 487d981a88cSJialin Zhang * and the size of the loaded initrd 488ec93fc37SArd Biesheuvel * @max: upper limit for the initrd memory allocation 4898c0a839cSHeinrich Schuchardt * 4908c0a839cSHeinrich Schuchardt * Return: 4918c0a839cSHeinrich Schuchardt * * %EFI_SUCCESS if the initrd was loaded successfully, in which 492ec93fc37SArd Biesheuvel * case @load_addr and @load_size are assigned accordingly 4938c0a839cSHeinrich Schuchardt * * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd device path 4948c0a839cSHeinrich Schuchardt * * %EFI_OUT_OF_RESOURCES if memory allocation failed 4958c0a839cSHeinrich Schuchardt * * %EFI_LOAD_ERROR in all other cases 496ec93fc37SArd Biesheuvel */ 497f61900fdSArvind Sankar static 498f4dc7fffSArd Biesheuvel efi_status_t efi_load_initrd_dev_path(struct linux_efi_initrd *initrd, 499ec93fc37SArd Biesheuvel unsigned long max) 500ec93fc37SArd Biesheuvel { 501ec93fc37SArd Biesheuvel efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; 502ec93fc37SArd Biesheuvel efi_device_path_protocol_t *dp; 503ec93fc37SArd Biesheuvel efi_load_file2_protocol_t *lf2; 504ec93fc37SArd Biesheuvel efi_handle_t handle; 505ec93fc37SArd Biesheuvel efi_status_t status; 506ec93fc37SArd Biesheuvel 507ec93fc37SArd Biesheuvel dp = (efi_device_path_protocol_t *)&initrd_dev_path; 508ec93fc37SArd Biesheuvel status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); 509ec93fc37SArd Biesheuvel if (status != EFI_SUCCESS) 510ec93fc37SArd Biesheuvel return status; 511ec93fc37SArd Biesheuvel 512ec93fc37SArd Biesheuvel status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, 513ec93fc37SArd Biesheuvel (void **)&lf2); 514ec93fc37SArd Biesheuvel if (status != EFI_SUCCESS) 515ec93fc37SArd Biesheuvel return status; 516ec93fc37SArd Biesheuvel 517f4dc7fffSArd Biesheuvel initrd->size = 0; 518f4dc7fffSArd Biesheuvel status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, NULL); 519ec93fc37SArd Biesheuvel if (status != EFI_BUFFER_TOO_SMALL) 520ec93fc37SArd Biesheuvel return EFI_LOAD_ERROR; 521ec93fc37SArd Biesheuvel 522f4dc7fffSArd Biesheuvel status = efi_allocate_pages(initrd->size, &initrd->base, max); 523ec93fc37SArd Biesheuvel if (status != EFI_SUCCESS) 524ec93fc37SArd Biesheuvel return status; 525ec93fc37SArd Biesheuvel 526f4dc7fffSArd Biesheuvel status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, 527f4dc7fffSArd Biesheuvel (void *)initrd->base); 528ec93fc37SArd Biesheuvel if (status != EFI_SUCCESS) { 529f4dc7fffSArd Biesheuvel efi_free(initrd->size, initrd->base); 530ec93fc37SArd Biesheuvel return EFI_LOAD_ERROR; 531ec93fc37SArd Biesheuvel } 532ec93fc37SArd Biesheuvel return EFI_SUCCESS; 533ec93fc37SArd Biesheuvel } 534f61900fdSArvind Sankar 535f61900fdSArvind Sankar static 536f61900fdSArvind Sankar efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, 537f4dc7fffSArd Biesheuvel struct linux_efi_initrd *initrd, 538f61900fdSArvind Sankar unsigned long soft_limit, 539f61900fdSArvind Sankar unsigned long hard_limit) 540f61900fdSArvind Sankar { 541*e346bebbSArd Biesheuvel if (image == NULL) 542f4dc7fffSArd Biesheuvel return EFI_UNSUPPORTED; 543f61900fdSArvind Sankar 544f61900fdSArvind Sankar return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, 545f61900fdSArvind Sankar soft_limit, hard_limit, 546f4dc7fffSArd Biesheuvel &initrd->base, &initrd->size); 547f61900fdSArvind Sankar } 548f61900fdSArvind Sankar 5498c0a839cSHeinrich Schuchardt /** 5508c0a839cSHeinrich Schuchardt * efi_load_initrd() - Load initial RAM disk 5518c0a839cSHeinrich Schuchardt * @image: EFI loaded image protocol 552947228cbSAtish Patra * @soft_limit: preferred address for loading the initrd 553947228cbSAtish Patra * @hard_limit: upper limit address for loading the initrd 5548c0a839cSHeinrich Schuchardt * 5558c0a839cSHeinrich Schuchardt * Return: status code 5568c0a839cSHeinrich Schuchardt */ 557f61900fdSArvind Sankar efi_status_t efi_load_initrd(efi_loaded_image_t *image, 558f61900fdSArvind Sankar unsigned long soft_limit, 559f4dc7fffSArd Biesheuvel unsigned long hard_limit, 560f4dc7fffSArd Biesheuvel const struct linux_efi_initrd **out) 561f61900fdSArvind Sankar { 562f4dc7fffSArd Biesheuvel efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; 563f4dc7fffSArd Biesheuvel efi_status_t status = EFI_SUCCESS; 564f4dc7fffSArd Biesheuvel struct linux_efi_initrd initrd, *tbl; 565f61900fdSArvind Sankar 566f4dc7fffSArd Biesheuvel if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD) || efi_noinitrd) 567f4dc7fffSArd Biesheuvel return EFI_SUCCESS; 568f4dc7fffSArd Biesheuvel 569f4dc7fffSArd Biesheuvel status = efi_load_initrd_dev_path(&initrd, hard_limit); 570f61900fdSArvind Sankar if (status == EFI_SUCCESS) { 571f61900fdSArvind Sankar efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); 57256633169SIlias Apalodimas if (initrd.size > 0 && 57356633169SIlias Apalodimas efi_measure_tagged_event(initrd.base, initrd.size, 57456633169SIlias Apalodimas EFISTUB_EVT_INITRD) == EFI_SUCCESS) 57556633169SIlias Apalodimas efi_info("Measured initrd data into PCR 9\n"); 576f61900fdSArvind Sankar } else if (status == EFI_NOT_FOUND) { 577f4dc7fffSArd Biesheuvel status = efi_load_initrd_cmdline(image, &initrd, soft_limit, 578f4dc7fffSArd Biesheuvel hard_limit); 579f4dc7fffSArd Biesheuvel /* command line loader disabled or no initrd= passed? */ 580f4dc7fffSArd Biesheuvel if (status == EFI_UNSUPPORTED || status == EFI_NOT_READY) 581f4dc7fffSArd Biesheuvel return EFI_SUCCESS; 582f4dc7fffSArd Biesheuvel if (status == EFI_SUCCESS) 583f61900fdSArvind Sankar efi_info("Loaded initrd from command line option\n"); 584f61900fdSArvind Sankar } 585f4dc7fffSArd Biesheuvel if (status != EFI_SUCCESS) 586f4dc7fffSArd Biesheuvel goto failed; 587f046fff8SIlias Apalodimas 588f4dc7fffSArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(initrd), 589f4dc7fffSArd Biesheuvel (void **)&tbl); 590f4dc7fffSArd Biesheuvel if (status != EFI_SUCCESS) 591f4dc7fffSArd Biesheuvel goto free_initrd; 592f4dc7fffSArd Biesheuvel 593f4dc7fffSArd Biesheuvel *tbl = initrd; 594f4dc7fffSArd Biesheuvel status = efi_bs_call(install_configuration_table, &tbl_guid, tbl); 595f4dc7fffSArd Biesheuvel if (status != EFI_SUCCESS) 596f4dc7fffSArd Biesheuvel goto free_tbl; 597f4dc7fffSArd Biesheuvel 598f4dc7fffSArd Biesheuvel if (out) 599f4dc7fffSArd Biesheuvel *out = tbl; 600f4dc7fffSArd Biesheuvel return EFI_SUCCESS; 601f4dc7fffSArd Biesheuvel 602f4dc7fffSArd Biesheuvel free_tbl: 603f4dc7fffSArd Biesheuvel efi_bs_call(free_pool, tbl); 604f4dc7fffSArd Biesheuvel free_initrd: 605f4dc7fffSArd Biesheuvel efi_free(initrd.size, initrd.base); 606f4dc7fffSArd Biesheuvel failed: 607f4dc7fffSArd Biesheuvel efi_err("Failed to load initrd: 0x%lx\n", status); 608f61900fdSArvind Sankar return status; 609f61900fdSArvind Sankar } 61014c574f3SArvind Sankar 6118c0a839cSHeinrich Schuchardt /** 6128c0a839cSHeinrich Schuchardt * efi_wait_for_key() - Wait for key stroke 6138c0a839cSHeinrich Schuchardt * @usec: number of microseconds to wait for key stroke 6148c0a839cSHeinrich Schuchardt * @key: key entered 6158c0a839cSHeinrich Schuchardt * 6168c0a839cSHeinrich Schuchardt * Wait for up to @usec microseconds for a key stroke. 6178c0a839cSHeinrich Schuchardt * 6188c0a839cSHeinrich Schuchardt * Return: status code, EFI_SUCCESS if key received 6198c0a839cSHeinrich Schuchardt */ 62014c574f3SArvind Sankar efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) 62114c574f3SArvind Sankar { 62214c574f3SArvind Sankar efi_event_t events[2], timer; 62314c574f3SArvind Sankar unsigned long index; 62414c574f3SArvind Sankar efi_simple_text_input_protocol_t *con_in; 62514c574f3SArvind Sankar efi_status_t status; 62614c574f3SArvind Sankar 62714c574f3SArvind Sankar con_in = efi_table_attr(efi_system_table, con_in); 62814c574f3SArvind Sankar if (!con_in) 62914c574f3SArvind Sankar return EFI_UNSUPPORTED; 63014c574f3SArvind Sankar efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key)); 63114c574f3SArvind Sankar 63214c574f3SArvind Sankar status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer); 63314c574f3SArvind Sankar if (status != EFI_SUCCESS) 63414c574f3SArvind Sankar return status; 63514c574f3SArvind Sankar 63614c574f3SArvind Sankar status = efi_bs_call(set_timer, timer, EfiTimerRelative, 63714c574f3SArvind Sankar EFI_100NSEC_PER_USEC * usec); 63814c574f3SArvind Sankar if (status != EFI_SUCCESS) 63914c574f3SArvind Sankar return status; 64014c574f3SArvind Sankar efi_set_event_at(events, 1, timer); 64114c574f3SArvind Sankar 64214c574f3SArvind Sankar status = efi_bs_call(wait_for_event, 2, events, &index); 64314c574f3SArvind Sankar if (status == EFI_SUCCESS) { 64414c574f3SArvind Sankar if (index == 0) 64514c574f3SArvind Sankar status = efi_call_proto(con_in, read_keystroke, key); 64614c574f3SArvind Sankar else 64714c574f3SArvind Sankar status = EFI_TIMEOUT; 64814c574f3SArvind Sankar } 64914c574f3SArvind Sankar 65014c574f3SArvind Sankar efi_bs_call(close_event, timer); 65114c574f3SArvind Sankar 65214c574f3SArvind Sankar return status; 65314c574f3SArvind Sankar } 654