blob: 1e72404ecc17311b168fc44baa95e592888c6357 [file] [log] [blame]
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
4 *
5 * dtbdump.efi saves the device tree provided as a configuration table
6 * to a file.
7 */
8
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +01009#include <efi_api.h>
Heinrich Schuchardt45910792020-12-13 19:13:57 +010010#include <efi_dt_fixup.h>
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +010011#include <part.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060012#include <linux/libfdt.h>
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010013
14#define BUFFER_SIZE 64
15#define ESC 0x17
Heinrich Schuchardt45910792020-12-13 19:13:57 +010016
17#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010018
19static struct efi_simple_text_output_protocol *cerr;
20static struct efi_simple_text_output_protocol *cout;
21static struct efi_simple_text_input_protocol *cin;
22static struct efi_boot_services *bs;
23static const efi_guid_t fdt_guid = EFI_FDT_GUID;
24static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
25static const efi_guid_t guid_simple_file_system_protocol =
26 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
Heinrich Schuchardt45910792020-12-13 19:13:57 +010027static efi_handle_t handle;
28static struct efi_system_table *systable;
29static const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID;
30static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +010031static const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID;
Adriano Cordova1462c872025-05-08 14:30:33 -040032static bool nocolor;
Heinrich Schuchardt45910792020-12-13 19:13:57 +010033
34/**
Adriano Cordova1462c872025-05-08 14:30:33 -040035 * color() - set foreground color
36 *
37 * @color: foreground color
38 */
39static void color(u8 color)
40{
41 if (!nocolor)
42 cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
43}
44
45/**
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +010046 * print() - print string
47 *
48 * @string: text
49 */
50static void print(u16 *string)
51{
52 cout->output_string(cout, string);
53}
54
55/**
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +020056 * print_char() - print character
57 *
58 * 0x00 is replaced by '", "'.
59 *
60 * @c: - character
61 */
62static void print_char(unsigned char c)
63{
64 u16 out[2] = u"?";
65
66 if (!c) {
67 print(u"\", \"");
68 return;
69 }
70
71 if (c > 0x1f && c < 0x80)
72 out[0] = c;
73
74 print(out);
75}
76
77/**
78 * print_hex_digit() - print hexadecimal digit
79 *
80 * @digit: digit to print
81 */
82static void print_hex_digit(unsigned char digit)
83{
84 if (digit < 10)
85 digit += '0';
86 else
87 digit += 'a' - 10;
88 print_char(digit);
89}
90
91/**
92 * printx() - print hexadecimal byte
93 *
94 * @val: value to print
95 */
96static void printx(unsigned char val)
97{
98 print_hex_digit(val >> 4);
99 print_hex_digit(val & 0xf);
100}
101
102/**
Adriano Cordova1462c872025-05-08 14:30:33 -0400103 * cls() - clear screen
104 */
105static void cls(void)
106{
107 if (nocolor)
108 print(u"\r\n");
109 else
110 cout->clear_screen(cout);
111}
112
113/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100114 * error() - print error string
115 *
116 * @string: error text
117 */
118static void error(u16 *string)
119{
Adriano Cordova1462c872025-05-08 14:30:33 -0400120 color(EFI_LIGHTRED);
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +0100121 print(string);
Adriano Cordova1462c872025-05-08 14:30:33 -0400122 color(EFI_LIGHTBLUE);
123}
124
125/**
126 * efi_drain_input() - drain console input
127 */
128static void efi_drain_input(void)
129{
130 cin->reset(cin, true);
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100131}
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100132
133/**
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100134 * efi_input_yn() - get answer to yes/no question
135 *
136 * Return:
137 * y or Y
138 * EFI_SUCCESS
139 * n or N
140 * EFI_ACCESS_DENIED
141 * ESC
142 * EFI_ABORTED
143 */
144static efi_status_t efi_input_yn(void)
145{
146 struct efi_input_key key = {0};
147 efi_uintn_t index;
148 efi_status_t ret;
149
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100150 for (;;) {
151 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
152 if (ret != EFI_SUCCESS)
153 continue;
154 ret = cin->read_key_stroke(cin, &key);
155 if (ret != EFI_SUCCESS)
156 continue;
157 switch (key.scan_code) {
158 case 0x17: /* Escape */
159 return EFI_ABORTED;
160 default:
161 break;
162 }
163 /* Convert to lower case */
164 switch (key.unicode_char | 0x20) {
165 case 'y':
166 return EFI_SUCCESS;
167 case 'n':
168 return EFI_ACCESS_DENIED;
169 default:
170 break;
171 }
172 }
173}
174
175/**
176 * efi_input() - read string from console
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100177 *
178 * @buffer: input buffer
179 * @buffer_size: buffer size
180 * Return: status code
181 */
182static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
183{
184 struct efi_input_key key = {0};
185 efi_uintn_t index;
186 efi_uintn_t pos = 0;
Simon Glass90975372022-01-23 12:55:12 -0700187 u16 outbuf[2] = u" ";
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100188 efi_status_t ret;
189
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100190 *buffer = 0;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100191 for (;;) {
192 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
193 if (ret != EFI_SUCCESS)
194 continue;
195 ret = cin->read_key_stroke(cin, &key);
196 if (ret != EFI_SUCCESS)
197 continue;
198 switch (key.scan_code) {
199 case 0x17: /* Escape */
Simon Glass90975372022-01-23 12:55:12 -0700200 print(u"\r\nAborted\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100201 return EFI_ABORTED;
202 default:
203 break;
204 }
205 switch (key.unicode_char) {
206 case 0x08: /* Backspace */
207 if (pos) {
208 buffer[pos--] = 0;
Simon Glass90975372022-01-23 12:55:12 -0700209 print(u"\b \b");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100210 }
211 break;
212 case 0x0a: /* Linefeed */
213 case 0x0d: /* Carriage return */
Simon Glass90975372022-01-23 12:55:12 -0700214 print(u"\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100215 return EFI_SUCCESS;
216 default:
217 break;
218 }
219 /* Ignore surrogate codes */
220 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
221 continue;
222 if (key.unicode_char >= 0x20 &&
223 pos < buffer_size - 1) {
224 *outbuf = key.unicode_char;
225 buffer[pos++] = key.unicode_char;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100226 buffer[pos] = 0;
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +0100227 print(outbuf);
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100228 }
229 }
230}
231
232/*
233 * Convert FDT value to host endianness.
234 *
235 * @val FDT value
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100236 * Return: converted value
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100237 */
238static u32 f2h(fdt32_t val)
239{
240 char *buf = (char *)&val;
241 char i;
242
243 /* Swap the bytes */
244 i = buf[0]; buf[0] = buf[3]; buf[3] = i;
245 i = buf[1]; buf[1] = buf[2]; buf[2] = i;
246 return *(u32 *)buf;
247}
248
249/**
250 * get_dtb() - get device tree
251 *
252 * @systable: system table
253 * Return: device tree or NULL
254 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300255static void *get_dtb(struct efi_system_table *systable)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100256{
257 void *dtb = NULL;
258 efi_uintn_t i;
259
260 for (i = 0; i < systable->nr_tables; ++i) {
261 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
262 sizeof(efi_guid_t))) {
263 dtb = systable->tables[i].table;
264 break;
265 }
266 }
267 return dtb;
268}
269
270/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100271 * skip_whitespace() - skip over leading whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100272 *
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100273 * @pos: UTF-16 string
274 * Return: pointer to first non-whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100275 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300276static u16 *skip_whitespace(u16 *pos)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100277{
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100278 for (; *pos && *pos <= 0x20; ++pos)
279 ;
280 return pos;
281}
282
283/**
284 * starts_with() - check if @string starts with @keyword
285 *
286 * @string: string to search for keyword
287 * @keyword: keyword to be searched
288 * Return: true fi @string starts with the keyword
289 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300290static bool starts_with(u16 *string, u16 *keyword)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100291{
Adriano Cordova1462c872025-05-08 14:30:33 -0400292 if (!string || !keyword)
293 return false;
294
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100295 for (; *keyword; ++string, ++keyword) {
296 if (*string != *keyword)
297 return false;
298 }
299 return true;
300}
301
302/**
303 * do_help() - print help
304 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300305static void do_help(void)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100306{
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200307 error(u"dump - print device-tree\r\n");
Simon Glass90975372022-01-23 12:55:12 -0700308 error(u"load <dtb> - load device-tree from file\r\n");
309 error(u"save <dtb> - save device-tree to file\r\n");
310 error(u"exit - exit the shell\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100311}
312
313/**
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100314 * open_file_system() - open simple file system protocol
315 *
316 * file_system: interface of the simple file system protocol
317 * Return: status code
318 */
319static efi_status_t
320open_file_system(struct efi_simple_file_system_protocol **file_system)
321{
322 struct efi_loaded_image *loaded_image;
323 efi_status_t ret;
324 efi_handle_t *handle_buffer = NULL;
325 efi_uintn_t count;
326
327 ret = bs->open_protocol(handle, &loaded_image_guid,
328 (void **)&loaded_image, NULL, NULL,
329 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
330 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700331 error(u"Loaded image protocol not found\r\n");
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100332 return ret;
333 }
334
335 /* Open the simple file system protocol on the same partition */
336 ret = bs->open_protocol(loaded_image->device_handle,
337 &guid_simple_file_system_protocol,
338 (void **)file_system, NULL, NULL,
339 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
340 if (ret == EFI_SUCCESS)
341 return ret;
342
343 /* Open the simple file system protocol on the UEFI system partition */
344 ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid,
345 NULL, &count, &handle_buffer);
346 if (ret == EFI_SUCCESS && handle_buffer)
347 ret = bs->open_protocol(handle_buffer[0],
348 &guid_simple_file_system_protocol,
349 (void **)file_system, NULL, NULL,
350 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
351 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700352 error(u"Failed to open simple file system protocol\r\n");
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100353 if (handle)
354 bs->free_pool(handle_buffer);
355
356 return ret;
357}
358
359/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100360 * do_load() - load and install device-tree
361 *
362 * @filename: file name
363 * Return: status code
364 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300365static efi_status_t do_load(u16 *filename)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100366{
367 struct efi_dt_fixup_protocol *dt_fixup_prot;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100368 struct efi_simple_file_system_protocol *file_system;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100369 struct efi_file_handle *root = NULL, *file = NULL;
370 u64 addr = 0;
371 struct efi_file_info *info;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100372 struct fdt_header *dtb;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100373 efi_uintn_t buffer_size;
374 efi_uintn_t pages;
375 efi_status_t ret, ret2;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100376
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100377 ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
378 (void **)&dt_fixup_prot);
379 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700380 error(u"Device-tree fix-up protocol not found\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100381 return ret;
382 }
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100383
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100384 filename = skip_whitespace(filename);
385
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100386 ret = open_file_system(&file_system);
387 if (ret != EFI_SUCCESS)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100388 goto out;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100389
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 Schuchardt45910792020-12-13 19:13:57 +0100394 goto out;
395 }
396
397 /* Open file */
398 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
399 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700400 error(u"File not found\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100401 goto out;
402 }
403 /* Get file size */
404 buffer_size = 0;
405 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
406 if (ret != EFI_BUFFER_TOO_SMALL) {
Simon Glass90975372022-01-23 12:55:12 -0700407 error(u"Can't get file info size\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100408 goto out;
409 }
410 ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
411 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700412 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100413 goto out;
414 }
415 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
416 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700417 error(u"Can't get file info\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100418 goto out;
419 }
420 buffer_size = info->file_size;
421 pages = efi_size_in_pages(buffer_size);
422 ret = bs->free_pool(info);
423 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700424 error(u"Can't free memory pool\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100425 /* Read file */
426 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
427 EFI_ACPI_RECLAIM_MEMORY,
428 pages, &addr);
429 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700430 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100431 goto out;
432 }
433 dtb = (struct fdt_header *)(uintptr_t)addr;
434 ret = file->read(file, &buffer_size, dtb);
435 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700436 error(u"Can't read file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100437 goto out;
438 }
439 /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
440 ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
441 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
442 EFI_DT_INSTALL_TABLE);
443 if (ret == EFI_BUFFER_TOO_SMALL) {
444 /* Read file into larger buffer */
445 ret = bs->free_pages(addr, pages);
446 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700447 error(u"Can't free memory pages\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100448 pages = efi_size_in_pages(buffer_size);
449 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
450 EFI_ACPI_RECLAIM_MEMORY,
451 pages, &addr);
452 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700453 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100454 goto out;
455 }
456 dtb = (struct fdt_header *)(uintptr_t)addr;
457 ret = file->setpos(file, 0);
458 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700459 error(u"Can't position file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100460 goto out;
461 }
462 ret = file->read(file, &buffer_size, dtb);
463 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700464 error(u"Can't read file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100465 goto out;
466 }
467 buffer_size = pages << EFI_PAGE_SHIFT;
468 ret = dt_fixup_prot->fixup(
469 dt_fixup_prot, dtb, &buffer_size,
470 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
471 EFI_DT_INSTALL_TABLE);
472 }
473 if (ret == EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700474 print(u"device-tree installed\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100475 else
Simon Glass90975372022-01-23 12:55:12 -0700476 error(u"Device-tree fix-up failed\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100477out:
478 if (addr) {
479 ret2 = bs->free_pages(addr, pages);
480 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700481 error(u"Can't free memory pages\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100482 }
483 if (file) {
484 ret2 = file->close(file);
485 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700486 error(u"Can't close file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100487 }
488 if (root) {
489 ret2 = root->close(root);
490 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700491 error(u"Can't close volume\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100492 }
493 return ret;
494}
495
496/**
497 * do_save() - save current device-tree
498 *
499 * @filename: file name
500 * Return: status code
501 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300502static efi_status_t do_save(u16 *filename)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100503{
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100504 struct efi_simple_file_system_protocol *file_system;
505 efi_uintn_t dtb_size;
506 struct efi_file_handle *root, *file;
507 struct fdt_header *dtb;
508 efi_uintn_t ret;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100509
510 dtb = get_dtb(systable);
511 if (!dtb) {
Simon Glass90975372022-01-23 12:55:12 -0700512 error(u"DTB not found\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100513 return EFI_NOT_FOUND;
514 }
515 if (f2h(dtb->magic) != FDT_MAGIC) {
Simon Glass90975372022-01-23 12:55:12 -0700516 error(u"Wrong device tree magic\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100517 return EFI_NOT_FOUND;
518 }
519 dtb_size = f2h(dtb->totalsize);
520
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100521 filename = skip_whitespace(filename);
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100522
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100523 ret = open_file_system(&file_system);
524 if (ret != EFI_SUCCESS)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100525 return ret;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100526
527 /* Open volume */
528 ret = file_system->open_volume(file_system, &root);
529 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700530 error(u"Failed to open volume\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100531 return ret;
532 }
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100533 /* Check if file already exists */
534 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
535 if (ret == EFI_SUCCESS) {
536 file->close(file);
Simon Glass90975372022-01-23 12:55:12 -0700537 print(u"Overwrite existing file (y/n)? ");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100538 ret = efi_input_yn();
Simon Glass90975372022-01-23 12:55:12 -0700539 print(u"\r\n");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100540 if (ret != EFI_SUCCESS) {
541 root->close(root);
Simon Glass90975372022-01-23 12:55:12 -0700542 error(u"Aborted by user\r\n");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100543 return ret;
544 }
545 }
546
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100547 /* Create file */
548 ret = root->open(root, &file, filename,
549 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
550 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
551 if (ret == EFI_SUCCESS) {
552 /* Write file */
553 ret = file->write(file, &dtb_size, dtb);
554 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700555 error(u"Failed to write file\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100556 file->close(file);
557 } else {
Simon Glass90975372022-01-23 12:55:12 -0700558 error(u"Failed to open file\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100559 }
560 root->close(root);
561
562 if (ret == EFI_SUCCESS) {
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +0100563 print(filename);
Simon Glass90975372022-01-23 12:55:12 -0700564 print(u" written\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100565 }
566
567 return ret;
568}
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100569
570/**
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200571 * indent() - print a number of tabstops
572 *
573 * @level: indentation level
574 */
575static void indent(u32 level)
576{
577 for (; level; --level)
578 print(u"\t");
579}
580
581/**
582 * is_string_value() - determine if property is a string
583 *
584 * If a property is a string, an x-string, or a u32 cannot be deducted
585 * from the device-tree. Therefore a heuristic is used.
586 *
587 * @str: pointer to device-tree property
588 * @len: length of the device-tree property
589 * Return: 1 for string, 0 otherwise
590 */
591static int is_string_value(const unsigned char *str, u32 len)
592{
593 int nonzero_flag = 0;
594
595 /* Zero length or not ending with 0x00 */
596 if (!len || str[len - 1])
597 return 0;
598
599 for (u32 i = 0; i < len; ++i) {
600 if (!str[i]) {
601 /* Zero length string or two consecutive 0x00 */
602 if (!nonzero_flag)
603 return 0;
604
605 nonzero_flag = 0;
606
607 continue;
608 }
609 /* Non-printable */
610 if (str[i] < 0x20 || str[i] >= 0x80)
611 return 0;
612
613 nonzero_flag = 1;
614 }
615
616 return 1;
617}
618
619/**
620 * print_property() - print device-tree property
621 *
622 * If a property is a string, an x-string, or a u32 cannot be deducted
623 * from the device-tree. Therefore a heuristic is used.
624 *
625 * @str: property value
626 * @len: length of property value
627 */
628static void print_property(const unsigned char *val, u32 len)
629{
630 if (is_string_value(val, len)) {
631 /* string */
632 print(u"\"");
633 for (int i = 0; i < len - 1; ++i)
634 print_char(val[i]);
635 print(u"\"");
636 } else if (len & 0x3) {
637 /* byte string */
638 print(u"[");
639 for (int i = 0; i < len; ++i) {
640 if (i)
641 print(u" ");
642 printx(val[i]);
643 }
644 print(u"]\"");
645 } else {
646 /* cell list */
647 print(u"<");
648 for (u32 i = 0; i < len; ++i) {
649 if ((i & 0x3) == 0) {
650 if (i > 0)
651 print(u" ");
652 print(u"0x");
653 }
654 printx(val[i]);
655 }
656 print(u">");
657 }
658}
659
660/**
661 * print_mem_res_block() - print memory reservation block
662 *
663 * @rsvblk: memory reservation block
664 */
665static void print_mem_res_block(const struct fdt_reserve_entry *rsvblk)
666{
667 for (; rsvblk->address || rsvblk->size; ++rsvblk) {
668 const unsigned char *val;
669
670 print(u"/memreserve/ 0x");
671 val = (const unsigned char *)&rsvblk->address;
672 for (u32 i = 0; i < sizeof(u64); ++i)
673 printx(val[i]);
674 print(u" 0x");
675 val = (const unsigned char *)&rsvblk->size;
676 for (u32 i = 0; i < sizeof(u64); ++i)
677 printx(val[i]);
678 print(u";\r\n");
679 }
680}
681
682/**
683 * do_dump() - print device-tree
684 */
685static efi_status_t do_dump(void)
686{
687 const unsigned char *fdt;
688 struct fdt_header *header;
689 const u32 *end;
690 const u32 *pos;
691 const char *strings;
692 u32 level = 0;
693
694 fdt = get_dtb(systable);
695 if (!fdt) {
696 error(u"DTB not found\r\n");
697 return EFI_NOT_FOUND;
698 }
699
700 header = (struct fdt_header *)fdt;
701 if (f2h(header->magic) != FDT_MAGIC) {
702 error(u"Wrong device tree magic\r\n");
703 error(u"Not a device-tree\r\n");
704 return EFI_LOAD_ERROR;
705 }
706
707 pos = (u32 *)(fdt + f2h(header->off_dt_struct));
708 end = &pos[f2h(header->totalsize) >> 2];
709 strings = fdt + f2h(header->off_dt_strings);
710
711 print(u"/dts-v1/;\r\n");
712
713 print_mem_res_block((const struct fdt_reserve_entry *)
714 (fdt + f2h(header->off_mem_rsvmap)));
715
716 print(u"/");
717 for (; pos < end;) {
718 switch (f2h(pos[0])) {
719 case FDT_BEGIN_NODE: {
720 const char *c = (char *)&pos[1];
721 size_t i;
722
723 indent(level);
724 for (i = 0; c[i]; ++i)
725 print_char(c[i]);
726 print(u" {\n\r");
727
728 ++level;
729 pos = &pos[2 + (i >> 2)];
730 break;
731 }
732 case FDT_PROP: {
733 struct fdt_property *prop = (struct fdt_property *)pos;
734 const unsigned char *label = &strings[f2h(prop->nameoff)];
735 u32 len = f2h(prop->len);
736 const unsigned char *str = (unsigned char *)&pos[3];
737
738 indent(level);
739 for (int i = 0; label[i]; ++i)
740 print_char(label[i]);
741
742 if (len) {
743 print(u" = ");
744 print_property(str, len);
745 }
746 print(u";\r\n");
747
748 pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
749 break;
750 }
751 case FDT_NOP:
752 ++pos;
753 break;
754 case FDT_END_NODE:
755 if (!level) {
756 error(u"Extraneous end node\r\n");
757 return EFI_LOAD_ERROR;
758 }
759
760 --level;
761 indent(level);
762 print(u"};\n\r");
763 ++pos;
764 break;
765 case FDT_END:
766 if (level) {
767 error(u"Missing end node\r\n");
768 return EFI_LOAD_ERROR;
769 }
Adriano Cordova1462c872025-05-08 14:30:33 -0400770 print(u"\r\n");
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200771 return EFI_SUCCESS;
772 default:
773 error(u"Invalid device tree token\r\n");
774 return EFI_LOAD_ERROR;
775 }
776 }
777 error(u"Overrun\r\n");
778
779 return EFI_LOAD_ERROR;
780}
781
782/**
Adriano Cordova1462c872025-05-08 14:30:33 -0400783 * get_load_options() - get load options
784 *
785 * Return: load options or NULL
786 */
787static u16 *get_load_options(void)
788{
789 efi_status_t ret;
790 struct efi_loaded_image *loaded_image;
791
792 ret = bs->open_protocol(handle, &loaded_image_guid,
793 (void **)&loaded_image, NULL, NULL,
794 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
795 if (ret != EFI_SUCCESS) {
796 error(u"Loaded image protocol not found\r\n");
797 return NULL;
798 }
799
800 if (!loaded_image->load_options_size || !loaded_image->load_options)
801 return NULL;
802
803 return loaded_image->load_options;
804}
805
806/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100807 * efi_main() - entry point of the EFI application.
808 *
809 * @handle: handle of the loaded image
810 * @systab: system table
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +0100811 * Return: status code
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100812 */
813efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
814 struct efi_system_table *systab)
815{
Adriano Cordova1462c872025-05-08 14:30:33 -0400816 u16 *load_options;
817
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100818 handle = image_handle;
819 systable = systab;
820 cerr = systable->std_err;
821 cout = systable->con_out;
822 cin = systable->con_in;
823 bs = systable->boottime;
Adriano Cordova1462c872025-05-08 14:30:33 -0400824 load_options = get_load_options();
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100825
Adriano Cordova1462c872025-05-08 14:30:33 -0400826 if (starts_with(load_options, u"nocolor"))
827 nocolor = true;
828
829 color(EFI_LIGHTBLUE);
830 cls();
831 color(EFI_WHITE);
Simon Glass90975372022-01-23 12:55:12 -0700832 print(u"DTB Dump\r\n========\r\n\r\n");
Adriano Cordova1462c872025-05-08 14:30:33 -0400833 color(EFI_LIGHTBLUE);
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100834
835 for (;;) {
836 u16 command[BUFFER_SIZE];
837 u16 *pos;
838 efi_uintn_t ret;
839
Adriano Cordova1462c872025-05-08 14:30:33 -0400840 efi_drain_input();
Simon Glass90975372022-01-23 12:55:12 -0700841 print(u"=> ");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100842 ret = efi_input(command, sizeof(command));
843 if (ret == EFI_ABORTED)
844 break;
845 pos = skip_whitespace(command);
Simon Glass90975372022-01-23 12:55:12 -0700846 if (starts_with(pos, u"exit"))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100847 break;
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200848 else if (starts_with(pos, u"dump"))
849 do_dump();
Simon Glass90975372022-01-23 12:55:12 -0700850 else if (starts_with(pos, u"load "))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100851 do_load(pos + 5);
Simon Glass90975372022-01-23 12:55:12 -0700852 else if (starts_with(pos, u"save "))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100853 do_save(pos + 5);
854 else
855 do_help();
856 }
857
Adriano Cordova1462c872025-05-08 14:30:33 -0400858 color(EFI_LIGHTGRAY);
859 cls();
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100860 return EFI_SUCCESS;
861}