Heinrich Schuchardt | 12083ba | 2020-11-04 22:00:48 +0100 | [diff] [blame] | 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 | } |