efi_selftest: add tool to download dtb
[platform/kernel/u-boot.git] / lib / efi_selftest / dtbdump.c
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 <common.h>
10 #include <efi_api.h>
11
12 #define BUFFER_SIZE 64
13 #define ESC 0x17
14 #define DEFAULT_FILENAME L"dtb.dtb"
15
16 static struct efi_simple_text_output_protocol *cerr;
17 static struct efi_simple_text_output_protocol *cout;
18 static struct efi_simple_text_input_protocol *cin;
19 static struct efi_boot_services *bs;
20 static const efi_guid_t fdt_guid = EFI_FDT_GUID;
21 static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
22 static const efi_guid_t guid_simple_file_system_protocol =
23                                         EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
24
25 /**
26  * input() - read string from console
27  *
28  * @buffer:             input buffer
29  * @buffer_size:        buffer size
30  * Return:              status code
31  */
32 static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
33 {
34         struct efi_input_key key = {0};
35         efi_uintn_t index;
36         efi_uintn_t pos = 0;
37         u16 outbuf[2] = L" ";
38         efi_status_t ret;
39
40         /* Drain the console input */
41         ret = cin->reset(cin, true);
42         for (;;) {
43                 ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
44                 if (ret != EFI_SUCCESS)
45                         continue;
46                 ret = cin->read_key_stroke(cin, &key);
47                 if (ret != EFI_SUCCESS)
48                         continue;
49                 switch (key.scan_code) {
50                 case 0x17: /* Escape */
51                         cout->output_string(cout, L"\nAborted\n");
52                         return EFI_ABORTED;
53                 default:
54                         break;
55                 }
56                 switch (key.unicode_char) {
57                 case 0x08: /* Backspace */
58                         if (pos) {
59                                 buffer[pos--] = 0;
60                                 cout->output_string(cout, L"\b \b");
61                         }
62                         break;
63                 case 0x0a: /* Linefeed */
64                 case 0x0d: /* Carriage return */
65                         return EFI_SUCCESS;
66                 default:
67                         break;
68                 }
69                 /* Ignore surrogate codes */
70                 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
71                         continue;
72                 if (key.unicode_char >= 0x20 &&
73                     pos < buffer_size - 1) {
74                         *outbuf = key.unicode_char;
75                         buffer[pos++] = key.unicode_char;
76                         cout->output_string(cout, outbuf);
77                 }
78         }
79 }
80
81 /*
82  * Convert FDT value to host endianness.
83  *
84  * @val         FDT value
85  * @return      converted value
86  */
87 static u32 f2h(fdt32_t val)
88 {
89         char *buf = (char *)&val;
90         char i;
91
92         /* Swap the bytes */
93         i = buf[0]; buf[0] = buf[3]; buf[3] = i;
94         i = buf[1]; buf[1] = buf[2]; buf[2] = i;
95         return *(u32 *)buf;
96 }
97
98 /**
99  * get_dtb() - get device tree
100  *
101  * @systable:   system table
102  * Return:      device tree or NULL
103  */
104 void *get_dtb(struct efi_system_table *systable)
105 {
106         void *dtb = NULL;
107         efi_uintn_t i;
108
109         for (i = 0; i < systable->nr_tables; ++i) {
110                 if (!memcmp(&systable->tables[i].guid, &fdt_guid,
111                             sizeof(efi_guid_t))) {
112                         dtb = systable->tables[i].table;
113                         break;
114                 }
115         }
116         return dtb;
117 }
118
119 /**
120  * efi_main() - entry point of the EFI application.
121  *
122  * @handle:     handle of the loaded image
123  * @systable:   system table
124  * @return:     status code
125  */
126 efi_status_t EFIAPI efi_main(efi_handle_t handle,
127                              struct efi_system_table *systable)
128 {
129         efi_uintn_t ret;
130         u16 filename[BUFFER_SIZE] = {0};
131         efi_uintn_t dtb_size;
132         struct efi_loaded_image *loaded_image;
133         struct efi_simple_file_system_protocol *file_system;
134         struct efi_file_handle *root, *file;
135         struct fdt_header *dtb;
136
137         cerr = systable->std_err;
138         cout = systable->con_out;
139         cin = systable->con_in;
140         bs = systable->boottime;
141
142         cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
143         cout->clear_screen(cout);
144         cout->set_attribute(cout, EFI_YELLOW | EFI_BACKGROUND_BLACK);
145         cout->output_string(cout, L"DTB Dump\n\n");
146         cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
147
148         dtb = get_dtb(systable);
149         if (!dtb) {
150                 cerr->output_string(cout, L"DTB not found\n");
151                 return EFI_NOT_FOUND;
152         }
153         if (f2h(dtb->magic) != FDT_MAGIC) {
154                 cerr->output_string(cout, L"Wrong device tree magic\n");
155                 return EFI_NOT_FOUND;
156         }
157         dtb_size = f2h(dtb->totalsize);
158
159         cout->output_string(cout, L"Filename (" DEFAULT_FILENAME ")?\n");
160         ret = efi_input(filename, sizeof(filename));
161         if (ret != EFI_SUCCESS)
162                 return ret;
163         if (!*filename)
164                 memcpy(filename, DEFAULT_FILENAME, sizeof(DEFAULT_FILENAME));
165
166         cout->output_string(cout, L"\n");
167
168         ret = bs->open_protocol(handle, &loaded_image_guid,
169                                 (void **)&loaded_image, NULL, NULL,
170                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
171         if (ret != EFI_SUCCESS) {
172                 cerr->output_string(cout,
173                                     L"Loaded image protocol not found\n");
174                 return ret;
175         }
176
177         /* Open the simple file system protocol */
178         ret = bs->open_protocol(loaded_image->device_handle,
179                                 &guid_simple_file_system_protocol,
180                                 (void **)&file_system, NULL, NULL,
181                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
182         if (ret != EFI_SUCCESS) {
183                 cerr->output_string(
184                         cout, L"Failed to open simple file system protocol\n");
185                 return ret;
186         }
187
188         /* Open volume */
189         ret = file_system->open_volume(file_system, &root);
190         if (ret != EFI_SUCCESS) {
191                 cerr->output_string(cerr, L"Failed to open volume\n");
192                 return ret;
193         }
194         /* Create file */
195         ret = root->open(root, &file, filename,
196                          EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
197                          EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
198         if (ret == EFI_SUCCESS) {
199                 /* Write file */
200                 ret = file->write(file, &dtb_size, dtb);
201                 if (ret != EFI_SUCCESS)
202                         cerr->output_string(cerr, L"Failed to write file\n");
203                 file->close(file);
204         } else {
205                 cerr->output_string(cerr, L"Failed to open file\n");
206         }
207         root->close(root);
208
209         if (ret == EFI_SUCCESS) {
210                 cout->output_string(cout, filename);
211                 cout->output_string(cout, L" written\n");
212         }
213
214         return ret;
215 }