1230557Sjimharris// SPDX-License-Identifier: GPL-2.0+ 2230557Sjimharris/* 3230557Sjimharris * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> 4230557Sjimharris * 5230557Sjimharris * dtbdump.efi saves the device tree provided as a configuration table 6230557Sjimharris * to a file. 7230557Sjimharris */ 8230557Sjimharris 9230557Sjimharris#include <efi_api.h> 10230557Sjimharris#include <efi_dt_fixup.h> 11230557Sjimharris#include <part.h> 12230557Sjimharris#include <linux/libfdt.h> 13230557Sjimharris 14230557Sjimharris#define BUFFER_SIZE 64 15230557Sjimharris#define ESC 0x17 16230557Sjimharris 17230557Sjimharris#define efi_size_in_pages(size) ((size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT) 18230557Sjimharris 19230557Sjimharrisstatic struct efi_simple_text_output_protocol *cerr; 20230557Sjimharrisstatic struct efi_simple_text_output_protocol *cout; 21230557Sjimharrisstatic struct efi_simple_text_input_protocol *cin; 22230557Sjimharrisstatic struct efi_boot_services *bs; 23230557Sjimharrisstatic const efi_guid_t fdt_guid = EFI_FDT_GUID; 24230557Sjimharrisstatic const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; 25230557Sjimharrisstatic const efi_guid_t guid_simple_file_system_protocol = 26230557Sjimharris EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; 27230557Sjimharrisstatic efi_handle_t handle; 28230557Sjimharrisstatic struct efi_system_table *systable; 29230557Sjimharrisstatic const efi_guid_t efi_dt_fixup_protocol_guid = EFI_DT_FIXUP_PROTOCOL_GUID; 30230557Sjimharrisstatic const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID; 31230557Sjimharrisstatic const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; 32230557Sjimharris 33230557Sjimharris/** 34230557Sjimharris * print() - print string 35230557Sjimharris * 36230557Sjimharris * @string: text 37230557Sjimharris */ 38230557Sjimharrisstatic void print(u16 *string) 39230557Sjimharris{ 40230557Sjimharris cout->output_string(cout, string); 41230557Sjimharris} 42230557Sjimharris 43230557Sjimharris/** 44230557Sjimharris * error() - print error string 45230557Sjimharris * 46230557Sjimharris * @string: error text 47230557Sjimharris */ 48230557Sjimharrisstatic void error(u16 *string) 49230557Sjimharris{ 50230557Sjimharris cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); 51230557Sjimharris print(string); 52230557Sjimharris cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); 53230557Sjimharris} 54230557Sjimharris 55230557Sjimharris/** 56230557Sjimharris * efi_input_yn() - get answer to yes/no question 57230557Sjimharris * 58230557Sjimharris * Return: 59230557Sjimharris * y or Y 60230557Sjimharris * EFI_SUCCESS 61230557Sjimharris * n or N 62230557Sjimharris * EFI_ACCESS_DENIED 63230557Sjimharris * ESC 64230557Sjimharris * EFI_ABORTED 65230557Sjimharris */ 66230557Sjimharrisstatic efi_status_t efi_input_yn(void) 67230557Sjimharris{ 68230557Sjimharris struct efi_input_key key = {0}; 69230557Sjimharris efi_uintn_t index; 70230557Sjimharris efi_status_t ret; 71230557Sjimharris 72230557Sjimharris /* Drain the console input */ 73230557Sjimharris ret = cin->reset(cin, true); 74230557Sjimharris for (;;) { 75230557Sjimharris ret = bs->wait_for_event(1, &cin->wait_for_key, &index); 76230557Sjimharris if (ret != EFI_SUCCESS) 77230557Sjimharris continue; 78230557Sjimharris ret = cin->read_key_stroke(cin, &key); 79230557Sjimharris if (ret != EFI_SUCCESS) 80230557Sjimharris continue; 81230557Sjimharris switch (key.scan_code) { 82230557Sjimharris case 0x17: /* Escape */ 83230557Sjimharris return EFI_ABORTED; 84230557Sjimharris default: 85230557Sjimharris break; 86230557Sjimharris } 87230557Sjimharris /* Convert to lower case */ 88230557Sjimharris switch (key.unicode_char | 0x20) { 89230557Sjimharris case 'y': 90230557Sjimharris return EFI_SUCCESS; 91230557Sjimharris case 'n': 92230557Sjimharris return EFI_ACCESS_DENIED; 93230557Sjimharris default: 94230557Sjimharris break; 95230557Sjimharris } 96230557Sjimharris } 97230557Sjimharris} 98230557Sjimharris 99230557Sjimharris/** 100230557Sjimharris * efi_input() - read string from console 101230557Sjimharris * 102230557Sjimharris * @buffer: input buffer 103230557Sjimharris * @buffer_size: buffer size 104230557Sjimharris * Return: status code 105230557Sjimharris */ 106230557Sjimharrisstatic efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) 107230557Sjimharris{ 108230557Sjimharris struct efi_input_key key = {0}; 109230557Sjimharris efi_uintn_t index; 110230557Sjimharris efi_uintn_t pos = 0; 111230557Sjimharris u16 outbuf[2] = u" "; 112230557Sjimharris efi_status_t ret; 113230557Sjimharris 114230557Sjimharris /* Drain the console input */ 115230557Sjimharris ret = cin->reset(cin, true); 116230557Sjimharris *buffer = 0; 117230557Sjimharris for (;;) { 118230557Sjimharris ret = bs->wait_for_event(1, &cin->wait_for_key, &index); 119230557Sjimharris if (ret != EFI_SUCCESS) 120230557Sjimharris continue; 121230557Sjimharris ret = cin->read_key_stroke(cin, &key); 122230557Sjimharris if (ret != EFI_SUCCESS) 123230557Sjimharris continue; 124230557Sjimharris switch (key.scan_code) { 125230557Sjimharris case 0x17: /* Escape */ 126230557Sjimharris print(u"\r\nAborted\r\n"); 127230557Sjimharris return EFI_ABORTED; 128230557Sjimharris default: 129230557Sjimharris break; 130230557Sjimharris } 131230557Sjimharris switch (key.unicode_char) { 132230557Sjimharris case 0x08: /* Backspace */ 133230557Sjimharris if (pos) { 134230557Sjimharris buffer[pos--] = 0; 135230557Sjimharris print(u"\b \b"); 136230557Sjimharris } 137230557Sjimharris break; 138230557Sjimharris case 0x0a: /* Linefeed */ 139230557Sjimharris case 0x0d: /* Carriage return */ 140230557Sjimharris print(u"\r\n"); 141230557Sjimharris return EFI_SUCCESS; 142230557Sjimharris default: 143230557Sjimharris break; 144230557Sjimharris } 145230557Sjimharris /* Ignore surrogate codes */ 146230557Sjimharris 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