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