12604d783SLoGin #include "dragonstub/printk.h"
2f412fd2aSLoGin #include "efidef.h"
3f412fd2aSLoGin #include <efi.h>
4f412fd2aSLoGin #include <efilib.h>
5f412fd2aSLoGin #include <lib.h>
6f412fd2aSLoGin #include <dragonstub/dragonstub.h>
7f412fd2aSLoGin
878b790faSLoGin bool efi_nochunk;
978b790faSLoGin bool efi_nokaslr = true;
1078b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
11*85156066SLoGin bool efi_novamap = false;
1278b790faSLoGin
1378b790faSLoGin static bool efi_noinitrd;
1478b790faSLoGin static bool efi_nosoftreserve;
1578b790faSLoGin static bool efi_disable_pci_dma = false;
1678b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
1778b790faSLoGin
18f412fd2aSLoGin enum efistub_event {
19f412fd2aSLoGin EFISTUB_EVT_INITRD,
20f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS,
21f412fd2aSLoGin EFISTUB_EVT_COUNT,
22f412fd2aSLoGin };
23f412fd2aSLoGin
24f412fd2aSLoGin #define STR_WITH_SIZE(s) sizeof(s), s
25f412fd2aSLoGin
26f412fd2aSLoGin static const struct {
27f412fd2aSLoGin u32 pcr_index;
28f412fd2aSLoGin u32 event_id;
29f412fd2aSLoGin u32 event_data_len;
30f412fd2aSLoGin u8 event_data[52];
31f412fd2aSLoGin } events[] = {
32f412fd2aSLoGin [EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID,
33f412fd2aSLoGin STR_WITH_SIZE("Linux initrd") },
34f412fd2aSLoGin [EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID,
35f412fd2aSLoGin STR_WITH_SIZE(
36f412fd2aSLoGin "LOADED_IMAGE::LoadOptions") },
37f412fd2aSLoGin };
38f412fd2aSLoGin
efi_measure_tagged_event(unsigned long load_addr,unsigned long load_size,enum efistub_event event)39f412fd2aSLoGin static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
40f412fd2aSLoGin unsigned long load_size,
41f412fd2aSLoGin enum efistub_event event)
42f412fd2aSLoGin {
43f412fd2aSLoGin efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
44f412fd2aSLoGin efi_tcg2_protocol_t *tcg2 = NULL;
45f412fd2aSLoGin efi_status_t status;
46f412fd2aSLoGin
47f412fd2aSLoGin efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2);
48f412fd2aSLoGin if (tcg2) {
49f412fd2aSLoGin struct efi_measured_event {
50f412fd2aSLoGin efi_tcg2_event_t event_data;
51f412fd2aSLoGin efi_tcg2_tagged_event_t tagged_event;
52f412fd2aSLoGin u8 tagged_event_data[];
53f412fd2aSLoGin } * evt;
54f412fd2aSLoGin int size = sizeof(*evt) + events[event].event_data_len;
55f412fd2aSLoGin
56f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, size,
57f412fd2aSLoGin (void **)&evt);
58f412fd2aSLoGin if (status != EFI_SUCCESS)
59f412fd2aSLoGin goto fail;
60f412fd2aSLoGin
61f412fd2aSLoGin evt->event_data = (struct efi_tcg2_event){
62f412fd2aSLoGin .event_size = size,
63f412fd2aSLoGin .event_header.header_size =
64f412fd2aSLoGin sizeof(evt->event_data.event_header),
65f412fd2aSLoGin .event_header.header_version =
66f412fd2aSLoGin EFI_TCG2_EVENT_HEADER_VERSION,
67f412fd2aSLoGin .event_header.pcr_index = events[event].pcr_index,
68f412fd2aSLoGin .event_header.event_type = EV_EVENT_TAG,
69f412fd2aSLoGin };
70f412fd2aSLoGin
71f412fd2aSLoGin evt->tagged_event = (struct efi_tcg2_tagged_event){
72f412fd2aSLoGin .tagged_event_id = events[event].event_id,
73f412fd2aSLoGin .tagged_event_data_size = events[event].event_data_len,
74f412fd2aSLoGin };
75f412fd2aSLoGin
76f412fd2aSLoGin memcpy(evt->tagged_event_data, events[event].event_data,
77f412fd2aSLoGin events[event].event_data_len);
78f412fd2aSLoGin
79f412fd2aSLoGin status = efi_call_proto(tcg2, hash_log_extend_event, 0,
80f412fd2aSLoGin load_addr, load_size, &evt->event_data);
81f412fd2aSLoGin efi_bs_call(FreePool, evt);
82f412fd2aSLoGin
83f412fd2aSLoGin if (status != EFI_SUCCESS)
84f412fd2aSLoGin goto fail;
85f412fd2aSLoGin return EFI_SUCCESS;
86f412fd2aSLoGin }
87f412fd2aSLoGin
88f412fd2aSLoGin return EFI_UNSUPPORTED;
89f412fd2aSLoGin fail:
90f412fd2aSLoGin efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
91f412fd2aSLoGin return status;
92f412fd2aSLoGin }
93f412fd2aSLoGin
94f412fd2aSLoGin /*
95f412fd2aSLoGin * At least some versions of Dell firmware pass the entire contents of the
96f412fd2aSLoGin * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
97f412fd2aSLoGin * OptionalData field.
98f412fd2aSLoGin *
99f412fd2aSLoGin * Detect this case and extract OptionalData.
100f412fd2aSLoGin */
efi_apply_loadoptions_quirk(const void ** load_options,u32 * load_options_size)101f412fd2aSLoGin void efi_apply_loadoptions_quirk(const void **load_options,
102f412fd2aSLoGin u32 *load_options_size)
103f412fd2aSLoGin {
104f412fd2aSLoGin #ifndef CONFIG_X86
105f412fd2aSLoGin return;
106f412fd2aSLoGin #else
107f412fd2aSLoGin const efi_load_option_t *load_option = *load_options;
108f412fd2aSLoGin efi_load_option_unpacked_t load_option_unpacked;
109f412fd2aSLoGin if (!load_option)
110f412fd2aSLoGin return;
111f412fd2aSLoGin if (*load_options_size < sizeof(*load_option))
112f412fd2aSLoGin return;
113f412fd2aSLoGin if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
114f412fd2aSLoGin return;
115f412fd2aSLoGin
116f412fd2aSLoGin if (!efi_load_option_unpack(&load_option_unpacked, load_option,
117f412fd2aSLoGin *load_options_size))
118f412fd2aSLoGin return;
119f412fd2aSLoGin
120f412fd2aSLoGin efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
121f412fd2aSLoGin efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
122f412fd2aSLoGin
123f412fd2aSLoGin *load_options = load_option_unpacked.optional_data;
124f412fd2aSLoGin *load_options_size = load_option_unpacked.optional_data_size;
125f412fd2aSLoGin #endif
126f412fd2aSLoGin }
127f412fd2aSLoGin
128f412fd2aSLoGin /*
129f412fd2aSLoGin * Convert the unicode UEFI command line to ASCII to pass to kernel.
130f412fd2aSLoGin * Size of memory allocated return in *cmd_line_len.
131f412fd2aSLoGin * Returns NULL on error.
132f412fd2aSLoGin */
efi_convert_cmdline(EFI_LOADED_IMAGE * image,int * cmd_line_len)133f412fd2aSLoGin char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len)
134f412fd2aSLoGin {
135f412fd2aSLoGin const efi_char16_t *options = efi_table_attr(image, LoadOptions);
136f412fd2aSLoGin u32 options_size = efi_table_attr(image, LoadOptionsSize);
137f412fd2aSLoGin int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
138f412fd2aSLoGin unsigned long cmdline_addr = 0;
139f412fd2aSLoGin const efi_char16_t *s2;
140f412fd2aSLoGin bool in_quote = false;
141f412fd2aSLoGin efi_status_t status;
142f412fd2aSLoGin u32 options_chars;
143f412fd2aSLoGin
144f412fd2aSLoGin if (options_size > 0)
145f412fd2aSLoGin efi_measure_tagged_event((unsigned long)options, options_size,
146f412fd2aSLoGin EFISTUB_EVT_LOAD_OPTIONS);
147f412fd2aSLoGin
148f412fd2aSLoGin efi_apply_loadoptions_quirk((const void **)&options, &options_size);
149f412fd2aSLoGin options_chars = options_size / sizeof(efi_char16_t);
150f412fd2aSLoGin
151f412fd2aSLoGin if (options) {
152f412fd2aSLoGin s2 = options;
153f412fd2aSLoGin while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
154f412fd2aSLoGin efi_char16_t c = *s2++;
155f412fd2aSLoGin
156f412fd2aSLoGin if (c < 0x80) {
157f412fd2aSLoGin if (c == L'\0' || c == L'\n')
158f412fd2aSLoGin break;
159f412fd2aSLoGin if (c == L'"')
160f412fd2aSLoGin in_quote = !in_quote;
161f412fd2aSLoGin else if (!in_quote && isspace((char)c))
162f412fd2aSLoGin safe_options_bytes = options_bytes;
163f412fd2aSLoGin
164f412fd2aSLoGin options_bytes++;
165f412fd2aSLoGin continue;
166f412fd2aSLoGin }
167f412fd2aSLoGin
168f412fd2aSLoGin /*
169f412fd2aSLoGin * Get the number of UTF-8 bytes corresponding to a
170f412fd2aSLoGin * UTF-16 character.
171f412fd2aSLoGin * The first part handles everything in the BMP.
172f412fd2aSLoGin */
173f412fd2aSLoGin options_bytes += 2 + (c >= 0x800);
174f412fd2aSLoGin /*
175f412fd2aSLoGin * Add one more byte for valid surrogate pairs. Invalid
176f412fd2aSLoGin * surrogates will be replaced with 0xfffd and take up
177f412fd2aSLoGin * only 3 bytes.
178f412fd2aSLoGin */
179f412fd2aSLoGin if ((c & 0xfc00) == 0xd800) {
180f412fd2aSLoGin /*
181f412fd2aSLoGin * If the very last word is a high surrogate,
182f412fd2aSLoGin * we must ignore it since we can't access the
183f412fd2aSLoGin * low surrogate.
184f412fd2aSLoGin */
185f412fd2aSLoGin if (!options_chars) {
186f412fd2aSLoGin options_bytes -= 3;
187f412fd2aSLoGin } else if ((*s2 & 0xfc00) == 0xdc00) {
188f412fd2aSLoGin options_bytes++;
189f412fd2aSLoGin options_chars--;
190f412fd2aSLoGin s2++;
191f412fd2aSLoGin }
192f412fd2aSLoGin }
193f412fd2aSLoGin }
194f412fd2aSLoGin if (options_bytes >= COMMAND_LINE_SIZE) {
195f412fd2aSLoGin options_bytes = safe_options_bytes;
196f412fd2aSLoGin efi_err("Command line is too long: truncated to %d bytes\n",
197f412fd2aSLoGin options_bytes);
198f412fd2aSLoGin }
199f412fd2aSLoGin }
200f412fd2aSLoGin
201f412fd2aSLoGin options_bytes++; /* NUL termination */
202f412fd2aSLoGin
203f412fd2aSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes,
204f412fd2aSLoGin (void **)&cmdline_addr);
205f412fd2aSLoGin if (status != EFI_SUCCESS)
206f412fd2aSLoGin return NULL;
207f412fd2aSLoGin
208f412fd2aSLoGin snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
209f412fd2aSLoGin options_bytes - 1, options);
210f412fd2aSLoGin
211f412fd2aSLoGin *cmd_line_len = options_bytes;
212f412fd2aSLoGin return (char *)cmdline_addr;
213f412fd2aSLoGin }
21478b790faSLoGin
21578b790faSLoGin /**
21678b790faSLoGin * parse_option_str - Parse a string and check an option is set or not
21778b790faSLoGin * @str: String to be parsed
21878b790faSLoGin * @option: option name
21978b790faSLoGin *
22078b790faSLoGin * This function parses a string containing a comma-separated list of
22178b790faSLoGin * strings like a=b,c.
22278b790faSLoGin *
22378b790faSLoGin * Return true if there's such option in the string, or return false.
22478b790faSLoGin */
parse_option_str(const char * str,const char * option)22578b790faSLoGin bool parse_option_str(const char *str, const char *option)
22678b790faSLoGin {
22778b790faSLoGin while (*str) {
22878b790faSLoGin if (!strncmp(str, option, strlen(option))) {
22978b790faSLoGin str += strlen(option);
23078b790faSLoGin if (!*str || *str == ',')
23178b790faSLoGin return true;
23278b790faSLoGin }
23378b790faSLoGin
23478b790faSLoGin while (*str && *str != ',')
23578b790faSLoGin str++;
23678b790faSLoGin
23778b790faSLoGin if (*str == ',')
23878b790faSLoGin str++;
23978b790faSLoGin }
24078b790faSLoGin
24178b790faSLoGin return false;
24278b790faSLoGin }
24378b790faSLoGin
24478b790faSLoGin /**
24578b790faSLoGin * efi_parse_options() - Parse EFI command line options
24678b790faSLoGin * @cmdline: kernel command line
24778b790faSLoGin *
24878b790faSLoGin * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
24978b790faSLoGin * option, e.g. efi=nochunk.
25078b790faSLoGin *
25178b790faSLoGin * It should be noted that efi= is parsed in two very different
25278b790faSLoGin * environments, first in the early boot environment of the EFI boot
25378b790faSLoGin * stub, and subsequently during the kernel boot.
25478b790faSLoGin *
25578b790faSLoGin * Return: status code
25678b790faSLoGin */
efi_parse_options(char const * cmdline)25778b790faSLoGin efi_status_t efi_parse_options(char const *cmdline)
25878b790faSLoGin {
25978b790faSLoGin size_t len;
26078b790faSLoGin efi_status_t status;
26178b790faSLoGin char *str, *buf;
26278b790faSLoGin
26378b790faSLoGin if (!cmdline)
26478b790faSLoGin return EFI_SUCCESS;
26578b790faSLoGin
26678b790faSLoGin len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
26778b790faSLoGin status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf);
26878b790faSLoGin if (status != EFI_SUCCESS)
26978b790faSLoGin return status;
27078b790faSLoGin
27178b790faSLoGin memcpy(buf, cmdline, len - 1);
27278b790faSLoGin buf[len - 1] = '\0';
27378b790faSLoGin str = skip_spaces(buf);
27478b790faSLoGin
27578b790faSLoGin while (*str) {
27678b790faSLoGin char *param, *val;
27778b790faSLoGin
27878b790faSLoGin str = next_arg(str, ¶m, &val);
27978b790faSLoGin if (!val && !strcmp(param, "--"))
28078b790faSLoGin break;
28178b790faSLoGin
28278b790faSLoGin if (!strcmp(param, "nokaslr")) {
28378b790faSLoGin efi_nokaslr = true;
28478b790faSLoGin } else if (!strcmp(param, "quiet")) {
28578b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
28678b790faSLoGin } else if (!strcmp(param, "noinitrd")) {
28778b790faSLoGin efi_noinitrd = true;
28878b790faSLoGin }
28978b790faSLoGin #ifdef CONFIG_X86_64
29078b790faSLoGin else if (IS_ENABLED(CONFIG_X86_64) &&
29178b790faSLoGin !strcmp(param, "no5lvl")) {
29278b790faSLoGin efi_no5lvl = true;
29378b790faSLoGin }
29478b790faSLoGin #endif
29578b790faSLoGin else if (!strcmp(param, "efi") && val) {
29678b790faSLoGin efi_nochunk = parse_option_str(val, "nochunk");
29778b790faSLoGin efi_novamap |= parse_option_str(val, "novamap");
29878b790faSLoGin
29978b790faSLoGin // efi_nosoftreserve =
30078b790faSLoGin // IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
30178b790faSLoGin // parse_option_str(val, "nosoftreserve");
30278b790faSLoGin efi_nosoftreserve = false;
30378b790faSLoGin
30478b790faSLoGin if (parse_option_str(val, "disable_early_pci_dma"))
30578b790faSLoGin efi_disable_pci_dma = true;
30678b790faSLoGin if (parse_option_str(val, "no_disable_early_pci_dma"))
30778b790faSLoGin efi_disable_pci_dma = false;
30878b790faSLoGin if (parse_option_str(val, "debug")) {
30978b790faSLoGin // efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
31078b790faSLoGin }
31178b790faSLoGin } else if (!strcmp(param, "video") && val &&
31278b790faSLoGin strstarts(val, "efifb:")) {
31378b790faSLoGin // efi_parse_option_graphics(val + strlen("efifb:"));
31478b790faSLoGin }
31578b790faSLoGin }
31678b790faSLoGin efi_bs_call(FreePool, buf);
31778b790faSLoGin return EFI_SUCCESS;
31878b790faSLoGin }
3193e6106c4SLoGin
3203e6106c4SLoGin /**
3213e6106c4SLoGin * get_efi_config_table() - retrieve UEFI configuration table
3223e6106c4SLoGin * @guid: GUID of the configuration table to be retrieved
3233e6106c4SLoGin * Return: pointer to the configuration table or NULL
3243e6106c4SLoGin */
get_efi_config_table(efi_guid_t guid)3253e6106c4SLoGin void *get_efi_config_table(efi_guid_t guid)
3263e6106c4SLoGin {
3273e6106c4SLoGin efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable);
3283e6106c4SLoGin int nr_tables = efi_table_attr(ST, NumberOfTableEntries);
3293e6106c4SLoGin int i;
3303e6106c4SLoGin
3313e6106c4SLoGin for (i = 0; i < nr_tables; i++) {
3323e6106c4SLoGin efi_config_table_t *t = (void *)tables;
3333e6106c4SLoGin // print_efi_guid(&t->VendorGuid);
3343e6106c4SLoGin if (efi_guidcmp(t->VendorGuid, guid) == 0)
3353e6106c4SLoGin return efi_table_attr(t, VendorTable);
3363e6106c4SLoGin
3373e6106c4SLoGin tables++;
3383e6106c4SLoGin }
3393e6106c4SLoGin return NULL;
3403e6106c4SLoGin }
341823f0493SLoGin
342823f0493SLoGin /**
343823f0493SLoGin * efi_exit_boot_services() - Exit boot services
344823f0493SLoGin * @handle: handle of the exiting image
345823f0493SLoGin * @priv: argument to be passed to @priv_func
346823f0493SLoGin * @priv_func: function to process the memory map before exiting boot services
347823f0493SLoGin *
348823f0493SLoGin * Handle calling ExitBootServices according to the requirements set out by the
349823f0493SLoGin * spec. Obtains the current memory map, and returns that info after calling
350823f0493SLoGin * ExitBootServices. The client must specify a function to perform any
351823f0493SLoGin * processing of the memory map data prior to ExitBootServices. A client
352823f0493SLoGin * specific structure may be passed to the function via priv. The client
353823f0493SLoGin * function may be called multiple times.
354823f0493SLoGin *
355823f0493SLoGin * Return: status code
356823f0493SLoGin */
efi_exit_boot_services(void * handle,void * priv,efi_exit_boot_map_processing priv_func)357823f0493SLoGin efi_status_t efi_exit_boot_services(void *handle, void *priv,
358823f0493SLoGin efi_exit_boot_map_processing priv_func)
359823f0493SLoGin {
360823f0493SLoGin struct efi_boot_memmap *map;
361823f0493SLoGin efi_status_t status;
362823f0493SLoGin
363823f0493SLoGin if (efi_disable_pci_dma) {
364823f0493SLoGin efi_todo("efi_exit_boot_services:: efi_disable_pci_dma: efi_pci_disable_bridge_busmaster");
365823f0493SLoGin // efi_pci_disable_bridge_busmaster();
366823f0493SLoGin }
367823f0493SLoGin
368823f0493SLoGin status = efi_get_memory_map(&map, true);
369823f0493SLoGin if (status != EFI_SUCCESS)
370823f0493SLoGin return status;
371823f0493SLoGin efi_debug("before priv_func\n");
372823f0493SLoGin status = priv_func(map, priv);
373823f0493SLoGin if (status != EFI_SUCCESS) {
374823f0493SLoGin
375823f0493SLoGin efi_bs_call(FreePool, map);
376823f0493SLoGin return status;
377823f0493SLoGin }
378823f0493SLoGin
379823f0493SLoGin efi_debug("before ExitBootServices, handle=%p, map_key=%p\n", handle, map->map_key);
3802604d783SLoGin efi_debug("BS->ExitBootServices=%p\n", BS->ExitBootServices);
3812604d783SLoGin efi_debug("ST->BS->ExitBootServices=%p\n", ST->BootServices->ExitBootServices);
382823f0493SLoGin status = efi_bs_call(ExitBootServices, handle, map->map_key);
3832604d783SLoGin // exit之后不能再使用打印函数,否则会出现错误
384823f0493SLoGin
385823f0493SLoGin if (status == EFI_INVALID_PARAMETER) {
386823f0493SLoGin /*
387823f0493SLoGin * The memory map changed between efi_get_memory_map() and
388823f0493SLoGin * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4:
389823f0493SLoGin * EFI_BOOT_SERVICES.ExitBootServices we need to get the
390823f0493SLoGin * updated map, and try again. The spec implies one retry
391823f0493SLoGin * should be sufficent, which is confirmed against the EDK2
392823f0493SLoGin * implementation. Per the spec, we can only invoke
393823f0493SLoGin * get_memory_map() and exit_boot_services() - we cannot alloc
394823f0493SLoGin * so efi_get_memory_map() cannot be used, and we must reuse
395823f0493SLoGin * the buffer. For all practical purposes, the headroom in the
396823f0493SLoGin * buffer should account for any changes in the map so the call
397823f0493SLoGin * to get_memory_map() is expected to succeed here.
398823f0493SLoGin */
399823f0493SLoGin map->map_size = map->buff_size;
400823f0493SLoGin status = efi_bs_call(GetMemoryMap, &map->map_size, &map->map,
401823f0493SLoGin &map->map_key, &map->desc_size,
402823f0493SLoGin &map->desc_ver);
403823f0493SLoGin
404823f0493SLoGin /* exit_boot_services() was called, thus cannot free */
405823f0493SLoGin if (status != EFI_SUCCESS)
406823f0493SLoGin return status;
407823f0493SLoGin
408823f0493SLoGin status = priv_func(map, priv);
409823f0493SLoGin /* exit_boot_services() was called, thus cannot free */
410823f0493SLoGin if (status != EFI_SUCCESS)
411823f0493SLoGin return status;
412823f0493SLoGin
413823f0493SLoGin status = efi_bs_call(ExitBootServices, handle, map->map_key);
414823f0493SLoGin }
415823f0493SLoGin
416823f0493SLoGin return status;
417823f0493SLoGin }