blob: a3d59a2fd3bead87a10e514d214ff31691deb175 [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;
Heinrich Schuchardt45910792020-12-13 19:13:57 +010032
33/**
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +010034 * print() - print string
35 *
36 * @string: text
37 */
38static void print(u16 *string)
39{
40 cout->output_string(cout, string);
41}
42
43/**
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +020044 * print_char() - print character
45 *
46 * 0x00 is replaced by '", "'.
47 *
48 * @c: - character
49 */
50static void print_char(unsigned char c)
51{
52 u16 out[2] = u"?";
53
54 if (!c) {
55 print(u"\", \"");
56 return;
57 }
58
59 if (c > 0x1f && c < 0x80)
60 out[0] = c;
61
62 print(out);
63}
64
65/**
66 * print_hex_digit() - print hexadecimal digit
67 *
68 * @digit: digit to print
69 */
70static void print_hex_digit(unsigned char digit)
71{
72 if (digit < 10)
73 digit += '0';
74 else
75 digit += 'a' - 10;
76 print_char(digit);
77}
78
79/**
80 * printx() - print hexadecimal byte
81 *
82 * @val: value to print
83 */
84static void printx(unsigned char val)
85{
86 print_hex_digit(val >> 4);
87 print_hex_digit(val & 0xf);
88}
89
90/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +010091 * error() - print error string
92 *
93 * @string: error text
94 */
95static void error(u16 *string)
96{
97 cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +010098 print(string);
Heinrich Schuchardt45910792020-12-13 19:13:57 +010099 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
100}
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100101
102/**
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100103 * efi_input_yn() - get answer to yes/no question
104 *
105 * Return:
106 * y or Y
107 * EFI_SUCCESS
108 * n or N
109 * EFI_ACCESS_DENIED
110 * ESC
111 * EFI_ABORTED
112 */
113static efi_status_t efi_input_yn(void)
114{
115 struct efi_input_key key = {0};
116 efi_uintn_t index;
117 efi_status_t ret;
118
119 /* Drain the console input */
120 ret = cin->reset(cin, true);
121 for (;;) {
122 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
123 if (ret != EFI_SUCCESS)
124 continue;
125 ret = cin->read_key_stroke(cin, &key);
126 if (ret != EFI_SUCCESS)
127 continue;
128 switch (key.scan_code) {
129 case 0x17: /* Escape */
130 return EFI_ABORTED;
131 default:
132 break;
133 }
134 /* Convert to lower case */
135 switch (key.unicode_char | 0x20) {
136 case 'y':
137 return EFI_SUCCESS;
138 case 'n':
139 return EFI_ACCESS_DENIED;
140 default:
141 break;
142 }
143 }
144}
145
146/**
147 * efi_input() - read string from console
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100148 *
149 * @buffer: input buffer
150 * @buffer_size: buffer size
151 * Return: status code
152 */
153static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
154{
155 struct efi_input_key key = {0};
156 efi_uintn_t index;
157 efi_uintn_t pos = 0;
Simon Glass90975372022-01-23 12:55:12 -0700158 u16 outbuf[2] = u" ";
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100159 efi_status_t ret;
160
161 /* Drain the console input */
162 ret = cin->reset(cin, true);
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100163 *buffer = 0;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100164 for (;;) {
165 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
166 if (ret != EFI_SUCCESS)
167 continue;
168 ret = cin->read_key_stroke(cin, &key);
169 if (ret != EFI_SUCCESS)
170 continue;
171 switch (key.scan_code) {
172 case 0x17: /* Escape */
Simon Glass90975372022-01-23 12:55:12 -0700173 print(u"\r\nAborted\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100174 return EFI_ABORTED;
175 default:
176 break;
177 }
178 switch (key.unicode_char) {
179 case 0x08: /* Backspace */
180 if (pos) {
181 buffer[pos--] = 0;
Simon Glass90975372022-01-23 12:55:12 -0700182 print(u"\b \b");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100183 }
184 break;
185 case 0x0a: /* Linefeed */
186 case 0x0d: /* Carriage return */
Simon Glass90975372022-01-23 12:55:12 -0700187 print(u"\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100188 return EFI_SUCCESS;
189 default:
190 break;
191 }
192 /* Ignore surrogate codes */
193 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
194 continue;
195 if (key.unicode_char >= 0x20 &&
196 pos < buffer_size - 1) {
197 *outbuf = key.unicode_char;
198 buffer[pos++] = key.unicode_char;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100199 buffer[pos] = 0;
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +0100200 print(outbuf);
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100201 }
202 }
203}
204
205/*
206 * Convert FDT value to host endianness.
207 *
208 * @val FDT value
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100209 * Return: converted value
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100210 */
211static u32 f2h(fdt32_t val)
212{
213 char *buf = (char *)&val;
214 char i;
215
216 /* Swap the bytes */
217 i = buf[0]; buf[0] = buf[3]; buf[3] = i;
218 i = buf[1]; buf[1] = buf[2]; buf[2] = i;
219 return *(u32 *)buf;
220}
221
222/**
223 * get_dtb() - get device tree
224 *
225 * @systable: system table
226 * Return: device tree or NULL
227 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300228static void *get_dtb(struct efi_system_table *systable)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100229{
230 void *dtb = NULL;
231 efi_uintn_t i;
232
233 for (i = 0; i < systable->nr_tables; ++i) {
234 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
235 sizeof(efi_guid_t))) {
236 dtb = systable->tables[i].table;
237 break;
238 }
239 }
240 return dtb;
241}
242
243/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100244 * skip_whitespace() - skip over leading whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100245 *
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100246 * @pos: UTF-16 string
247 * Return: pointer to first non-whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100248 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300249static u16 *skip_whitespace(u16 *pos)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100250{
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100251 for (; *pos && *pos <= 0x20; ++pos)
252 ;
253 return pos;
254}
255
256/**
257 * starts_with() - check if @string starts with @keyword
258 *
259 * @string: string to search for keyword
260 * @keyword: keyword to be searched
261 * Return: true fi @string starts with the keyword
262 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300263static bool starts_with(u16 *string, u16 *keyword)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100264{
265 for (; *keyword; ++string, ++keyword) {
266 if (*string != *keyword)
267 return false;
268 }
269 return true;
270}
271
272/**
273 * do_help() - print help
274 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300275static void do_help(void)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100276{
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200277 error(u"dump - print device-tree\r\n");
Simon Glass90975372022-01-23 12:55:12 -0700278 error(u"load <dtb> - load device-tree from file\r\n");
279 error(u"save <dtb> - save device-tree to file\r\n");
280 error(u"exit - exit the shell\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100281}
282
283/**
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100284 * open_file_system() - open simple file system protocol
285 *
286 * file_system: interface of the simple file system protocol
287 * Return: status code
288 */
289static efi_status_t
290open_file_system(struct efi_simple_file_system_protocol **file_system)
291{
292 struct efi_loaded_image *loaded_image;
293 efi_status_t ret;
294 efi_handle_t *handle_buffer = NULL;
295 efi_uintn_t count;
296
297 ret = bs->open_protocol(handle, &loaded_image_guid,
298 (void **)&loaded_image, NULL, NULL,
299 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
300 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700301 error(u"Loaded image protocol not found\r\n");
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100302 return ret;
303 }
304
305 /* Open the simple file system protocol on the same partition */
306 ret = bs->open_protocol(loaded_image->device_handle,
307 &guid_simple_file_system_protocol,
308 (void **)file_system, NULL, NULL,
309 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
310 if (ret == EFI_SUCCESS)
311 return ret;
312
313 /* Open the simple file system protocol on the UEFI system partition */
314 ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid,
315 NULL, &count, &handle_buffer);
316 if (ret == EFI_SUCCESS && handle_buffer)
317 ret = bs->open_protocol(handle_buffer[0],
318 &guid_simple_file_system_protocol,
319 (void **)file_system, NULL, NULL,
320 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
321 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700322 error(u"Failed to open simple file system protocol\r\n");
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100323 if (handle)
324 bs->free_pool(handle_buffer);
325
326 return ret;
327}
328
329/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100330 * do_load() - load and install device-tree
331 *
332 * @filename: file name
333 * Return: status code
334 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300335static efi_status_t do_load(u16 *filename)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100336{
337 struct efi_dt_fixup_protocol *dt_fixup_prot;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100338 struct efi_simple_file_system_protocol *file_system;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100339 struct efi_file_handle *root = NULL, *file = NULL;
340 u64 addr = 0;
341 struct efi_file_info *info;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100342 struct fdt_header *dtb;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100343 efi_uintn_t buffer_size;
344 efi_uintn_t pages;
345 efi_status_t ret, ret2;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100346
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100347 ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
348 (void **)&dt_fixup_prot);
349 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700350 error(u"Device-tree fix-up protocol not found\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100351 return ret;
352 }
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100353
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100354 filename = skip_whitespace(filename);
355
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100356 ret = open_file_system(&file_system);
357 if (ret != EFI_SUCCESS)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100358 goto out;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100359
360 /* Open volume */
361 ret = file_system->open_volume(file_system, &root);
362 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700363 error(u"Failed to open volume\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100364 goto out;
365 }
366
367 /* Open file */
368 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
369 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700370 error(u"File not found\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100371 goto out;
372 }
373 /* Get file size */
374 buffer_size = 0;
375 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
376 if (ret != EFI_BUFFER_TOO_SMALL) {
Simon Glass90975372022-01-23 12:55:12 -0700377 error(u"Can't get file info size\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100378 goto out;
379 }
380 ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
381 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700382 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100383 goto out;
384 }
385 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
386 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700387 error(u"Can't get file info\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100388 goto out;
389 }
390 buffer_size = info->file_size;
391 pages = efi_size_in_pages(buffer_size);
392 ret = bs->free_pool(info);
393 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700394 error(u"Can't free memory pool\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100395 /* Read file */
396 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
397 EFI_ACPI_RECLAIM_MEMORY,
398 pages, &addr);
399 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700400 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100401 goto out;
402 }
403 dtb = (struct fdt_header *)(uintptr_t)addr;
404 ret = file->read(file, &buffer_size, dtb);
405 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700406 error(u"Can't read file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100407 goto out;
408 }
409 /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
410 ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
411 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
412 EFI_DT_INSTALL_TABLE);
413 if (ret == EFI_BUFFER_TOO_SMALL) {
414 /* Read file into larger buffer */
415 ret = bs->free_pages(addr, pages);
416 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700417 error(u"Can't free memory pages\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100418 pages = efi_size_in_pages(buffer_size);
419 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
420 EFI_ACPI_RECLAIM_MEMORY,
421 pages, &addr);
422 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700423 error(u"Out of memory\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100424 goto out;
425 }
426 dtb = (struct fdt_header *)(uintptr_t)addr;
427 ret = file->setpos(file, 0);
428 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700429 error(u"Can't position file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100430 goto out;
431 }
432 ret = file->read(file, &buffer_size, dtb);
433 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700434 error(u"Can't read file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100435 goto out;
436 }
437 buffer_size = pages << EFI_PAGE_SHIFT;
438 ret = dt_fixup_prot->fixup(
439 dt_fixup_prot, dtb, &buffer_size,
440 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
441 EFI_DT_INSTALL_TABLE);
442 }
443 if (ret == EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700444 print(u"device-tree installed\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100445 else
Simon Glass90975372022-01-23 12:55:12 -0700446 error(u"Device-tree fix-up failed\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100447out:
448 if (addr) {
449 ret2 = bs->free_pages(addr, pages);
450 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700451 error(u"Can't free memory pages\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100452 }
453 if (file) {
454 ret2 = file->close(file);
455 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700456 error(u"Can't close file\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100457 }
458 if (root) {
459 ret2 = root->close(root);
460 if (ret2 != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700461 error(u"Can't close volume\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100462 }
463 return ret;
464}
465
466/**
467 * do_save() - save current device-tree
468 *
469 * @filename: file name
470 * Return: status code
471 */
Ilias Apalodimascf5b09e2024-10-26 10:43:17 +0300472static efi_status_t do_save(u16 *filename)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100473{
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100474 struct efi_simple_file_system_protocol *file_system;
475 efi_uintn_t dtb_size;
476 struct efi_file_handle *root, *file;
477 struct fdt_header *dtb;
478 efi_uintn_t ret;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100479
480 dtb = get_dtb(systable);
481 if (!dtb) {
Simon Glass90975372022-01-23 12:55:12 -0700482 error(u"DTB not found\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100483 return EFI_NOT_FOUND;
484 }
485 if (f2h(dtb->magic) != FDT_MAGIC) {
Simon Glass90975372022-01-23 12:55:12 -0700486 error(u"Wrong device tree magic\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100487 return EFI_NOT_FOUND;
488 }
489 dtb_size = f2h(dtb->totalsize);
490
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100491 filename = skip_whitespace(filename);
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100492
Heinrich Schuchardtfd20d8a2021-02-02 18:02:19 +0100493 ret = open_file_system(&file_system);
494 if (ret != EFI_SUCCESS)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100495 return ret;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100496
497 /* Open volume */
498 ret = file_system->open_volume(file_system, &root);
499 if (ret != EFI_SUCCESS) {
Simon Glass90975372022-01-23 12:55:12 -0700500 error(u"Failed to open volume\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100501 return ret;
502 }
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100503 /* Check if file already exists */
504 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
505 if (ret == EFI_SUCCESS) {
506 file->close(file);
Simon Glass90975372022-01-23 12:55:12 -0700507 print(u"Overwrite existing file (y/n)? ");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100508 ret = efi_input_yn();
Simon Glass90975372022-01-23 12:55:12 -0700509 print(u"\r\n");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100510 if (ret != EFI_SUCCESS) {
511 root->close(root);
Simon Glass90975372022-01-23 12:55:12 -0700512 error(u"Aborted by user\r\n");
Heinrich Schuchardt2e88aad2021-01-17 05:13:21 +0100513 return ret;
514 }
515 }
516
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100517 /* Create file */
518 ret = root->open(root, &file, filename,
519 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
520 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
521 if (ret == EFI_SUCCESS) {
522 /* Write file */
523 ret = file->write(file, &dtb_size, dtb);
524 if (ret != EFI_SUCCESS)
Simon Glass90975372022-01-23 12:55:12 -0700525 error(u"Failed to write file\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100526 file->close(file);
527 } else {
Simon Glass90975372022-01-23 12:55:12 -0700528 error(u"Failed to open file\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100529 }
530 root->close(root);
531
532 if (ret == EFI_SUCCESS) {
Heinrich Schuchardt9d4c19e2021-01-19 10:06:00 +0100533 print(filename);
Simon Glass90975372022-01-23 12:55:12 -0700534 print(u" written\r\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100535 }
536
537 return ret;
538}
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100539
540/**
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200541 * indent() - print a number of tabstops
542 *
543 * @level: indentation level
544 */
545static void indent(u32 level)
546{
547 for (; level; --level)
548 print(u"\t");
549}
550
551/**
552 * is_string_value() - determine if property is a string
553 *
554 * If a property is a string, an x-string, or a u32 cannot be deducted
555 * from the device-tree. Therefore a heuristic is used.
556 *
557 * @str: pointer to device-tree property
558 * @len: length of the device-tree property
559 * Return: 1 for string, 0 otherwise
560 */
561static int is_string_value(const unsigned char *str, u32 len)
562{
563 int nonzero_flag = 0;
564
565 /* Zero length or not ending with 0x00 */
566 if (!len || str[len - 1])
567 return 0;
568
569 for (u32 i = 0; i < len; ++i) {
570 if (!str[i]) {
571 /* Zero length string or two consecutive 0x00 */
572 if (!nonzero_flag)
573 return 0;
574
575 nonzero_flag = 0;
576
577 continue;
578 }
579 /* Non-printable */
580 if (str[i] < 0x20 || str[i] >= 0x80)
581 return 0;
582
583 nonzero_flag = 1;
584 }
585
586 return 1;
587}
588
589/**
590 * print_property() - print device-tree property
591 *
592 * If a property is a string, an x-string, or a u32 cannot be deducted
593 * from the device-tree. Therefore a heuristic is used.
594 *
595 * @str: property value
596 * @len: length of property value
597 */
598static void print_property(const unsigned char *val, u32 len)
599{
600 if (is_string_value(val, len)) {
601 /* string */
602 print(u"\"");
603 for (int i = 0; i < len - 1; ++i)
604 print_char(val[i]);
605 print(u"\"");
606 } else if (len & 0x3) {
607 /* byte string */
608 print(u"[");
609 for (int i = 0; i < len; ++i) {
610 if (i)
611 print(u" ");
612 printx(val[i]);
613 }
614 print(u"]\"");
615 } else {
616 /* cell list */
617 print(u"<");
618 for (u32 i = 0; i < len; ++i) {
619 if ((i & 0x3) == 0) {
620 if (i > 0)
621 print(u" ");
622 print(u"0x");
623 }
624 printx(val[i]);
625 }
626 print(u">");
627 }
628}
629
630/**
631 * print_mem_res_block() - print memory reservation block
632 *
633 * @rsvblk: memory reservation block
634 */
635static void print_mem_res_block(const struct fdt_reserve_entry *rsvblk)
636{
637 for (; rsvblk->address || rsvblk->size; ++rsvblk) {
638 const unsigned char *val;
639
640 print(u"/memreserve/ 0x");
641 val = (const unsigned char *)&rsvblk->address;
642 for (u32 i = 0; i < sizeof(u64); ++i)
643 printx(val[i]);
644 print(u" 0x");
645 val = (const unsigned char *)&rsvblk->size;
646 for (u32 i = 0; i < sizeof(u64); ++i)
647 printx(val[i]);
648 print(u";\r\n");
649 }
650}
651
652/**
653 * do_dump() - print device-tree
654 */
655static efi_status_t do_dump(void)
656{
657 const unsigned char *fdt;
658 struct fdt_header *header;
659 const u32 *end;
660 const u32 *pos;
661 const char *strings;
662 u32 level = 0;
663
664 fdt = get_dtb(systable);
665 if (!fdt) {
666 error(u"DTB not found\r\n");
667 return EFI_NOT_FOUND;
668 }
669
670 header = (struct fdt_header *)fdt;
671 if (f2h(header->magic) != FDT_MAGIC) {
672 error(u"Wrong device tree magic\r\n");
673 error(u"Not a device-tree\r\n");
674 return EFI_LOAD_ERROR;
675 }
676
677 pos = (u32 *)(fdt + f2h(header->off_dt_struct));
678 end = &pos[f2h(header->totalsize) >> 2];
679 strings = fdt + f2h(header->off_dt_strings);
680
681 print(u"/dts-v1/;\r\n");
682
683 print_mem_res_block((const struct fdt_reserve_entry *)
684 (fdt + f2h(header->off_mem_rsvmap)));
685
686 print(u"/");
687 for (; pos < end;) {
688 switch (f2h(pos[0])) {
689 case FDT_BEGIN_NODE: {
690 const char *c = (char *)&pos[1];
691 size_t i;
692
693 indent(level);
694 for (i = 0; c[i]; ++i)
695 print_char(c[i]);
696 print(u" {\n\r");
697
698 ++level;
699 pos = &pos[2 + (i >> 2)];
700 break;
701 }
702 case FDT_PROP: {
703 struct fdt_property *prop = (struct fdt_property *)pos;
704 const unsigned char *label = &strings[f2h(prop->nameoff)];
705 u32 len = f2h(prop->len);
706 const unsigned char *str = (unsigned char *)&pos[3];
707
708 indent(level);
709 for (int i = 0; label[i]; ++i)
710 print_char(label[i]);
711
712 if (len) {
713 print(u" = ");
714 print_property(str, len);
715 }
716 print(u";\r\n");
717
718 pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
719 break;
720 }
721 case FDT_NOP:
722 ++pos;
723 break;
724 case FDT_END_NODE:
725 if (!level) {
726 error(u"Extraneous end node\r\n");
727 return EFI_LOAD_ERROR;
728 }
729
730 --level;
731 indent(level);
732 print(u"};\n\r");
733 ++pos;
734 break;
735 case FDT_END:
736 if (level) {
737 error(u"Missing end node\r\n");
738 return EFI_LOAD_ERROR;
739 }
740 return EFI_SUCCESS;
741 default:
742 error(u"Invalid device tree token\r\n");
743 return EFI_LOAD_ERROR;
744 }
745 }
746 error(u"Overrun\r\n");
747
748 return EFI_LOAD_ERROR;
749}
750
751/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100752 * efi_main() - entry point of the EFI application.
753 *
754 * @handle: handle of the loaded image
755 * @systab: system table
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +0100756 * Return: status code
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100757 */
758efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
759 struct efi_system_table *systab)
760{
761 handle = image_handle;
762 systable = systab;
763 cerr = systable->std_err;
764 cout = systable->con_out;
765 cin = systable->con_in;
766 bs = systable->boottime;
767
768 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
769 cout->clear_screen(cout);
770 cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
Simon Glass90975372022-01-23 12:55:12 -0700771 print(u"DTB Dump\r\n========\r\n\r\n");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100772 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
773
774 for (;;) {
775 u16 command[BUFFER_SIZE];
776 u16 *pos;
777 efi_uintn_t ret;
778
Simon Glass90975372022-01-23 12:55:12 -0700779 print(u"=> ");
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100780 ret = efi_input(command, sizeof(command));
781 if (ret == EFI_ABORTED)
782 break;
783 pos = skip_whitespace(command);
Simon Glass90975372022-01-23 12:55:12 -0700784 if (starts_with(pos, u"exit"))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100785 break;
Heinrich Schuchardt67a81d22024-06-29 09:00:46 +0200786 else if (starts_with(pos, u"dump"))
787 do_dump();
Simon Glass90975372022-01-23 12:55:12 -0700788 else if (starts_with(pos, u"load "))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100789 do_load(pos + 5);
Simon Glass90975372022-01-23 12:55:12 -0700790 else if (starts_with(pos, u"save "))
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100791 do_save(pos + 5);
792 else
793 do_help();
794 }
795
796 cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
797 cout->clear_screen(cout);
798 return EFI_SUCCESS;
799}