efi: Create ECPT table
[platform/kernel/u-boot.git] / lib / efi_loader / initrddump.c
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  * Specifying 'nocolor' as load option data suppresses colored output and
9  * clearing of the screen.
10  */
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
21 static struct efi_system_table *systable;
22 static struct efi_boot_services *bs;
23 static struct efi_simple_text_output_protocol *cerr;
24 static struct efi_simple_text_output_protocol *cout;
25 static struct efi_simple_text_input_protocol *cin;
26 static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
27 static const efi_guid_t guid_simple_file_system_protocol =
28                                         EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
29 static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
30 static efi_handle_t handle;
31 static bool nocolor;
32
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  */
37 static 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 /**
54  * color() - set foreground color
55  *
56  * @color:      foreground color
57  */
58 static void color(u8 color)
59 {
60         if (!nocolor)
61                 cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
62 }
63
64 /**
65  * print() - print string
66  *
67  * @string:     text
68  */
69 static void print(u16 *string)
70 {
71         cout->output_string(cout, string);
72 }
73
74 /**
75  * cls() - clear screen
76  */
77 static void cls(void)
78 {
79         if (nocolor)
80                 print(u"\r\n");
81         else
82                 cout->clear_screen(cout);
83 }
84
85 /**
86  * error() - print error string
87  *
88  * @string:     error text
89  */
90 static void error(u16 *string)
91 {
92         color(EFI_LIGHTRED);
93         print(string);
94         color(EFI_LIGHTBLUE);
95 }
96
97 /*
98  * printx() - print hexadecimal number
99  *
100  * @val:        value to print;
101  * @prec:       minimum number of digits to print
102  */
103 static 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 /**
124  * efi_drain_input() - drain console input
125  */
126 static void efi_drain_input(void)
127 {
128         cin->reset(cin, true);
129 }
130
131 /**
132  * 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  */
142 static 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
148         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  */
180 static 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;
185         u16 outbuf[2] = u" ";
186         efi_status_t ret;
187
188         *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 */
198                         print(u"\r\nAborted\r\n");
199                         return EFI_ABORTED;
200                 default:
201                         break;
202                 }
203                 switch (key.unicode_char) {
204                 case 0x08: /* Backspace */
205                         if (pos) {
206                                 buffer[pos--] = 0;
207                                 print(u"\b \b");
208                         }
209                         break;
210                 case 0x0a: /* Linefeed */
211                 case 0x0d: /* Carriage return */
212                         print(u"\r\n");
213                         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  */
236 static 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
248  * Return:      true if @string starts with the keyword
249  */
250 static bool starts_with(u16 *string, u16 *keyword)
251 {
252         if (!string || !keyword)
253                 return false;
254
255         for (; *keyword; ++string, ++keyword) {
256                 if (*string != *keyword)
257                         return false;
258         }
259         return true;
260 }
261
262 /**
263  * do_help() - print help
264  */
265 static void do_help(void)
266 {
267         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");
270 }
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  */
279 static 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) {
291                 error(u"Load File2 protocol not found\r\n");
292                 return ret;
293         }
294         ret = bs->handle_protocol(handle, &load_file2_guid,
295                                  (void **)&load_file2_prot);
296         ret = load_file2_prot->load_file(load_file2_prot, dp, false,
297                                          initrd_size, NULL);
298         if (ret != EFI_BUFFER_TOO_SMALL) {
299                 error(u"Load File2 protocol does not provide file length\r\n");
300                 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) {
305                 error(u"Out of memory\r\n");
306                 return ret;
307         }
308         *initrd = (void *)(uintptr_t)buffer;
309         ret = load_file2_prot->load_file(load_file2_prot, dp, false,
310                                          initrd_size, *initrd);
311         if (ret != EFI_SUCCESS) {
312                 error(u"Load File2 protocol failed to provide file\r\n");
313                 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  */
325 static 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;
335         print(u"length: 0x");
336         printx(initrd_size, 1);
337         print(u"\r\n");
338
339         ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
340         if (ret != EFI_SUCCESS) {
341                 error(u"Calculating CRC32 failed\r\n");
342                 return EFI_LOAD_ERROR;
343         }
344         print(u"crc32: 0x");
345         printx(crc32, 8);
346         print(u"\r\n");
347
348         return EFI_SUCCESS;
349 }
350
351 /**
352  * do_save() - save initial RAM disk
353  *
354  * @filename:   file name
355  * Return:      status code
356  */
357 static 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) {
376                 error(u"Loaded image protocol not found\r\n");
377                 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) {
386                 error(u"Failed to open simple file system protocol\r\n");
387                 goto out;
388         }
389
390         /* Open volume */
391         ret = file_system->open_volume(file_system, &root);
392         if (ret != EFI_SUCCESS) {
393                 error(u"Failed to open volume\r\n");
394                 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);
400                 efi_drain_input();
401                 print(u"Overwrite existing file (y/n)? ");
402                 ret = efi_input_yn();
403                 print(u"\r\n");
404                 if (ret != EFI_SUCCESS) {
405                         root->close(root);
406                         error(u"Aborted by user\r\n");
407                         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) {
419                         error(u"Failed to write file\r\n");
420                 } else {
421                         print(filename);
422                         print(u" written\r\n");
423                 }
424                 file->close(file);
425         } else {
426                 error(u"Failed to open file\r\n");
427         }
428         root->close(root);
429
430 out:
431         if (initrd)
432                 bs->free_pages((uintptr_t)initrd,
433                                efi_size_in_pages(initrd_size));
434         return ret;
435 }
436
437 /**
438  * get_load_options() - get load options
439  *
440  * Return:      load options or NULL
441  */
442 u16 *get_load_options(void)
443 {
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 /**
462  * efi_main() - entry point of the EFI application.
463  *
464  * @handle:     handle of the loaded image
465  * @systab:     system table
466  * Return:      status code
467  */
468 efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
469                              struct efi_system_table *systab)
470 {
471         u16 *load_options;
472
473         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;
479         load_options = get_load_options();
480
481         if (starts_with(load_options, u"nocolor"))
482                 nocolor = true;
483
484         color(EFI_WHITE);
485         cls();
486         print(u"INITRD Dump\r\n===========\r\n\r\n");
487         color(EFI_LIGHTBLUE);
488
489         for (;;) {
490                 u16 command[BUFFER_SIZE];
491                 u16 *pos;
492                 efi_uintn_t ret;
493
494                 efi_drain_input();
495                 print(u"=> ");
496                 ret = efi_input(command, sizeof(command));
497                 if (ret == EFI_ABORTED)
498                         break;
499                 pos = skip_whitespace(command);
500                 if (starts_with(pos, u"exit"))
501                         break;
502                 else if (starts_with(pos, u"load"))
503                         do_load();
504                 else if (starts_with(pos, u"save "))
505                         do_save(pos + 5);
506                 else
507                         do_help();
508         }
509
510         color(EFI_LIGHTGRAY);
511         cls();
512
513         return EFI_SUCCESS;
514 }