blob: cbc9067b5a5bba6e7f5d447a4746801bdad04923 [file] [log] [blame]
Heinrich Schuchardtb1aa6fc2020-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
9#include <common.h>
10#include <efi_api.h>
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +010011#include <efi_dt_fixup.h>
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +010012
13#define BUFFER_SIZE 64
14#define ESC 0x17
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +010015
16#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +010017
18static struct efi_simple_text_output_protocol *cerr;
19static struct efi_simple_text_output_protocol *cout;
20static struct efi_simple_text_input_protocol *cin;
21static struct efi_boot_services *bs;
22static const efi_guid_t fdt_guid = EFI_FDT_GUID;
23static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
24static const efi_guid_t guid_simple_file_system_protocol =
25 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +010026static efi_handle_t handle;
27static struct efi_system_table *systable;
28static const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID;
29static const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID;
30
31/**
32 * error() - print error string
33 *
34 * @string: error text
35 */
36static void error(u16 *string)
37{
38 cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
39 cout->output_string(cout, string);
40 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
41}
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +010042
43/**
Heinrich Schuchardtb6f60802021-01-17 05:13:21 +010044 * efi_input_yn() - get answer to yes/no question
45 *
46 * Return:
47 * y or Y
48 * EFI_SUCCESS
49 * n or N
50 * EFI_ACCESS_DENIED
51 * ESC
52 * EFI_ABORTED
53 */
54static efi_status_t efi_input_yn(void)
55{
56 struct efi_input_key key = {0};
57 efi_uintn_t index;
58 efi_status_t ret;
59
60 /* Drain the console input */
61 ret = cin->reset(cin, true);
62 for (;;) {
63 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
64 if (ret != EFI_SUCCESS)
65 continue;
66 ret = cin->read_key_stroke(cin, &key);
67 if (ret != EFI_SUCCESS)
68 continue;
69 switch (key.scan_code) {
70 case 0x17: /* Escape */
71 return EFI_ABORTED;
72 default:
73 break;
74 }
75 /* Convert to lower case */
76 switch (key.unicode_char | 0x20) {
77 case 'y':
78 return EFI_SUCCESS;
79 case 'n':
80 return EFI_ACCESS_DENIED;
81 default:
82 break;
83 }
84 }
85}
86
87/**
88 * efi_input() - read string from console
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +010089 *
90 * @buffer: input buffer
91 * @buffer_size: buffer size
92 * Return: status code
93 */
94static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
95{
96 struct efi_input_key key = {0};
97 efi_uintn_t index;
98 efi_uintn_t pos = 0;
99 u16 outbuf[2] = L" ";
100 efi_status_t ret;
101
102 /* Drain the console input */
103 ret = cin->reset(cin, true);
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100104 *buffer = 0;
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100105 for (;;) {
106 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
107 if (ret != EFI_SUCCESS)
108 continue;
109 ret = cin->read_key_stroke(cin, &key);
110 if (ret != EFI_SUCCESS)
111 continue;
112 switch (key.scan_code) {
113 case 0x17: /* Escape */
114 cout->output_string(cout, L"\nAborted\n");
115 return EFI_ABORTED;
116 default:
117 break;
118 }
119 switch (key.unicode_char) {
120 case 0x08: /* Backspace */
121 if (pos) {
122 buffer[pos--] = 0;
123 cout->output_string(cout, L"\b \b");
124 }
125 break;
126 case 0x0a: /* Linefeed */
127 case 0x0d: /* Carriage return */
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100128 cout->output_string(cout, L"\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100129 return EFI_SUCCESS;
130 default:
131 break;
132 }
133 /* Ignore surrogate codes */
134 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
135 continue;
136 if (key.unicode_char >= 0x20 &&
137 pos < buffer_size - 1) {
138 *outbuf = key.unicode_char;
139 buffer[pos++] = key.unicode_char;
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100140 buffer[pos] = 0;
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100141 cout->output_string(cout, outbuf);
142 }
143 }
144}
145
146/*
147 * Convert FDT value to host endianness.
148 *
149 * @val FDT value
150 * @return converted value
151 */
152static u32 f2h(fdt32_t val)
153{
154 char *buf = (char *)&val;
155 char i;
156
157 /* Swap the bytes */
158 i = buf[0]; buf[0] = buf[3]; buf[3] = i;
159 i = buf[1]; buf[1] = buf[2]; buf[2] = i;
160 return *(u32 *)buf;
161}
162
163/**
164 * get_dtb() - get device tree
165 *
166 * @systable: system table
167 * Return: device tree or NULL
168 */
169void *get_dtb(struct efi_system_table *systable)
170{
171 void *dtb = NULL;
172 efi_uintn_t i;
173
174 for (i = 0; i < systable->nr_tables; ++i) {
175 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
176 sizeof(efi_guid_t))) {
177 dtb = systable->tables[i].table;
178 break;
179 }
180 }
181 return dtb;
182}
183
184/**
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100185 * skip_whitespace() - skip over leading whitespace
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100186 *
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100187 * @pos: UTF-16 string
188 * Return: pointer to first non-whitespace
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100189 */
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100190u16 *skip_whitespace(u16 *pos)
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100191{
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100192 for (; *pos && *pos <= 0x20; ++pos)
193 ;
194 return pos;
195}
196
197/**
198 * starts_with() - check if @string starts with @keyword
199 *
200 * @string: string to search for keyword
201 * @keyword: keyword to be searched
202 * Return: true fi @string starts with the keyword
203 */
204bool starts_with(u16 *string, u16 *keyword)
205{
206 for (; *keyword; ++string, ++keyword) {
207 if (*string != *keyword)
208 return false;
209 }
210 return true;
211}
212
213/**
214 * do_help() - print help
215 */
216void do_help(void)
217{
218 error(L"load <dtb> - load device-tree from file\n");
219 error(L"save <dtb> - save device-tree to file\n");
220 error(L"exit - exit the shell\n");
221}
222
223/**
224 * do_load() - load and install device-tree
225 *
226 * @filename: file name
227 * Return: status code
228 */
229efi_status_t do_load(u16 *filename)
230{
231 struct efi_dt_fixup_protocol *dt_fixup_prot;
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100232 struct efi_loaded_image *loaded_image;
233 struct efi_simple_file_system_protocol *file_system;
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100234 struct efi_file_handle *root = NULL, *file = NULL;
235 u64 addr = 0;
236 struct efi_file_info *info;
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100237 struct fdt_header *dtb;
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100238 efi_uintn_t buffer_size;
239 efi_uintn_t pages;
240 efi_status_t ret, ret2;
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100241
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100242 ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
243 (void **)&dt_fixup_prot);
244 if (ret != EFI_SUCCESS) {
245 error(L"Device-tree fix-up protocol not found\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100246 return ret;
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100247 }
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100248
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100249 filename = skip_whitespace(filename);
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100250
251 ret = bs->open_protocol(handle, &loaded_image_guid,
252 (void **)&loaded_image, NULL, NULL,
253 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
254 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100255 error(L"Loaded image protocol not found\n");
256 return ret;
257 }
258 /* Open the simple file system protocol */
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 error(L"Failed to open simple file system protocol\n");
265 goto out;
266 }
267
268 /* Open volume */
269 ret = file_system->open_volume(file_system, &root);
270 if (ret != EFI_SUCCESS) {
271 error(L"Failed to open volume\n");
272 goto out;
273 }
274
275 /* Open file */
276 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
277 if (ret != EFI_SUCCESS) {
278 error(L"File not found\n");
279 goto out;
280 }
281 /* Get file size */
282 buffer_size = 0;
283 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
284 if (ret != EFI_BUFFER_TOO_SMALL) {
285 error(L"Can't get file info size\n");
286 goto out;
287 }
288 ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
289 if (ret != EFI_SUCCESS) {
290 error(L"Out of memory\n");
291 goto out;
292 }
293 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
294 if (ret != EFI_SUCCESS) {
295 error(L"Can't get file info\n");
296 goto out;
297 }
298 buffer_size = info->file_size;
299 pages = efi_size_in_pages(buffer_size);
300 ret = bs->free_pool(info);
301 if (ret != EFI_SUCCESS)
302 error(L"Can't free memory pool\n");
303 /* Read file */
304 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
305 EFI_ACPI_RECLAIM_MEMORY,
306 pages, &addr);
307 if (ret != EFI_SUCCESS) {
308 error(L"Out of memory\n");
309 goto out;
310 }
311 dtb = (struct fdt_header *)(uintptr_t)addr;
312 ret = file->read(file, &buffer_size, dtb);
313 if (ret != EFI_SUCCESS) {
314 error(L"Can't read file\n");
315 goto out;
316 }
317 /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
318 ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
319 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
320 EFI_DT_INSTALL_TABLE);
321 if (ret == EFI_BUFFER_TOO_SMALL) {
322 /* Read file into larger buffer */
323 ret = bs->free_pages(addr, pages);
324 if (ret != EFI_SUCCESS)
325 error(L"Can't free memory pages\n");
326 pages = efi_size_in_pages(buffer_size);
327 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
328 EFI_ACPI_RECLAIM_MEMORY,
329 pages, &addr);
330 if (ret != EFI_SUCCESS) {
331 error(L"Out of memory\n");
332 goto out;
333 }
334 dtb = (struct fdt_header *)(uintptr_t)addr;
335 ret = file->setpos(file, 0);
336 if (ret != EFI_SUCCESS) {
337 error(L"Can't position file\n");
338 goto out;
339 }
340 ret = file->read(file, &buffer_size, dtb);
341 if (ret != EFI_SUCCESS) {
342 error(L"Can't read file\n");
343 goto out;
344 }
345 buffer_size = pages << EFI_PAGE_SHIFT;
346 ret = dt_fixup_prot->fixup(
347 dt_fixup_prot, dtb, &buffer_size,
348 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
349 EFI_DT_INSTALL_TABLE);
350 }
351 if (ret == EFI_SUCCESS)
352 cout->output_string(cout, L"device-tree installed\n");
353 else
354 error(L"Device-tree fix-up failed\n");
355out:
356 if (addr) {
357 ret2 = bs->free_pages(addr, pages);
358 if (ret2 != EFI_SUCCESS)
359 error(L"Can't free memory pages\n");
360 }
361 if (file) {
362 ret2 = file->close(file);
363 if (ret2 != EFI_SUCCESS)
364 error(L"Can't close file\n");
365 }
366 if (root) {
367 ret2 = root->close(root);
368 if (ret2 != EFI_SUCCESS)
369 error(L"Can't close volume\n");
370 }
371 return ret;
372}
373
374/**
375 * do_save() - save current device-tree
376 *
377 * @filename: file name
378 * Return: status code
379 */
380efi_status_t do_save(u16 *filename)
381{
382 struct efi_loaded_image *loaded_image;
383 struct efi_simple_file_system_protocol *file_system;
384 efi_uintn_t dtb_size;
385 struct efi_file_handle *root, *file;
386 struct fdt_header *dtb;
387 efi_uintn_t ret;
388
389 dtb = get_dtb(systable);
390 if (!dtb) {
391 error(L"DTB not found\n");
392 return EFI_NOT_FOUND;
393 }
394 if (f2h(dtb->magic) != FDT_MAGIC) {
395 error(L"Wrong device tree magic\n");
396 return EFI_NOT_FOUND;
397 }
398 dtb_size = f2h(dtb->totalsize);
399
400 filename = skip_whitespace(filename);
401
402 ret = bs->open_protocol(handle, &loaded_image_guid,
403 (void **)&loaded_image, NULL, NULL,
404 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
405 if (ret != EFI_SUCCESS) {
406 error(L"Loaded image protocol not found\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100407 return ret;
408 }
409
410 /* Open the simple file system protocol */
411 ret = bs->open_protocol(loaded_image->device_handle,
412 &guid_simple_file_system_protocol,
413 (void **)&file_system, NULL, NULL,
414 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
415 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100416 error(L"Failed to open simple file system protocol\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100417 return ret;
418 }
419
420 /* Open volume */
421 ret = file_system->open_volume(file_system, &root);
422 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100423 error(L"Failed to open volume\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100424 return ret;
425 }
Heinrich Schuchardtb6f60802021-01-17 05:13:21 +0100426 /* Check if file already exists */
427 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
428 if (ret == EFI_SUCCESS) {
429 file->close(file);
430 cout->output_string(cout, L"Overwrite existing file (y/n)? ");
431 ret = efi_input_yn();
432 cout->output_string(cout, L"\n");
433 if (ret != EFI_SUCCESS) {
434 root->close(root);
435 error(L"Aborted by user\n");
436 return ret;
437 }
438 }
439
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100440 /* Create file */
441 ret = root->open(root, &file, filename,
442 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
443 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
444 if (ret == EFI_SUCCESS) {
445 /* Write file */
446 ret = file->write(file, &dtb_size, dtb);
447 if (ret != EFI_SUCCESS)
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100448 error(L"Failed to write file\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100449 file->close(file);
450 } else {
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100451 error(L"Failed to open file\n");
Heinrich Schuchardtb1aa6fc2020-11-04 22:00:48 +0100452 }
453 root->close(root);
454
455 if (ret == EFI_SUCCESS) {
456 cout->output_string(cout, filename);
457 cout->output_string(cout, L" written\n");
458 }
459
460 return ret;
461}
Heinrich Schuchardt8e70f1c2020-12-13 19:13:57 +0100462
463/**
464 * efi_main() - entry point of the EFI application.
465 *
466 * @handle: handle of the loaded image
467 * @systab: system table
468 * @return: status code
469 */
470efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
471 struct efi_system_table *systab)
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
480 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
481 cout->clear_screen(cout);
482 cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
483 cout->output_string(cout, L"DTB Dump\n========\n\n");
484 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
485
486 for (;;) {
487 u16 command[BUFFER_SIZE];
488 u16 *pos;
489 efi_uintn_t ret;
490
491 cout->output_string(cout, L"=> ");
492 ret = efi_input(command, sizeof(command));
493 if (ret == EFI_ABORTED)
494 break;
495 pos = skip_whitespace(command);
496 if (starts_with(pos, L"exit"))
497 break;
498 else if (starts_with(pos, L"load "))
499 do_load(pos + 5);
500 else if (starts_with(pos, L"save "))
501 do_save(pos + 5);
502 else
503 do_help();
504 }
505
506 cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
507 cout->clear_screen(cout);
508 return EFI_SUCCESS;
509}