blob: efef759863e3ad23ac949332713e2d577ce4c97b [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
9#include <common.h>
10#include <efi_api.h>
Heinrich Schuchardt45910792020-12-13 19:13:57 +010011#include <efi_dt_fixup.h>
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010012
13#define BUFFER_SIZE 64
14#define ESC 0x17
Heinrich Schuchardt45910792020-12-13 19:13:57 +010015
16#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
Heinrich Schuchardt12083ba2020-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 Schuchardt45910792020-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 Schuchardt12083ba2020-11-04 22:00:48 +010042
43/**
44 * input() - read string from console
45 *
46 * @buffer: input buffer
47 * @buffer_size: buffer size
48 * Return: status code
49 */
50static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
51{
52 struct efi_input_key key = {0};
53 efi_uintn_t index;
54 efi_uintn_t pos = 0;
55 u16 outbuf[2] = L" ";
56 efi_status_t ret;
57
58 /* Drain the console input */
59 ret = cin->reset(cin, true);
Heinrich Schuchardt45910792020-12-13 19:13:57 +010060 *buffer = 0;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010061 for (;;) {
62 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
63 if (ret != EFI_SUCCESS)
64 continue;
65 ret = cin->read_key_stroke(cin, &key);
66 if (ret != EFI_SUCCESS)
67 continue;
68 switch (key.scan_code) {
69 case 0x17: /* Escape */
70 cout->output_string(cout, L"\nAborted\n");
71 return EFI_ABORTED;
72 default:
73 break;
74 }
75 switch (key.unicode_char) {
76 case 0x08: /* Backspace */
77 if (pos) {
78 buffer[pos--] = 0;
79 cout->output_string(cout, L"\b \b");
80 }
81 break;
82 case 0x0a: /* Linefeed */
83 case 0x0d: /* Carriage return */
Heinrich Schuchardt45910792020-12-13 19:13:57 +010084 cout->output_string(cout, L"\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010085 return EFI_SUCCESS;
86 default:
87 break;
88 }
89 /* Ignore surrogate codes */
90 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
91 continue;
92 if (key.unicode_char >= 0x20 &&
93 pos < buffer_size - 1) {
94 *outbuf = key.unicode_char;
95 buffer[pos++] = key.unicode_char;
Heinrich Schuchardt45910792020-12-13 19:13:57 +010096 buffer[pos] = 0;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +010097 cout->output_string(cout, outbuf);
98 }
99 }
100}
101
102/*
103 * Convert FDT value to host endianness.
104 *
105 * @val FDT value
106 * @return converted value
107 */
108static u32 f2h(fdt32_t val)
109{
110 char *buf = (char *)&val;
111 char i;
112
113 /* Swap the bytes */
114 i = buf[0]; buf[0] = buf[3]; buf[3] = i;
115 i = buf[1]; buf[1] = buf[2]; buf[2] = i;
116 return *(u32 *)buf;
117}
118
119/**
120 * get_dtb() - get device tree
121 *
122 * @systable: system table
123 * Return: device tree or NULL
124 */
125void *get_dtb(struct efi_system_table *systable)
126{
127 void *dtb = NULL;
128 efi_uintn_t i;
129
130 for (i = 0; i < systable->nr_tables; ++i) {
131 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
132 sizeof(efi_guid_t))) {
133 dtb = systable->tables[i].table;
134 break;
135 }
136 }
137 return dtb;
138}
139
140/**
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100141 * skip_whitespace() - skip over leading whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100142 *
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100143 * @pos: UTF-16 string
144 * Return: pointer to first non-whitespace
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100145 */
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100146u16 *skip_whitespace(u16 *pos)
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100147{
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100148 for (; *pos && *pos <= 0x20; ++pos)
149 ;
150 return pos;
151}
152
153/**
154 * starts_with() - check if @string starts with @keyword
155 *
156 * @string: string to search for keyword
157 * @keyword: keyword to be searched
158 * Return: true fi @string starts with the keyword
159 */
160bool starts_with(u16 *string, u16 *keyword)
161{
162 for (; *keyword; ++string, ++keyword) {
163 if (*string != *keyword)
164 return false;
165 }
166 return true;
167}
168
169/**
170 * do_help() - print help
171 */
172void do_help(void)
173{
174 error(L"load <dtb> - load device-tree from file\n");
175 error(L"save <dtb> - save device-tree to file\n");
176 error(L"exit - exit the shell\n");
177}
178
179/**
180 * do_load() - load and install device-tree
181 *
182 * @filename: file name
183 * Return: status code
184 */
185efi_status_t do_load(u16 *filename)
186{
187 struct efi_dt_fixup_protocol *dt_fixup_prot;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100188 struct efi_loaded_image *loaded_image;
189 struct efi_simple_file_system_protocol *file_system;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100190 struct efi_file_handle *root = NULL, *file = NULL;
191 u64 addr = 0;
192 struct efi_file_info *info;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100193 struct fdt_header *dtb;
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100194 efi_uintn_t buffer_size;
195 efi_uintn_t pages;
196 efi_status_t ret, ret2;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100197
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100198 ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL,
199 (void **)&dt_fixup_prot);
200 if (ret != EFI_SUCCESS) {
201 error(L"Device-tree fix-up protocol not found\n");
202 return ret;
203 }
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100204
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100205 filename = skip_whitespace(filename);
206
207 ret = bs->open_protocol(handle, &loaded_image_guid,
208 (void **)&loaded_image, NULL, NULL,
209 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
210 if (ret != EFI_SUCCESS) {
211 error(L"Loaded image protocol not found\n");
212 return ret;
213 }
214 /* Open the simple file system protocol */
215 ret = bs->open_protocol(loaded_image->device_handle,
216 &guid_simple_file_system_protocol,
217 (void **)&file_system, NULL, NULL,
218 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
219 if (ret != EFI_SUCCESS) {
220 error(L"Failed to open simple file system protocol\n");
221 goto out;
222 }
223
224 /* Open volume */
225 ret = file_system->open_volume(file_system, &root);
226 if (ret != EFI_SUCCESS) {
227 error(L"Failed to open volume\n");
228 goto out;
229 }
230
231 /* Open file */
232 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
233 if (ret != EFI_SUCCESS) {
234 error(L"File not found\n");
235 goto out;
236 }
237 /* Get file size */
238 buffer_size = 0;
239 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL);
240 if (ret != EFI_BUFFER_TOO_SMALL) {
241 error(L"Can't get file info size\n");
242 goto out;
243 }
244 ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info);
245 if (ret != EFI_SUCCESS) {
246 error(L"Out of memory\n");
247 goto out;
248 }
249 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info);
250 if (ret != EFI_SUCCESS) {
251 error(L"Can't get file info\n");
252 goto out;
253 }
254 buffer_size = info->file_size;
255 pages = efi_size_in_pages(buffer_size);
256 ret = bs->free_pool(info);
257 if (ret != EFI_SUCCESS)
258 error(L"Can't free memory pool\n");
259 /* Read file */
260 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
261 EFI_ACPI_RECLAIM_MEMORY,
262 pages, &addr);
263 if (ret != EFI_SUCCESS) {
264 error(L"Out of memory\n");
265 goto out;
266 }
267 dtb = (struct fdt_header *)(uintptr_t)addr;
268 ret = file->read(file, &buffer_size, dtb);
269 if (ret != EFI_SUCCESS) {
270 error(L"Can't read file\n");
271 goto out;
272 }
273 /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */
274 ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size,
275 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
276 EFI_DT_INSTALL_TABLE);
277 if (ret == EFI_BUFFER_TOO_SMALL) {
278 /* Read file into larger buffer */
279 ret = bs->free_pages(addr, pages);
280 if (ret != EFI_SUCCESS)
281 error(L"Can't free memory pages\n");
282 pages = efi_size_in_pages(buffer_size);
283 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES,
284 EFI_ACPI_RECLAIM_MEMORY,
285 pages, &addr);
286 if (ret != EFI_SUCCESS) {
287 error(L"Out of memory\n");
288 goto out;
289 }
290 dtb = (struct fdt_header *)(uintptr_t)addr;
291 ret = file->setpos(file, 0);
292 if (ret != EFI_SUCCESS) {
293 error(L"Can't position file\n");
294 goto out;
295 }
296 ret = file->read(file, &buffer_size, dtb);
297 if (ret != EFI_SUCCESS) {
298 error(L"Can't read file\n");
299 goto out;
300 }
301 buffer_size = pages << EFI_PAGE_SHIFT;
302 ret = dt_fixup_prot->fixup(
303 dt_fixup_prot, dtb, &buffer_size,
304 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY |
305 EFI_DT_INSTALL_TABLE);
306 }
307 if (ret == EFI_SUCCESS)
308 cout->output_string(cout, L"device-tree installed\n");
309 else
310 error(L"Device-tree fix-up failed\n");
311out:
312 if (addr) {
313 ret2 = bs->free_pages(addr, pages);
314 if (ret2 != EFI_SUCCESS)
315 error(L"Can't free memory pages\n");
316 }
317 if (file) {
318 ret2 = file->close(file);
319 if (ret2 != EFI_SUCCESS)
320 error(L"Can't close file\n");
321 }
322 if (root) {
323 ret2 = root->close(root);
324 if (ret2 != EFI_SUCCESS)
325 error(L"Can't close volume\n");
326 }
327 return ret;
328}
329
330/**
331 * do_save() - save current device-tree
332 *
333 * @filename: file name
334 * Return: status code
335 */
336efi_status_t do_save(u16 *filename)
337{
338 struct efi_loaded_image *loaded_image;
339 struct efi_simple_file_system_protocol *file_system;
340 efi_uintn_t dtb_size;
341 struct efi_file_handle *root, *file;
342 struct fdt_header *dtb;
343 efi_uintn_t ret;
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100344
345 dtb = get_dtb(systable);
346 if (!dtb) {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100347 error(L"DTB not found\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100348 return EFI_NOT_FOUND;
349 }
350 if (f2h(dtb->magic) != FDT_MAGIC) {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100351 error(L"Wrong device tree magic\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100352 return EFI_NOT_FOUND;
353 }
354 dtb_size = f2h(dtb->totalsize);
355
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100356 filename = skip_whitespace(filename);
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100357
358 ret = bs->open_protocol(handle, &loaded_image_guid,
359 (void **)&loaded_image, NULL, NULL,
360 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
361 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100362 error(L"Loaded image protocol not found\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100363 return ret;
364 }
365
366 /* Open the simple file system protocol */
367 ret = bs->open_protocol(loaded_image->device_handle,
368 &guid_simple_file_system_protocol,
369 (void **)&file_system, NULL, NULL,
370 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
371 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100372 error(L"Failed to open simple file system protocol\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100373 return ret;
374 }
375
376 /* Open volume */
377 ret = file_system->open_volume(file_system, &root);
378 if (ret != EFI_SUCCESS) {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100379 error(L"Failed to open volume\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100380 return ret;
381 }
382 /* Create file */
383 ret = root->open(root, &file, filename,
384 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
385 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
386 if (ret == EFI_SUCCESS) {
387 /* Write file */
388 ret = file->write(file, &dtb_size, dtb);
389 if (ret != EFI_SUCCESS)
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100390 error(L"Failed to write file\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100391 file->close(file);
392 } else {
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100393 error(L"Failed to open file\n");
Heinrich Schuchardt12083ba2020-11-04 22:00:48 +0100394 }
395 root->close(root);
396
397 if (ret == EFI_SUCCESS) {
398 cout->output_string(cout, filename);
399 cout->output_string(cout, L" written\n");
400 }
401
402 return ret;
403}
Heinrich Schuchardt45910792020-12-13 19:13:57 +0100404
405/**
406 * efi_main() - entry point of the EFI application.
407 *
408 * @handle: handle of the loaded image
409 * @systab: system table
410 * @return: status code
411 */
412efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
413 struct efi_system_table *systab)
414{
415 handle = image_handle;
416 systable = systab;
417 cerr = systable->std_err;
418 cout = systable->con_out;
419 cin = systable->con_in;
420 bs = systable->boottime;
421
422 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
423 cout->clear_screen(cout);
424 cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
425 cout->output_string(cout, L"DTB Dump\n========\n\n");
426 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
427
428 for (;;) {
429 u16 command[BUFFER_SIZE];
430 u16 *pos;
431 efi_uintn_t ret;
432
433 cout->output_string(cout, L"=> ");
434 ret = efi_input(command, sizeof(command));
435 if (ret == EFI_ABORTED)
436 break;
437 pos = skip_whitespace(command);
438 if (starts_with(pos, L"exit"))
439 break;
440 else if (starts_with(pos, L"load "))
441 do_load(pos + 5);
442 else if (starts_with(pos, L"save "))
443 do_save(pos + 5);
444 else
445 do_help();
446 }
447
448 cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
449 cout->clear_screen(cout);
450 return EFI_SUCCESS;
451}