blob: 5b470f481946eaa0d6a3775b5bf7df17bb5f950a [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
12#include <common.h>
13#include <efi_api.h>
14#include <efi_load_initrd.h>
15
16#define BUFFER_SIZE 64
17#define ESC 0x17
18
19#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
20
21static struct efi_system_table *systable;
22static struct efi_boot_services *bs;
23static struct efi_simple_text_output_protocol *cerr;
24static struct efi_simple_text_output_protocol *cout;
25static struct efi_simple_text_input_protocol *cin;
26static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
27static const efi_guid_t guid_simple_file_system_protocol =
28 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
29static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
30static efi_handle_t handle;
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010031static bool nocolor;
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010032
33/*
34 * Device path defined by Linux to identify the handle providing the
35 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
36 */
37static const struct efi_initrd_dp initrd_dp = {
38 .vendor = {
39 {
40 DEVICE_PATH_TYPE_MEDIA_DEVICE,
41 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
42 sizeof(initrd_dp.vendor),
43 },
44 EFI_INITRD_MEDIA_GUID,
45 },
46 .end = {
47 DEVICE_PATH_TYPE_END,
48 DEVICE_PATH_SUB_TYPE_END,
49 sizeof(initrd_dp.end),
50 }
51};
52
53/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010054 * color() - set foreground color
55 *
56 * @color: foreground color
57 */
58static void color(u8 color)
59{
60 if (!nocolor)
61 cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
62}
63
64/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010065 * print() - print string
66 *
67 * @string: text
68 */
69static void print(u16 *string)
70{
71 cout->output_string(cout, string);
72}
73
74/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010075 * cls() - clear screen
76 */
77static void cls(void)
78{
79 if (nocolor)
80 print(u"\r\n");
81 else
82 cout->clear_screen(cout);
83}
84
85/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010086 * error() - print error string
87 *
88 * @string: error text
89 */
90static void error(u16 *string)
91{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010092 color(EFI_LIGHTRED);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010093 print(string);
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +010094 color(EFI_LIGHTBLUE);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +010095}
96
97/*
98 * printx() - print hexadecimal number
99 *
100 * @val: value to print;
101 * @prec: minimum number of digits to print
102 */
103static void printx(u64 val, u32 prec)
104{
105 int i;
106 u16 c;
107 u16 buf[16];
108 u16 *pos = buf;
109
110 for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
111 c = (val >> (4 * i)) & 0x0f;
112 if (c || pos != buf || !i || i < prec) {
113 c += '0';
114 if (c > '9')
115 c += 'a' - '9' - 1;
116 *pos++ = c;
117 }
118 }
119 *pos = 0;
120 print(buf);
121}
122
123/**
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100124 * efi_drain_input() - drain console input
125 */
126static void efi_drain_input(void)
127{
128 cin->reset(cin, true);
129}
130
131/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100132 * efi_input_yn() - get answer to yes/no question
133 *
134 * Return:
135 * y or Y
136 * EFI_SUCCESS
137 * n or N
138 * EFI_ACCESS_DENIED
139 * ESC
140 * EFI_ABORTED
141 */
142static efi_status_t efi_input_yn(void)
143{
144 struct efi_input_key key = {0};
145 efi_uintn_t index;
146 efi_status_t ret;
147
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100148 for (;;) {
149 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
150 if (ret != EFI_SUCCESS)
151 continue;
152 ret = cin->read_key_stroke(cin, &key);
153 if (ret != EFI_SUCCESS)
154 continue;
155 switch (key.scan_code) {
156 case 0x17: /* Escape */
157 return EFI_ABORTED;
158 default:
159 break;
160 }
161 /* Convert to lower case */
162 switch (key.unicode_char | 0x20) {
163 case 'y':
164 return EFI_SUCCESS;
165 case 'n':
166 return EFI_ACCESS_DENIED;
167 default:
168 break;
169 }
170 }
171}
172
173/**
174 * efi_input() - read string from console
175 *
176 * @buffer: input buffer
177 * @buffer_size: buffer size
178 * Return: status code
179 */
180static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
181{
182 struct efi_input_key key = {0};
183 efi_uintn_t index;
184 efi_uintn_t pos = 0;
Simon Glass90975372022-01-23 12:55:12 -0700185 u16 outbuf[2] = u" ";
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100186 efi_status_t ret;
187
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100188 *buffer = 0;
189 for (;;) {
190 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
191 if (ret != EFI_SUCCESS)
192 continue;
193 ret = cin->read_key_stroke(cin, &key);
194 if (ret != EFI_SUCCESS)
195 continue;
196 switch (key.scan_code) {
197 case 0x17: /* Escape */
Simon Glass90975372022-01-23 12:55:12 -0700198 print(u"\r\nAborted\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100199 return EFI_ABORTED;
200 default:
201 break;
202 }
203 switch (key.unicode_char) {
204 case 0x08: /* Backspace */
205 if (pos) {
206 buffer[pos--] = 0;
Simon Glass90975372022-01-23 12:55:12 -0700207 print(u"\b \b");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100208 }
209 break;
210 case 0x0a: /* Linefeed */
211 case 0x0d: /* Carriage return */
Simon Glass90975372022-01-23 12:55:12 -0700212 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100213 return EFI_SUCCESS;
214 default:
215 break;
216 }
217 /* Ignore surrogate codes */
218 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
219 continue;
220 if (key.unicode_char >= 0x20 &&
221 pos < buffer_size - 1) {
222 *outbuf = key.unicode_char;
223 buffer[pos++] = key.unicode_char;
224 buffer[pos] = 0;
225 print(outbuf);
226 }
227 }
228}
229
230/**
231 * skip_whitespace() - skip over leading whitespace
232 *
233 * @pos: UTF-16 string
234 * Return: pointer to first non-whitespace
235 */
236static u16 *skip_whitespace(u16 *pos)
237{
238 for (; *pos && *pos <= 0x20; ++pos)
239 ;
240 return pos;
241}
242
243/**
244 * starts_with() - check if @string starts with @keyword
245 *
246 * @string: string to search for keyword
247 * @keyword: keyword to be searched
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100248 * Return: true if @string starts with the keyword
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100249 */
250static bool starts_with(u16 *string, u16 *keyword)
251{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100252 if (!string || !keyword)
253 return false;
254
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100255 for (; *keyword; ++string, ++keyword) {
256 if (*string != *keyword)
257 return false;
258 }
259 return true;
260}
261
262/**
263 * do_help() - print help
264 */
265static void do_help(void)
266{
Simon Glass90975372022-01-23 12:55:12 -0700267 error(u"load - show length and CRC32 of initial RAM disk\r\n");
268 error(u"save <initrd> - save initial RAM disk to file\r\n");
269 error(u"exit - exit the shell\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100270}
271
272/**
273 * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
274 *
275 * @initrd: on return buffer with initial RAM disk
276 * @initrd_size: size of initial RAM disk
277 * Return: status code
278 */
279static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
280{
281 struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
282 struct efi_load_file_protocol *load_file2_prot;
283 u64 buffer;
284 efi_handle_t handle;
285 efi_status_t ret;
286
287 *initrd = NULL;
288 *initrd_size = 0;
289 ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
290 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700291 error(u"Load File2 protocol not found\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100292 return ret;
293 }
Heinrich Schuchardt1a97b512023-04-04 07:23:53 +0200294 ret = bs->open_protocol(handle, &load_file2_guid,
295 (void **)&load_file2_prot, NULL, NULL,
296 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100297 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
298 initrd_size, NULL);
299 if (ret != EFI_BUFFER_TOO_SMALL) {
Simon Glass90975372022-01-23 12:55:12 -0700300 error(u"Load File2 protocol does not provide file length\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100301 return EFI_LOAD_ERROR;
302 }
303 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
304 efi_size_in_pages(*initrd_size), &buffer);
305 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700306 error(u"Out of memory\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100307 return ret;
308 }
Heinrich Schuchardt22f038f2021-03-14 10:12:01 +0100309 *initrd = (void *)(uintptr_t)buffer;
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100310 ret = load_file2_prot->load_file(load_file2_prot, dp, false,
311 initrd_size, *initrd);
312 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700313 error(u"Load File2 protocol failed to provide file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100314 bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
315 return EFI_LOAD_ERROR;
316 }
317 return ret;
318}
319
320/**
321 * do_load() - load initial RAM disk and display CRC32 and length
322 *
323 * @filename: file name
324 * Return: status code
325 */
326static efi_status_t do_load(void)
327{
328 void *initrd;
329 efi_uintn_t initrd_size;
330 u32 crc32;
331 efi_uintn_t ret;
332
333 ret = get_initrd(&initrd, &initrd_size);
334 if (ret != EFI_SUCCESS)
335 return ret;
Simon Glass90975372022-01-23 12:55:12 -0700336 print(u"length: 0x");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100337 printx(initrd_size, 1);
Simon Glass90975372022-01-23 12:55:12 -0700338 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100339
340 ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
341 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700342 error(u"Calculating CRC32 failed\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100343 return EFI_LOAD_ERROR;
344 }
Simon Glass90975372022-01-23 12:55:12 -0700345 print(u"crc32: 0x");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100346 printx(crc32, 8);
Simon Glass90975372022-01-23 12:55:12 -0700347 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100348
349 return EFI_SUCCESS;
350}
351
352/**
353 * do_save() - save initial RAM disk
354 *
355 * @filename: file name
356 * Return: status code
357 */
358static efi_status_t do_save(u16 *filename)
359{
360 struct efi_loaded_image *loaded_image;
361 struct efi_simple_file_system_protocol *file_system;
362 struct efi_file_handle *root, *file;
363 void *initrd;
364 efi_uintn_t initrd_size;
365 efi_uintn_t ret;
366
367 ret = get_initrd(&initrd, &initrd_size);
368 if (ret != EFI_SUCCESS)
369 return ret;
370
371 filename = skip_whitespace(filename);
372
373 ret = bs->open_protocol(handle, &loaded_image_guid,
374 (void **)&loaded_image, NULL, NULL,
375 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
376 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700377 error(u"Loaded image protocol not found\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100378 goto out;
379 }
380
381 /* Open the simple file system protocol */
382 ret = bs->open_protocol(loaded_image->device_handle,
383 &guid_simple_file_system_protocol,
384 (void **)&file_system, NULL, NULL,
385 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
386 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700387 error(u"Failed to open simple file system protocol\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100388 goto out;
389 }
390
391 /* Open volume */
392 ret = file_system->open_volume(file_system, &root);
393 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700394 error(u"Failed to open volume\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100395 goto out;
396 }
397 /* Check if file already exists */
398 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
399 if (ret == EFI_SUCCESS) {
400 file->close(file);
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100401 efi_drain_input();
Simon Glass90975372022-01-23 12:55:12 -0700402 print(u"Overwrite existing file (y/n)? ");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100403 ret = efi_input_yn();
Simon Glass90975372022-01-23 12:55:12 -0700404 print(u"\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100405 if (ret != EFI_SUCCESS) {
406 root->close(root);
Simon Glass90975372022-01-23 12:55:12 -0700407 error(u"Aborted by user\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100408 goto out;
409 }
410 }
411
412 /* Create file */
413 ret = root->open(root, &file, filename,
414 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
415 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
416 if (ret == EFI_SUCCESS) {
417 /* Write file */
418 ret = file->write(file, &initrd_size, initrd);
419 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700420 error(u"Failed to write file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100421 } else {
422 print(filename);
Simon Glass90975372022-01-23 12:55:12 -0700423 print(u" written\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100424 }
425 file->close(file);
426 } else {
Simon Glass90975372022-01-23 12:55:12 -0700427 error(u"Failed to open file\r\n");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100428 }
429 root->close(root);
430
431out:
432 if (initrd)
433 bs->free_pages((uintptr_t)initrd,
434 efi_size_in_pages(initrd_size));
435 return ret;
436}
437
438/**
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100439 * get_load_options() - get load options
440 *
441 * Return: load options or NULL
442 */
Heinrich Schuchardt1e9fa492023-02-10 08:09:40 +0100443static u16 *get_load_options(void)
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100444{
445 efi_status_t ret;
446 struct efi_loaded_image *loaded_image;
447
448 ret = bs->open_protocol(handle, &loaded_image_guid,
449 (void **)&loaded_image, NULL, NULL,
450 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
451 if (ret != EFI_SUCCESS) {
452 error(u"Loaded image protocol not found\r\n");
453 return NULL;
454 }
455
456 if (!loaded_image->load_options_size || !loaded_image->load_options)
457 return NULL;
458
459 return loaded_image->load_options;
460}
461
462/**
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100463 * efi_main() - entry point of the EFI application.
464 *
465 * @handle: handle of the loaded image
466 * @systab: system table
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +0100467 * Return: status code
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100468 */
469efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
470 struct efi_system_table *systab)
471{
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100472 u16 *load_options;
473
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100474 handle = image_handle;
475 systable = systab;
476 cerr = systable->std_err;
477 cout = systable->con_out;
478 cin = systable->con_in;
479 bs = systable->boottime;
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100480 load_options = get_load_options();
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100481
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100482 if (starts_with(load_options, u"nocolor"))
483 nocolor = true;
484
485 color(EFI_WHITE);
486 cls();
Heinrich Schuchardtc348cf22022-03-03 08:13:53 +0100487 print(u"INITRD Dump\r\n===========\r\n\r\n");
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100488 color(EFI_LIGHTBLUE);
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100489
490 for (;;) {
491 u16 command[BUFFER_SIZE];
492 u16 *pos;
493 efi_uintn_t ret;
494
Heinrich Schuchardt7e810a02022-03-22 18:20:07 +0100495 efi_drain_input();
Simon Glass90975372022-01-23 12:55:12 -0700496 print(u"=> ");
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100497 ret = efi_input(command, sizeof(command));
498 if (ret == EFI_ABORTED)
499 break;
500 pos = skip_whitespace(command);
Simon Glass90975372022-01-23 12:55:12 -0700501 if (starts_with(pos, u"exit"))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100502 break;
Simon Glass90975372022-01-23 12:55:12 -0700503 else if (starts_with(pos, u"load"))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100504 do_load();
Simon Glass90975372022-01-23 12:55:12 -0700505 else if (starts_with(pos, u"save "))
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100506 do_save(pos + 5);
507 else
508 do_help();
509 }
510
Heinrich Schuchardtf6d28992022-03-20 09:21:57 +0100511 color(EFI_LIGHTGRAY);
512 cls();
513
Heinrich Schuchardtc4392d92021-01-17 07:30:26 +0100514 return EFI_SUCCESS;
515}