xref: /DragonStub/apps/helper.c (revision f412fd2a1a248b546b7085648dece8d908077fab)
1*f412fd2aSLoGin #include "efidef.h"
2*f412fd2aSLoGin #include <efi.h>
3*f412fd2aSLoGin #include <efilib.h>
4*f412fd2aSLoGin #include <lib.h>
5*f412fd2aSLoGin #include <dragonstub/dragonstub.h>
6*f412fd2aSLoGin 
7*f412fd2aSLoGin enum efistub_event {
8*f412fd2aSLoGin 	EFISTUB_EVT_INITRD,
9*f412fd2aSLoGin 	EFISTUB_EVT_LOAD_OPTIONS,
10*f412fd2aSLoGin 	EFISTUB_EVT_COUNT,
11*f412fd2aSLoGin };
12*f412fd2aSLoGin 
13*f412fd2aSLoGin #define STR_WITH_SIZE(s) sizeof(s), s
14*f412fd2aSLoGin 
15*f412fd2aSLoGin static const struct {
16*f412fd2aSLoGin 	u32 pcr_index;
17*f412fd2aSLoGin 	u32 event_id;
18*f412fd2aSLoGin 	u32 event_data_len;
19*f412fd2aSLoGin 	u8 event_data[52];
20*f412fd2aSLoGin } events[] = {
21*f412fd2aSLoGin 	[EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID,
22*f412fd2aSLoGin 				 STR_WITH_SIZE("Linux initrd") },
23*f412fd2aSLoGin 	[EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID,
24*f412fd2aSLoGin 				       STR_WITH_SIZE(
25*f412fd2aSLoGin 					       "LOADED_IMAGE::LoadOptions") },
26*f412fd2aSLoGin };
27*f412fd2aSLoGin 
28*f412fd2aSLoGin static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
29*f412fd2aSLoGin 					     unsigned long load_size,
30*f412fd2aSLoGin 					     enum efistub_event event)
31*f412fd2aSLoGin {
32*f412fd2aSLoGin 	efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
33*f412fd2aSLoGin 	efi_tcg2_protocol_t *tcg2 = NULL;
34*f412fd2aSLoGin 	efi_status_t status;
35*f412fd2aSLoGin 
36*f412fd2aSLoGin 	efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2);
37*f412fd2aSLoGin 	if (tcg2) {
38*f412fd2aSLoGin 		struct efi_measured_event {
39*f412fd2aSLoGin 			efi_tcg2_event_t event_data;
40*f412fd2aSLoGin 			efi_tcg2_tagged_event_t tagged_event;
41*f412fd2aSLoGin 			u8 tagged_event_data[];
42*f412fd2aSLoGin 		} * evt;
43*f412fd2aSLoGin 		int size = sizeof(*evt) + events[event].event_data_len;
44*f412fd2aSLoGin 
45*f412fd2aSLoGin 		status = efi_bs_call(AllocatePool, EfiLoaderData, size,
46*f412fd2aSLoGin 				     (void **)&evt);
47*f412fd2aSLoGin 		if (status != EFI_SUCCESS)
48*f412fd2aSLoGin 			goto fail;
49*f412fd2aSLoGin 
50*f412fd2aSLoGin 		evt->event_data = (struct efi_tcg2_event){
51*f412fd2aSLoGin 			.event_size = size,
52*f412fd2aSLoGin 			.event_header.header_size =
53*f412fd2aSLoGin 				sizeof(evt->event_data.event_header),
54*f412fd2aSLoGin 			.event_header.header_version =
55*f412fd2aSLoGin 				EFI_TCG2_EVENT_HEADER_VERSION,
56*f412fd2aSLoGin 			.event_header.pcr_index = events[event].pcr_index,
57*f412fd2aSLoGin 			.event_header.event_type = EV_EVENT_TAG,
58*f412fd2aSLoGin 		};
59*f412fd2aSLoGin 
60*f412fd2aSLoGin 		evt->tagged_event = (struct efi_tcg2_tagged_event){
61*f412fd2aSLoGin 			.tagged_event_id = events[event].event_id,
62*f412fd2aSLoGin 			.tagged_event_data_size = events[event].event_data_len,
63*f412fd2aSLoGin 		};
64*f412fd2aSLoGin 
65*f412fd2aSLoGin 		memcpy(evt->tagged_event_data, events[event].event_data,
66*f412fd2aSLoGin 		       events[event].event_data_len);
67*f412fd2aSLoGin 
68*f412fd2aSLoGin 		status = efi_call_proto(tcg2, hash_log_extend_event, 0,
69*f412fd2aSLoGin 					load_addr, load_size, &evt->event_data);
70*f412fd2aSLoGin 		efi_bs_call(FreePool, evt);
71*f412fd2aSLoGin 
72*f412fd2aSLoGin 		if (status != EFI_SUCCESS)
73*f412fd2aSLoGin 			goto fail;
74*f412fd2aSLoGin 		return EFI_SUCCESS;
75*f412fd2aSLoGin 	}
76*f412fd2aSLoGin 
77*f412fd2aSLoGin 	return EFI_UNSUPPORTED;
78*f412fd2aSLoGin fail:
79*f412fd2aSLoGin 	efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
80*f412fd2aSLoGin 	return status;
81*f412fd2aSLoGin }
82*f412fd2aSLoGin 
83*f412fd2aSLoGin /*
84*f412fd2aSLoGin  * At least some versions of Dell firmware pass the entire contents of the
85*f412fd2aSLoGin  * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
86*f412fd2aSLoGin  * OptionalData field.
87*f412fd2aSLoGin  *
88*f412fd2aSLoGin  * Detect this case and extract OptionalData.
89*f412fd2aSLoGin  */
90*f412fd2aSLoGin void efi_apply_loadoptions_quirk(const void **load_options,
91*f412fd2aSLoGin 				 u32 *load_options_size)
92*f412fd2aSLoGin {
93*f412fd2aSLoGin #ifndef CONFIG_X86
94*f412fd2aSLoGin 	return;
95*f412fd2aSLoGin #else
96*f412fd2aSLoGin 	const efi_load_option_t *load_option = *load_options;
97*f412fd2aSLoGin 	efi_load_option_unpacked_t load_option_unpacked;
98*f412fd2aSLoGin 	if (!load_option)
99*f412fd2aSLoGin 		return;
100*f412fd2aSLoGin 	if (*load_options_size < sizeof(*load_option))
101*f412fd2aSLoGin 		return;
102*f412fd2aSLoGin 	if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
103*f412fd2aSLoGin 		return;
104*f412fd2aSLoGin 
105*f412fd2aSLoGin 	if (!efi_load_option_unpack(&load_option_unpacked, load_option,
106*f412fd2aSLoGin 				    *load_options_size))
107*f412fd2aSLoGin 		return;
108*f412fd2aSLoGin 
109*f412fd2aSLoGin 	efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
110*f412fd2aSLoGin 	efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
111*f412fd2aSLoGin 
112*f412fd2aSLoGin 	*load_options = load_option_unpacked.optional_data;
113*f412fd2aSLoGin 	*load_options_size = load_option_unpacked.optional_data_size;
114*f412fd2aSLoGin #endif
115*f412fd2aSLoGin }
116*f412fd2aSLoGin 
117*f412fd2aSLoGin /*
118*f412fd2aSLoGin  * Convert the unicode UEFI command line to ASCII to pass to kernel.
119*f412fd2aSLoGin  * Size of memory allocated return in *cmd_line_len.
120*f412fd2aSLoGin  * Returns NULL on error.
121*f412fd2aSLoGin  */
122*f412fd2aSLoGin char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len)
123*f412fd2aSLoGin {
124*f412fd2aSLoGin 	const efi_char16_t *options = efi_table_attr(image, LoadOptions);
125*f412fd2aSLoGin 	u32 options_size = efi_table_attr(image, LoadOptionsSize);
126*f412fd2aSLoGin 	int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
127*f412fd2aSLoGin 	unsigned long cmdline_addr = 0;
128*f412fd2aSLoGin 	const efi_char16_t *s2;
129*f412fd2aSLoGin 	bool in_quote = false;
130*f412fd2aSLoGin 	efi_status_t status;
131*f412fd2aSLoGin 	u32 options_chars;
132*f412fd2aSLoGin 
133*f412fd2aSLoGin 	if (options_size > 0)
134*f412fd2aSLoGin 		efi_measure_tagged_event((unsigned long)options, options_size,
135*f412fd2aSLoGin 					 EFISTUB_EVT_LOAD_OPTIONS);
136*f412fd2aSLoGin 
137*f412fd2aSLoGin 	efi_apply_loadoptions_quirk((const void **)&options, &options_size);
138*f412fd2aSLoGin 	options_chars = options_size / sizeof(efi_char16_t);
139*f412fd2aSLoGin 
140*f412fd2aSLoGin 	if (options) {
141*f412fd2aSLoGin 		s2 = options;
142*f412fd2aSLoGin 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
143*f412fd2aSLoGin 			efi_char16_t c = *s2++;
144*f412fd2aSLoGin 
145*f412fd2aSLoGin 			if (c < 0x80) {
146*f412fd2aSLoGin 				if (c == L'\0' || c == L'\n')
147*f412fd2aSLoGin 					break;
148*f412fd2aSLoGin 				if (c == L'"')
149*f412fd2aSLoGin 					in_quote = !in_quote;
150*f412fd2aSLoGin 				else if (!in_quote && isspace((char)c))
151*f412fd2aSLoGin 					safe_options_bytes = options_bytes;
152*f412fd2aSLoGin 
153*f412fd2aSLoGin 				options_bytes++;
154*f412fd2aSLoGin 				continue;
155*f412fd2aSLoGin 			}
156*f412fd2aSLoGin 
157*f412fd2aSLoGin 			/*
158*f412fd2aSLoGin 			 * Get the number of UTF-8 bytes corresponding to a
159*f412fd2aSLoGin 			 * UTF-16 character.
160*f412fd2aSLoGin 			 * The first part handles everything in the BMP.
161*f412fd2aSLoGin 			 */
162*f412fd2aSLoGin 			options_bytes += 2 + (c >= 0x800);
163*f412fd2aSLoGin 			/*
164*f412fd2aSLoGin 			 * Add one more byte for valid surrogate pairs. Invalid
165*f412fd2aSLoGin 			 * surrogates will be replaced with 0xfffd and take up
166*f412fd2aSLoGin 			 * only 3 bytes.
167*f412fd2aSLoGin 			 */
168*f412fd2aSLoGin 			if ((c & 0xfc00) == 0xd800) {
169*f412fd2aSLoGin 				/*
170*f412fd2aSLoGin 				 * If the very last word is a high surrogate,
171*f412fd2aSLoGin 				 * we must ignore it since we can't access the
172*f412fd2aSLoGin 				 * low surrogate.
173*f412fd2aSLoGin 				 */
174*f412fd2aSLoGin 				if (!options_chars) {
175*f412fd2aSLoGin 					options_bytes -= 3;
176*f412fd2aSLoGin 				} else if ((*s2 & 0xfc00) == 0xdc00) {
177*f412fd2aSLoGin 					options_bytes++;
178*f412fd2aSLoGin 					options_chars--;
179*f412fd2aSLoGin 					s2++;
180*f412fd2aSLoGin 				}
181*f412fd2aSLoGin 			}
182*f412fd2aSLoGin 		}
183*f412fd2aSLoGin 		if (options_bytes >= COMMAND_LINE_SIZE) {
184*f412fd2aSLoGin 			options_bytes = safe_options_bytes;
185*f412fd2aSLoGin 			efi_err("Command line is too long: truncated to %d bytes\n",
186*f412fd2aSLoGin 				options_bytes);
187*f412fd2aSLoGin 		}
188*f412fd2aSLoGin 	}
189*f412fd2aSLoGin 
190*f412fd2aSLoGin 	options_bytes++; /* NUL termination */
191*f412fd2aSLoGin 
192*f412fd2aSLoGin 	status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes,
193*f412fd2aSLoGin 			     (void **)&cmdline_addr);
194*f412fd2aSLoGin 	if (status != EFI_SUCCESS)
195*f412fd2aSLoGin 		return NULL;
196*f412fd2aSLoGin 
197*f412fd2aSLoGin 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
198*f412fd2aSLoGin 		 options_bytes - 1, options);
199*f412fd2aSLoGin 
200*f412fd2aSLoGin 	*cmd_line_len = options_bytes;
201*f412fd2aSLoGin 	return (char *)cmdline_addr;
202*f412fd2aSLoGin }
203