1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
4  *
5  * initrddump.efi saves the initial RAM disk provided via the
6  * EFI_LOAD_FILE2_PROTOCOL.
7  */
8 
9 #include <common.h>
10 #include <efi_api.h>
11 #include <efi_load_initrd.h>
12 
13 #define BUFFER_SIZE 64
14 #define ESC 0x17
15 
16 #define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
17 
18 static struct efi_system_table *systable;
19 static struct efi_boot_services *bs;
20 static struct efi_simple_text_output_protocol *cerr;
21 static struct efi_simple_text_output_protocol *cout;
22 static struct efi_simple_text_input_protocol *cin;
23 static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
24 static const efi_guid_t guid_simple_file_system_protocol =
25 					EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
26 static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
27 static efi_handle_t handle;
28 
29 /*
30  * Device path defined by Linux to identify the handle providing the
31  * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
32  */
33 static const struct efi_initrd_dp initrd_dp = {
34 	.vendor = {
35 		{
36 		   DEVICE_PATH_TYPE_MEDIA_DEVICE,
37 		   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
38 		   sizeof(initrd_dp.vendor),
39 		},
40 		EFI_INITRD_MEDIA_GUID,
41 	},
42 	.end = {
43 		DEVICE_PATH_TYPE_END,
44 		DEVICE_PATH_SUB_TYPE_END,
45 		sizeof(initrd_dp.end),
46 	}
47 };
48 
49 /**
50  * print() - print string
51  *
52  * @string:	text
53  */
print(u16 * string)54 static void print(u16 *string)
55 {
56 	cout->output_string(cout, string);
57 }
58 
59 /**
60  * error() - print error string
61  *
62  * @string:	error text
63  */
error(u16 * string)64 static void error(u16 *string)
65 {
66 	cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
67 	print(string);
68 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
69 }
70 
71 /*
72  * printx() - print hexadecimal number
73  *
74  * @val:	value to print;
75  * @prec:	minimum number of digits to print
76  */
printx(u64 val,u32 prec)77 static void printx(u64 val, u32 prec)
78 {
79 	int i;
80 	u16 c;
81 	u16 buf[16];
82 	u16 *pos = buf;
83 
84 	for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
85 		c = (val >> (4 * i)) & 0x0f;
86 		if (c || pos != buf || !i || i < prec) {
87 			c += '0';
88 			if (c > '9')
89 				c += 'a' - '9' - 1;
90 			*pos++ = c;
91 		}
92 	}
93 	*pos = 0;
94 	print(buf);
95 }
96 
97 /**
98  * efi_input_yn() - get answer to yes/no question
99  *
100  * Return:
101  * y or Y
102  *     EFI_SUCCESS
103  * n or N
104  *     EFI_ACCESS_DENIED
105  * ESC
106  *     EFI_ABORTED
107  */
efi_input_yn(void)108 static efi_status_t efi_input_yn(void)
109 {
110 	struct efi_input_key key = {0};
111 	efi_uintn_t index;
112 	efi_status_t ret;
113 
114 	/* Drain the console input */
115 	ret = cin->reset(cin, true);
116 	for (;;) {
117 		ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
118 		if (ret != EFI_SUCCESS)
119 			continue;
120 		ret = cin->read_key_stroke(cin, &key);
121 		if (ret != EFI_SUCCESS)
122 			continue;
123 		switch (key.scan_code) {
124 		case 0x17: /* Escape */
125 			return EFI_ABORTED;
126 		default:
127 			break;
128 		}
129 		/* Convert to lower case */
130 		switch (key.unicode_char | 0x20) {
131 		case 'y':
132 			return EFI_SUCCESS;
133 		case 'n':
134 			return EFI_ACCESS_DENIED;
135 		default:
136 			break;
137 		}
138 	}
139 }
140 
141 /**
142  * efi_input() - read string from console
143  *
144  * @buffer:		input buffer
145  * @buffer_size:	buffer size
146  * Return:		status code
147  */
efi_input(u16 * buffer,efi_uintn_t buffer_size)148 static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
149 {
150 	struct efi_input_key key = {0};
151 	efi_uintn_t index;
152 	efi_uintn_t pos = 0;
153 	u16 outbuf[2] = L" ";
154 	efi_status_t ret;
155 
156 	/* Drain the console input */
157 	ret = cin->reset(cin, true);
158 	*buffer = 0;
159 	for (;;) {
160 		ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
161 		if (ret != EFI_SUCCESS)
162 			continue;
163 		ret = cin->read_key_stroke(cin, &key);
164 		if (ret != EFI_SUCCESS)
165 			continue;
166 		switch (key.scan_code) {
167 		case 0x17: /* Escape */
168 			print(L"\r\nAborted\r\n");
169 			return EFI_ABORTED;
170 		default:
171 			break;
172 		}
173 		switch (key.unicode_char) {
174 		case 0x08: /* Backspace */
175 			if (pos) {
176 				buffer[pos--] = 0;
177 				print(L"\b \b");
178 			}
179 			break;
180 		case 0x0a: /* Linefeed */
181 		case 0x0d: /* Carriage return */
182 			print(L"\r\n");
183 			return EFI_SUCCESS;
184 		default:
185 			break;
186 		}
187 		/* Ignore surrogate codes */
188 		if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
189 			continue;
190 		if (key.unicode_char >= 0x20 &&
191 		    pos < buffer_size - 1) {
192 			*outbuf = key.unicode_char;
193 			buffer[pos++] = key.unicode_char;
194 			buffer[pos] = 0;
195 			print(outbuf);
196 		}
197 	}
198 }
199 
200 /**
201  * skip_whitespace() - skip over leading whitespace
202  *
203  * @pos:	UTF-16 string
204  * Return:	pointer to first non-whitespace
205  */
skip_whitespace(u16 * pos)206 static u16 *skip_whitespace(u16 *pos)
207 {
208 	for (; *pos && *pos <= 0x20; ++pos)
209 		;
210 	return pos;
211 }
212 
213 /**
214  * starts_with() - check if @string starts with @keyword
215  *
216  * @string:	string to search for keyword
217  * @keyword:	keyword to be searched
218  * Return:	true fi @string starts with the keyword
219  */
starts_with(u16 * string,u16 * keyword)220 static bool starts_with(u16 *string, u16 *keyword)
221 {
222 	for (; *keyword; ++string, ++keyword) {
223 		if (*string != *keyword)
224 			return false;
225 	}
226 	return true;
227 }
228 
229 /**
230  * do_help() - print help
231  */
do_help(void)232 static void do_help(void)
233 {
234 	error(L"load          - show length and CRC32 of initial RAM disk\r\n");
235 	error(L"save <initrd> - save initial RAM disk to file\r\n");
236 	error(L"exit          - exit the shell\r\n");
237 }
238 
239 /**
240  * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
241  *
242  * @initrd:		on return buffer with initial RAM disk
243  * @initrd_size:	size of initial RAM disk
244  * Return:		status code
245  */
get_initrd(void ** initrd,efi_uintn_t * initrd_size)246 static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
247 {
248 	struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
249 	struct efi_load_file_protocol *load_file2_prot;
250 	u64 buffer;
251 	efi_handle_t handle;
252 	efi_status_t ret;
253 
254 	*initrd = NULL;
255 	*initrd_size = 0;
256 	ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
257 	if (ret != EFI_SUCCESS) {
258 		error(L"Load File2 protocol not found\r\n");
259 		return ret;
260 	}
261 	ret = bs->handle_protocol(handle, &load_file2_guid,
262 				 (void **)&load_file2_prot);
263 	ret = load_file2_prot->load_file(load_file2_prot, dp, false,
264 					 initrd_size, NULL);
265 	if (ret != EFI_BUFFER_TOO_SMALL) {
266 		error(L"Load File2 protocol does not provide file length\r\n");
267 		return EFI_LOAD_ERROR;
268 	}
269 	ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
270 				 efi_size_in_pages(*initrd_size), &buffer);
271 	if (ret != EFI_SUCCESS) {
272 		error(L"Out of memory\r\n");
273 		return ret;
274 	}
275 	*initrd = (void *)(uintptr_t)buffer;
276 	ret = load_file2_prot->load_file(load_file2_prot, dp, false,
277 					 initrd_size, *initrd);
278 	if (ret != EFI_SUCCESS) {
279 		error(L"Load File2 protocol failed to provide file\r\n");
280 		bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
281 		return EFI_LOAD_ERROR;
282 	}
283 	return ret;
284 }
285 
286 /**
287  * do_load() - load initial RAM disk and display CRC32 and length
288  *
289  * @filename:	file name
290  * Return:	status code
291  */
do_load(void)292 static efi_status_t do_load(void)
293 {
294 	void *initrd;
295 	efi_uintn_t initrd_size;
296 	u32 crc32;
297 	efi_uintn_t ret;
298 
299 	ret =  get_initrd(&initrd, &initrd_size);
300 	if (ret != EFI_SUCCESS)
301 		return ret;
302 	print(L"length: 0x");
303 	printx(initrd_size, 1);
304 	print(L"\r\n");
305 
306 	ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
307 	if (ret != EFI_SUCCESS) {
308 		error(L"Calculating CRC32 failed\r\n");
309 		return EFI_LOAD_ERROR;
310 	}
311 	print(L"crc32: 0x");
312 	printx(crc32, 8);
313 	print(L"\r\n");
314 
315 	return EFI_SUCCESS;
316 }
317 
318 /**
319  * do_save() - save initial RAM disk
320  *
321  * @filename:	file name
322  * Return:	status code
323  */
do_save(u16 * filename)324 static efi_status_t do_save(u16 *filename)
325 {
326 	struct efi_loaded_image *loaded_image;
327 	struct efi_simple_file_system_protocol *file_system;
328 	struct efi_file_handle *root, *file;
329 	void *initrd;
330 	efi_uintn_t initrd_size;
331 	efi_uintn_t ret;
332 
333 	ret = get_initrd(&initrd, &initrd_size);
334 	if (ret != EFI_SUCCESS)
335 		return ret;
336 
337 	filename = skip_whitespace(filename);
338 
339 	ret = bs->open_protocol(handle, &loaded_image_guid,
340 				(void **)&loaded_image, NULL, NULL,
341 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
342 	if (ret != EFI_SUCCESS) {
343 		error(L"Loaded image protocol not found\r\n");
344 		goto out;
345 	}
346 
347 	/* Open the simple file system protocol */
348 	ret = bs->open_protocol(loaded_image->device_handle,
349 				&guid_simple_file_system_protocol,
350 				(void **)&file_system, NULL, NULL,
351 				EFI_OPEN_PROTOCOL_GET_PROTOCOL);
352 	if (ret != EFI_SUCCESS) {
353 		error(L"Failed to open simple file system protocol\r\n");
354 		goto out;
355 	}
356 
357 	/* Open volume */
358 	ret = file_system->open_volume(file_system, &root);
359 	if (ret != EFI_SUCCESS) {
360 		error(L"Failed to open volume\r\n");
361 		goto out;
362 	}
363 	/* Check if file already exists */
364 	ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
365 	if (ret == EFI_SUCCESS) {
366 		file->close(file);
367 		print(L"Overwrite existing file (y/n)? ");
368 		ret = efi_input_yn();
369 		print(L"\r\n");
370 		if (ret != EFI_SUCCESS) {
371 			root->close(root);
372 			error(L"Aborted by user\r\n");
373 			goto out;
374 		}
375 	}
376 
377 	/* Create file */
378 	ret = root->open(root, &file, filename,
379 			 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
380 			 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
381 	if (ret == EFI_SUCCESS) {
382 		/* Write file */
383 		ret = file->write(file, &initrd_size, initrd);
384 		if (ret != EFI_SUCCESS) {
385 			error(L"Failed to write file\r\n");
386 		} else {
387 			print(filename);
388 			print(L" written\r\n");
389 		}
390 		file->close(file);
391 	} else {
392 		error(L"Failed to open file\r\n");
393 	}
394 	root->close(root);
395 
396 out:
397 	if (initrd)
398 		bs->free_pages((uintptr_t)initrd,
399 			       efi_size_in_pages(initrd_size));
400 	return ret;
401 }
402 
403 /**
404  * efi_main() - entry point of the EFI application.
405  *
406  * @handle:	handle of the loaded image
407  * @systab:	system table
408  * @return:	status code
409  */
efi_main(efi_handle_t image_handle,struct efi_system_table * systab)410 efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
411 			     struct efi_system_table *systab)
412 {
413 	handle = image_handle;
414 	systable = systab;
415 	cerr = systable->std_err;
416 	cout = systable->con_out;
417 	cin = systable->con_in;
418 	bs = systable->boottime;
419 
420 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
421 	cout->clear_screen(cout);
422 	cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
423 	print(L"INITRD Dump\r\n========\r\n\r\n");
424 	cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
425 
426 	for (;;) {
427 		u16 command[BUFFER_SIZE];
428 		u16 *pos;
429 		efi_uintn_t ret;
430 
431 		print(L"=> ");
432 		ret = efi_input(command, sizeof(command));
433 		if (ret == EFI_ABORTED)
434 			break;
435 		pos = skip_whitespace(command);
436 		if (starts_with(pos, L"exit"))
437 			break;
438 		else if (starts_with(pos, L"load"))
439 			do_load();
440 		else if (starts_with(pos, L"save "))
441 			do_save(pos + 5);
442 		else
443 			do_help();
444 	}
445 
446 	cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
447 	cout->clear_screen(cout);
448 	return EFI_SUCCESS;
449 }
450