blob: 0004b6b042b477555eb13bd54c1e80ab9a7aee42 [file] [log] [blame]
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +01001// 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.
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +01007 *
8 * Specifying 'nocolor' as load option data suppresses colored output and
9 * clearing of the screen.
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010010 */
11
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010012#include <efi_api.h>
13#include <efi_load_initrd.h>
14
15#define BUFFER_SIZE 64
16#define ESC 0x17
17
18#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
19
20static struct efi_system_table *systable;
21static struct efi_boot_services *bs;
22static struct efi_simple_text_output_protocol *cerr;
23static struct efi_simple_text_output_protocol *cout;
24static struct efi_simple_text_input_protocol *cin;
25static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
26static const efi_guid_t guid_simple_file_system_protocol =
27 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
28static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
29static efi_handle_t handle;
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010030static bool nocolor;
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010031
32/*
33 * Device path defined by Linux to identify the handle providing the
34 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
35 */
36static const struct efi_initrd_dp initrd_dp = {
37 .vendor = {
38 {
39 DEVICE_PATH_TYPE_MEDIA_DEVICE,
40 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
41 sizeof(initrd_dp.vendor),
42 },
43 EFI_INITRD_MEDIA_GUID,
44 },
45 .end = {
46 DEVICE_PATH_TYPE_END,
47 DEVICE_PATH_SUB_TYPE_END,
48 sizeof(initrd_dp.end),
49 }
50};
51
52/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010053 * color() - set foreground color
54 *
55 * @color: foreground color
56 */
57static void color(u8 color)
58{
59 if (!nocolor)
60 cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
61}
62
63/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010064 * print() - print string
65 *
66 * @string: text
67 */
68static void print(u16 *string)
69{
70 cout->output_string(cout, string);
71}
72
73/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010074 * cls() - clear screen
75 */
76static void cls(void)
77{
78 if (nocolor)
79 print(u"\r\n");
80 else
81 cout->clear_screen(cout);
82}
83
84/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010085 * error() - print error string
86 *
87 * @string: error text
88 */
89static void error(u16 *string)
90{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010091 color(EFI_LIGHTRED);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010092 print(string);
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010093 color(EFI_LIGHTBLUE);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010094}
95
96/*
97 * printx() - print hexadecimal number
98 *
99 * @val: value to print;
100 * @prec: minimum number of digits to print
101 */
102static void printx(u64 val, u32 prec)
103{
104 int i;
105 u16 c;
106 u16 buf[16];
107 u16 *pos = buf;
108
109 for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
110 c = (val >> (4 * i)) & 0x0f;
111 if (c || pos != buf || !i || i < prec) {
112 c += '0';
113 if (c > '9')
114 c += 'a' - '9' - 1;
115 *pos++ = c;
116 }
117 }
118 *pos = 0;
119 print(buf);
120}
121
122/**
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100123 * efi_drain_input() - drain console input
124 */
125static void efi_drain_input(void)
126{
127 cin->reset(cin, true);
128}
129
130/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100131 * efi_input_yn() - get answer to yes/no question
132 *
133 * Return:
134 * y or Y
135 * EFI_SUCCESS
136 * n or N
137 * EFI_ACCESS_DENIED
138 * ESC
139 * EFI_ABORTED
140 */
141static efi_status_t efi_input_yn(void)
142{
143 struct efi_input_key key = {0};
144 efi_uintn_t index;
145 efi_status_t ret;
146
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100147 for (;;) {
148 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
149 if (ret != EFI_SUCCESS)
150 continue;
151 ret = cin->read_key_stroke(cin, &key);
152 if (ret != EFI_SUCCESS)
153 continue;
154 switch (key.scan_code) {
155 case 0x17: /* Escape */
156 return EFI_ABORTED;
157 default:
158 break;
159 }
160 /* Convert to lower case */
161 switch (key.unicode_char | 0x20) {
162 case 'y':
163 return EFI_SUCCESS;
164 case 'n':
165 return EFI_ACCESS_DENIED;
166 default:
167 break;
168 }
169 }
170}
171
172/**
173 * efi_input() - read string from console
174 *
175 * @buffer: input buffer
176 * @buffer_size: buffer size
177 * Return: status code
178 */
179static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
180{
181 struct efi_input_key key = {0};
182 efi_uintn_t index;
183 efi_uintn_t pos = 0;
Simon Glass90975372022-01-23 12:55:12 -0700184 u16 outbuf[2] = u" ";
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100185 efi_status_t ret;
186
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100187 *buffer = 0;
188 for (;;) {
189 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
190 if (ret != EFI_SUCCESS)
191 continue;
192 ret = cin->read_key_stroke(cin, &key);
193 if (ret != EFI_SUCCESS)
194 continue;
195 switch (key.scan_code) {
196 case 0x17: /* Escape */
Simon Glass90975372022-01-23 12:55:12 -0700197 print(u"\r\nAborted\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100198 return EFI_ABORTED;
199 default:
200 break;
201 }
202 switch (key.unicode_char) {
203 case 0x08: /* Backspace */
204 if (pos) {
205 buffer[pos--] = 0;
Simon Glass90975372022-01-23 12:55:12 -0700206 print(u"\b \b");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100207 }
208 break;
209 case 0x0a: /* Linefeed */
210 case 0x0d: /* Carriage return */
Simon Glass90975372022-01-23 12:55:12 -0700211 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100212 return EFI_SUCCESS;
213 default:
214 break;
215 }
216 /* Ignore surrogate codes */
217 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
218 continue;
219 if (key.unicode_char >= 0x20 &&
220 pos < buffer_size - 1) {
221 *outbuf = key.unicode_char;
222 buffer[pos++] = key.unicode_char;
223 buffer[pos] = 0;
224 print(outbuf);
225 }
226 }
227}
228
229/**
230 * skip_whitespace() - skip over leading whitespace
231 *
232 * @pos: UTF-16 string
233 * Return: pointer to first non-whitespace
234 */
235static u16 *skip_whitespace(u16 *pos)
236{
237 for (; *pos && *pos <= 0x20; ++pos)
238 ;
239 return pos;
240}
241
242/**
243 * starts_with() - check if @string starts with @keyword
244 *
245 * @string: string to search for keyword
246 * @keyword: keyword to be searched
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100247 * Return: true if @string starts with the keyword
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100248 */
249static bool starts_with(u16 *string, u16 *keyword)
250{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100251 if (!string || !keyword)
252 return false;
253
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100254 for (; *keyword; ++string, ++keyword) {
255 if (*string != *keyword)
256 return false;
257 }
258 return true;
259}
260
261/**
262 * do_help() - print help
263 */
264static void do_help(void)
265{
Simon Glass90975372022-01-23 12:55:12 -0700266 error(u"load - show length and CRC32 of initial RAM disk\r\n");
267 error(u"save <initrd> - save initial RAM disk to file\r\n");
268 error(u"exit - exit the shell\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100269}
270
271/**
272 * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
273 *
274 * @initrd: on return buffer with initial RAM disk
275 * @initrd_size: size of initial RAM disk
276 * Return: status code
277 */
278static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
279{
280 struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
281 struct efi_load_file_protocol *load_file2_prot;
282 u64 buffer;
283 efi_handle_t handle;
284 efi_status_t ret;
285
286 *initrd = NULL;
287 *initrd_size = 0;
288 ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
289 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700290 error(u"Load File2 protocol not found\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100291 return ret;
292 }
Heinrich Schuchardt1a97b512023-04-04 07:23:53 +0200293 ret = bs->open_protocol(handle, &load_file2_guid,
294 (void **)&load_file2_prot, NULL, NULL,
295 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100296 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
297 initrd_size, NULL);
298 if (ret != EFI_BUFFER_TOO_SMALL) {
Simon Glass90975372022-01-23 12:55:12 -0700299 error(u"Load File2 protocol does not provide file length\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100300 return EFI_LOAD_ERROR;
301 }
302 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
303 efi_size_in_pages(*initrd_size), &buffer);
304 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700305 error(u"Out of memory\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100306 return ret;
307 }
Heinrich Schuchardt22f038f2021-03-14 10:12:01 +0100308 *initrd = (void *)(uintptr_t)buffer;
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100309 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
310 initrd_size, *initrd);
311 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700312 error(u"Load File2 protocol failed to provide file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100313 bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
314 return EFI_LOAD_ERROR;
315 }
316 return ret;
317}
318
319/**
320 * do_load() - load initial RAM disk and display CRC32 and length
321 *
322 * @filename: file name
323 * Return: status code
324 */
325static efi_status_t do_load(void)
326{
327 void *initrd;
328 efi_uintn_t initrd_size;
329 u32 crc32;
330 efi_uintn_t ret;
331
332 ret = get_initrd(&initrd, &initrd_size);
333 if (ret != EFI_SUCCESS)
334 return ret;
Simon Glass90975372022-01-23 12:55:12 -0700335 print(u"length: 0x");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100336 printx(initrd_size, 1);
Simon Glass90975372022-01-23 12:55:12 -0700337 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100338
339 ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
340 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700341 error(u"Calculating CRC32 failed\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100342 return EFI_LOAD_ERROR;
343 }
Simon Glass90975372022-01-23 12:55:12 -0700344 print(u"crc32: 0x");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100345 printx(crc32, 8);
Simon Glass90975372022-01-23 12:55:12 -0700346 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100347
348 return EFI_SUCCESS;
349}
350
351/**
352 * do_save() - save initial RAM disk
353 *
354 * @filename: file name
355 * Return: status code
356 */
357static efi_status_t do_save(u16 *filename)
358{
359 struct efi_loaded_image *loaded_image;
360 struct efi_simple_file_system_protocol *file_system;
361 struct efi_file_handle *root, *file;
362 void *initrd;
363 efi_uintn_t initrd_size;
364 efi_uintn_t ret;
365
366 ret = get_initrd(&initrd, &initrd_size);
367 if (ret != EFI_SUCCESS)
368 return ret;
369
370 filename = skip_whitespace(filename);
371
372 ret = bs->open_protocol(handle, &loaded_image_guid,
373 (void **)&loaded_image, NULL, NULL,
374 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
375 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700376 error(u"Loaded image protocol not found\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100377 goto out;
378 }
379
380 /* Open the simple file system protocol */
381 ret = bs->open_protocol(loaded_image->device_handle,
382 &guid_simple_file_system_protocol,
383 (void **)&file_system, NULL, NULL,
384 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
385 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700386 error(u"Failed to open simple file system protocol\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100387 goto out;
388 }
389
390 /* Open volume */
391 ret = file_system->open_volume(file_system, &root);
392 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700393 error(u"Failed to open volume\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100394 goto out;
395 }
396 /* Check if file already exists */
397 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
398 if (ret == EFI_SUCCESS) {
399 file->close(file);
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100400 efi_drain_input();
Simon Glass90975372022-01-23 12:55:12 -0700401 print(u"Overwrite existing file (y/n)? ");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100402 ret = efi_input_yn();
Simon Glass90975372022-01-23 12:55:12 -0700403 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100404 if (ret != EFI_SUCCESS) {
405 root->close(root);
Simon Glass90975372022-01-23 12:55:12 -0700406 error(u"Aborted by user\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100407 goto out;
408 }
409 }
410
411 /* Create file */
412 ret = root->open(root, &file, filename,
413 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
414 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
415 if (ret == EFI_SUCCESS) {
416 /* Write file */
417 ret = file->write(file, &initrd_size, initrd);
418 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700419 error(u"Failed to write file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100420 } else {
421 print(filename);
Simon Glass90975372022-01-23 12:55:12 -0700422 print(u" written\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100423 }
424 file->close(file);
425 } else {
Simon Glass90975372022-01-23 12:55:12 -0700426 error(u"Failed to open file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100427 }
428 root->close(root);
429
430out:
431 if (initrd)
432 bs->free_pages((uintptr_t)initrd,
433 efi_size_in_pages(initrd_size));
434 return ret;
435}
436
437/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100438 * get_load_options() - get load options
439 *
440 * Return: load options or NULL
441 */
Heinrich Schuchardt1e9fa492023-02-10 08:09:40 +0100442static u16 *get_load_options(void)
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100443{
444 efi_status_t ret;
445 struct efi_loaded_image *loaded_image;
446
447 ret = bs->open_protocol(handle, &loaded_image_guid,
448 (void **)&loaded_image, NULL, NULL,
449 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
450 if (ret != EFI_SUCCESS) {
451 error(u"Loaded image protocol not found\r\n");
452 return NULL;
453 }
454
455 if (!loaded_image->load_options_size || !loaded_image->load_options)
456 return NULL;
457
458 return loaded_image->load_options;
459}
460
461/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100462 * efi_main() - entry point of the EFI application.
463 *
464 * @handle: handle of the loaded image
465 * @systab: system table
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +0100466 * Return: status code
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100467 */
468efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
469 struct efi_system_table *systab)
470{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100471 u16 *load_options;
472
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100473 handle = image_handle;
474 systable = systab;
475 cerr = systable->std_err;
476 cout = systable->con_out;
477 cin = systable->con_in;
478 bs = systable->boottime;
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100479 load_options = get_load_options();
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100480
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100481 if (starts_with(load_options, u"nocolor"))
482 nocolor = true;
483
484 color(EFI_WHITE);
485 cls();
Heinrich Schuchardtc348cf22022-03-03 08:13:53 +0100486 print(u"INITRD Dump\r\n===========\r\n\r\n");
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100487 color(EFI_LIGHTBLUE);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100488
489 for (;;) {
490 u16 command[BUFFER_SIZE];
491 u16 *pos;
492 efi_uintn_t ret;
493
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100494 efi_drain_input();
Simon Glass90975372022-01-23 12:55:12 -0700495 print(u"=> ");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100496 ret = efi_input(command, sizeof(command));
497 if (ret == EFI_ABORTED)
498 break;
499 pos = skip_whitespace(command);
Simon Glass90975372022-01-23 12:55:12 -0700500 if (starts_with(pos, u"exit"))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100501 break;
Simon Glass90975372022-01-23 12:55:12 -0700502 else if (starts_with(pos, u"load"))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100503 do_load();
Simon Glass90975372022-01-23 12:55:12 -0700504 else if (starts_with(pos, u"save "))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100505 do_save(pos + 5);
506 else
507 do_help();
508 }
509
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100510 color(EFI_LIGHTGRAY);
511 cls();
512
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100513 return EFI_SUCCESS;
514}