]> Git Repo - u-boot.git/blob - lib/efi_selftest/dtbdump.c
efi_loader: correct function comment style
[u-boot.git] / lib / efi_selftest / dtbdump.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2020, Heinrich Schuchardt <[email protected]>
4  *
5  * dtbdump.efi saves the device tree provided as a configuration table
6  * to a file.
7  */
8
9 #include <common.h>
10 #include <efi_api.h>
11 #include <efi_dt_fixup.h>
12 #include <part.h>
13 #include <linux/libfdt.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
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 struct efi_boot_services *bs;
24 static const efi_guid_t fdt_guid = EFI_FDT_GUID;
25 static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
26 static const efi_guid_t guid_simple_file_system_protocol =
27                                         EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
28 static efi_handle_t handle;
29 static struct efi_system_table *systable;
30 static const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID;
31 static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
32 static const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID;
33
34 /**
35  * print() - print string
36  *
37  * @string:     text
38  */
39 static void print(u16 *string)
40 {
41         cout->output_string(cout, string);
42 }
43
44 /**
45  * error() - print error string
46  *
47  * @string:     error text
48  */
49 static void error(u16 *string)
50 {
51         cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
52         print(string);
53         cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
54 }
55
56 /**
57  * efi_input_yn() - get answer to yes/no question
58  *
59  * Return:
60  * y or Y
61  *     EFI_SUCCESS
62  * n or N
63  *     EFI_ACCESS_DENIED
64  * ESC
65  *     EFI_ABORTED
66  */
67 static efi_status_t efi_input_yn(void)
68 {
69         struct efi_input_key key = {0};
70         efi_uintn_t index;
71         efi_status_t ret;
72
73         /* Drain the console input */
74         ret = cin->reset(cin, true);
75         for (;;) {
76                 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
77                 if (ret != EFI_SUCCESS)
78                         continue;
79                 ret = cin->read_key_stroke(cin, &key);
80                 if (ret != EFI_SUCCESS)
81                         continue;
82                 switch (key.scan_code) {
83                 case 0x17: /* Escape */
84                         return EFI_ABORTED;
85                 default:
86                         break;
87                 }
88                 /* Convert to lower case */
89                 switch (key.unicode_char | 0x20) {
90                 case 'y':
91                         return EFI_SUCCESS;
92                 case 'n':
93                         return EFI_ACCESS_DENIED;
94                 default:
95                         break;
96                 }
97         }
98 }
99
100 /**
101  * efi_input() - read string from console
102  *
103  * @buffer:             input buffer
104  * @buffer_size:        buffer size
105  * Return:              status code
106  */
107 static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
108 {
109         struct efi_input_key key = {0};
110         efi_uintn_t index;
111         efi_uintn_t pos = 0;
112         u16 outbuf[2] = L" ";
113         efi_status_t ret;
114
115         /* Drain the console input */
116         ret = cin->reset(cin, true);
117         *buffer = 0;
118         for (;;) {
119                 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
120                 if (ret != EFI_SUCCESS)
121                         continue;
122                 ret = cin->read_key_stroke(cin, &key);
123                 if (ret != EFI_SUCCESS)
124                         continue;
125                 switch (key.scan_code) {
126                 case 0x17: /* Escape */
127                         print(L"\r\nAborted\r\n");
128                         return EFI_ABORTED;
129                 default:
130                         break;
131                 }
132                 switch (key.unicode_char) {
133                 case 0x08: /* Backspace */
134                         if (pos) {
135                                 buffer[pos--] = 0;
136                                 print(L"\b \b");
137                         }
138                         break;
139                 case 0x0a: /* Linefeed */
140                 case 0x0d: /* Carriage return */
141                         print(L"\r\n");
142                         return EFI_SUCCESS;
143                 default:
144                         break;
145                 }
146                 /* Ignore surrogate codes */
147                 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
148                         continue;
149                 if (key.unicode_char >= 0x20 &&
150                     pos < buffer_size - 1) {
151                         *outbuf = key.unicode_char;
152                         buffer[pos++] = key.unicode_char;
153                         buffer[pos] = 0;
154                         print(outbuf);
155                 }
156         }
157 }
158
159 /*
160  * Convert FDT value to host endianness.
161  *
162  * @val         FDT value
163  * Return:      converted value
164  */
165 static u32 f2h(fdt32_t val)
166 {
167         char *buf = (char *)&val;
168         char i;
169
170         /* Swap the bytes */
171         i = buf[0]; buf[0] = buf[3]; buf[3] = i;
172         i = buf[1]; buf[1] = buf[2]; buf[2] = i;
173         return *(u32 *)buf;
174 }
175
176 /**
177  * get_dtb() - get device tree
178  *
179  * @systable:   system table
180  * Return:      device tree or NULL
181  */
182 void *get_dtb(struct efi_system_table *systable)
183 {
184         void *dtb = NULL;
185         efi_uintn_t i;
186
187         for (i = 0; i < systable->nr_tables; ++i) {
188                 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
189                             sizeof(efi_guid_t))) {
190                         dtb = systable->tables[i].table;
191                         break;
192                 }
193         }
194         return dtb;
195 }
196
197 /**
198  * skip_whitespace() - skip over leading whitespace
199  *
200  * @pos:        UTF-16 string
201  * Return:      pointer to first non-whitespace
202  */
203 u16 *skip_whitespace(u16 *pos)
204 {
205         for (; *pos && *pos <= 0x20; ++pos)
206                 ;
207         return pos;
208 }
209
210 /**
211  * starts_with() - check if @string starts with @keyword
212  *
213  * @string:     string to search for keyword
214  * @keyword:    keyword to be searched
215  * Return:      true fi @string starts with the keyword
216  */
217 bool starts_with(u16 *string, u16 *keyword)
218 {
219         for (; *keyword; ++string, ++keyword) {
220                 if (*string != *keyword)
221                         return false;
222         }
223         return true;
224 }
225
226 /**
227  * do_help() - print help
228  */
229 void do_help(void)
230 {
231         error(L"load <dtb> - load device-tree from file\r\n");
232         error(L"save <dtb> - save device-tree to file\r\n");
233         error(L"exit       - exit the shell\r\n");
234 }
235
236 /**
237  * open_file_system() - open simple file system protocol
238  *
239  * file_system: interface of the simple file system protocol
240  * Return:      status code
241  */
242 static efi_status_t
243 open_file_system(struct efi_simple_file_system_protocol **file_system)
244 {
245         struct efi_loaded_image *loaded_image;
246         efi_status_t ret;
247         efi_handle_t *handle_buffer = NULL;
248         efi_uintn_t count;
249
250         ret = bs->open_protocol(handle, &loaded_image_guid,
251                                 (void **)&loaded_image, NULL, NULL,
252                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
253         if (ret != EFI_SUCCESS) {
254                 error(L"Loaded image protocol not found\r\n");
255                 return ret;
256         }
257
258         /* Open the simple file system protocol on the same partition */
259         ret = bs->open_protocol(loaded_image->device_handle,
260                                 &guid_simple_file_system_protocol,
261                                 (void **)file_system, NULL, NULL,
262                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
263         if (ret == EFI_SUCCESS)
264                 return ret;
265
266         /* Open the simple file system protocol on the UEFI system partition */
267         ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid,
268                                        NULL, &count, &handle_buffer);
269         if (ret == EFI_SUCCESS && handle_buffer)
270                 ret = bs->open_protocol(handle_buffer[0],
271                                         &guid_simple_file_system_protocol,
272                                         (void **)file_system, NULL, NULL,
273                                         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
274         if (ret != EFI_SUCCESS)
275                 error(L"Failed to open simple file system protocol\r\n");
276         if (handle)
277                 bs->free_pool(handle_buffer);
278
279         return ret;
280 }
281
282 /**
283  * do_load() - load and install device-tree
284  *
285  * @filename:   file name
286  * Return:      status code
287  */
288 efi_status_t do_load(u16 *filename)
289 {
290         struct efi_dt_fixup_protocol *dt_fixup_prot;
291         struct efi_simple_file_system_protocol *file_system;
292         struct efi_file_handle *root = NULL, *file = NULL;
293         u64 addr = 0;
294         struct efi_file_info *info;
295         struct fdt_header *dtb;
296         efi_uintn_t buffer_size;
297         efi_uintn_t pages;
298         efi_status_t ret, ret2;
299
300         ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
301                                   (void **)&dt_fixup_prot);
302         if (ret != EFI_SUCCESS) {
303                 error(L"Device-tree fix-up protocol not found\r\n");
304                 return ret;
305         }
306
307         filename = skip_whitespace(filename);
308
309         ret = open_file_system(&file_system);
310         if (ret != EFI_SUCCESS)
311                 goto out;
312
313         /* Open volume */
314         ret = file_system->open_volume(file_system, &root);
315         if (ret != EFI_SUCCESS) {
316                 error(L"Failed to open volume\r\n");
317                 goto out;
318         }
319
320         /* Open file */
321         ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
322         if (ret != EFI_SUCCESS) {
323                 error(L"File not found\r\n");
324                 goto out;
325         }
326         /* Get file size */
327         buffer_size = 0;
328         ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
329         if (ret != EFI_BUFFER_TOO_SMALL) {
330                 error(L"Can't get file info size\r\n");
331                 goto out;
332         }
333         ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
334         if (ret != EFI_SUCCESS) {
335                 error(L"Out of memory\r\n");
336                 goto out;
337         }
338         ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
339         if (ret != EFI_SUCCESS) {
340                 error(L"Can't get file info\r\n");
341                 goto out;
342         }
343         buffer_size = info->file_size;
344         pages = efi_size_in_pages(buffer_size);
345         ret = bs->free_pool(info);
346         if (ret != EFI_SUCCESS)
347                 error(L"Can't free memory pool\r\n");
348         /* Read file */
349         ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
350                                  EFI_ACPI_RECLAIM_MEMORY,
351                                  pages, &addr);
352         if (ret != EFI_SUCCESS) {
353                 error(L"Out of memory\r\n");
354                 goto out;
355         }
356         dtb = (struct fdt_header *)(uintptr_t)addr;
357         ret = file->read(file, &buffer_size, dtb);
358         if (ret != EFI_SUCCESS) {
359                 error(L"Can't read file\r\n");
360                 goto out;
361         }
362         /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
363         ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
364                                    EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
365                                    EFI_DT_INSTALL_TABLE);
366         if (ret == EFI_BUFFER_TOO_SMALL) {
367                 /* Read file into larger buffer */
368                 ret = bs->free_pages(addr, pages);
369                 if (ret != EFI_SUCCESS)
370                         error(L"Can't free memory pages\r\n");
371                 pages = efi_size_in_pages(buffer_size);
372                 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
373                                          EFI_ACPI_RECLAIM_MEMORY,
374                                          pages, &addr);
375                 if (ret != EFI_SUCCESS) {
376                         error(L"Out of memory\r\n");
377                         goto out;
378                 }
379                 dtb = (struct fdt_header *)(uintptr_t)addr;
380                 ret = file->setpos(file, 0);
381                 if (ret != EFI_SUCCESS) {
382                         error(L"Can't position file\r\n");
383                         goto out;
384                 }
385                 ret = file->read(file, &buffer_size, dtb);
386                 if (ret != EFI_SUCCESS) {
387                         error(L"Can't read file\r\n");
388                         goto out;
389                 }
390                 buffer_size = pages << EFI_PAGE_SHIFT;
391                 ret = dt_fixup_prot->fixup(
392                                 dt_fixup_prot, dtb, &buffer_size,
393                                 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
394                                 EFI_DT_INSTALL_TABLE);
395         }
396         if (ret == EFI_SUCCESS)
397                 print(L"device-tree installed\r\n");
398         else
399                 error(L"Device-tree fix-up failed\r\n");
400 out:
401         if (addr) {
402                 ret2 = bs->free_pages(addr, pages);
403                 if (ret2 != EFI_SUCCESS)
404                         error(L"Can't free memory pages\r\n");
405         }
406         if (file) {
407                 ret2 = file->close(file);
408                 if (ret2 != EFI_SUCCESS)
409                         error(L"Can't close file\r\n");
410         }
411         if (root) {
412                 ret2 = root->close(root);
413                 if (ret2 != EFI_SUCCESS)
414                         error(L"Can't close volume\r\n");
415         }
416         return ret;
417 }
418
419 /**
420  * do_save() - save current device-tree
421  *
422  * @filename:   file name
423  * Return:      status code
424  */
425 efi_status_t do_save(u16 *filename)
426 {
427         struct efi_simple_file_system_protocol *file_system;
428         efi_uintn_t dtb_size;
429         struct efi_file_handle *root, *file;
430         struct fdt_header *dtb;
431         efi_uintn_t ret;
432
433         dtb = get_dtb(systable);
434         if (!dtb) {
435                 error(L"DTB not found\r\n");
436                 return EFI_NOT_FOUND;
437         }
438         if (f2h(dtb->magic) != FDT_MAGIC) {
439                 error(L"Wrong device tree magic\r\n");
440                 return EFI_NOT_FOUND;
441         }
442         dtb_size = f2h(dtb->totalsize);
443
444         filename = skip_whitespace(filename);
445
446         ret = open_file_system(&file_system);
447         if (ret != EFI_SUCCESS)
448                 return ret;
449
450         /* Open volume */
451         ret = file_system->open_volume(file_system, &root);
452         if (ret != EFI_SUCCESS) {
453                 error(L"Failed to open volume\r\n");
454                 return ret;
455         }
456         /* Check if file already exists */
457         ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
458         if (ret == EFI_SUCCESS) {
459                 file->close(file);
460                 print(L"Overwrite existing file (y/n)? ");
461                 ret = efi_input_yn();
462                 print(L"\r\n");
463                 if (ret != EFI_SUCCESS) {
464                         root->close(root);
465                         error(L"Aborted by user\r\n");
466                         return ret;
467                 }
468         }
469
470         /* Create file */
471         ret = root->open(root, &file, filename,
472                          EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
473                          EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
474         if (ret == EFI_SUCCESS) {
475                 /* Write file */
476                 ret = file->write(file, &dtb_size, dtb);
477                 if (ret != EFI_SUCCESS)
478                         error(L"Failed to write file\r\n");
479                 file->close(file);
480         } else {
481                 error(L"Failed to open file\r\n");
482         }
483         root->close(root);
484
485         if (ret == EFI_SUCCESS) {
486                 print(filename);
487                 print(L" written\r\n");
488         }
489
490         return ret;
491 }
492
493 /**
494  * efi_main() - entry point of the EFI application.
495  *
496  * @handle:     handle of the loaded image
497  * @systab:     system table
498  * Return:      status code
499  */
500 efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
501                              struct efi_system_table *systab)
502 {
503         handle = image_handle;
504         systable = systab;
505         cerr = systable->std_err;
506         cout = systable->con_out;
507         cin = systable->con_in;
508         bs = systable->boottime;
509
510         cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
511         cout->clear_screen(cout);
512         cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
513         print(L"DTB Dump\r\n========\r\n\r\n");
514         cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
515
516         for (;;) {
517                 u16 command[BUFFER_SIZE];
518                 u16 *pos;
519                 efi_uintn_t ret;
520
521                 print(L"=> ");
522                 ret = efi_input(command, sizeof(command));
523                 if (ret == EFI_ABORTED)
524                         break;
525                 pos = skip_whitespace(command);
526                 if (starts_with(pos, L"exit"))
527                         break;
528                 else if (starts_with(pos, L"load "))
529                         do_load(pos + 5);
530                 else if (starts_with(pos, L"save "))
531                         do_save(pos + 5);
532                 else
533                         do_help();
534         }
535
536         cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
537         cout->clear_screen(cout);
538         return EFI_SUCCESS;
539 }
This page took 0.060948 seconds and 4 git commands to generate.