"Das U-Boot" Source Tree
at master 799 lines 18 kB view raw
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 * print_char() - print character 45 * 46 * 0x00 is replaced by '", "'. 47 * 48 * @c: - character 49 */ 50static void print_char(unsigned char c) 51{ 52 u16 out[2] = u"?"; 53 54 if (!c) { 55 print(u"\", \""); 56 return; 57 } 58 59 if (c > 0x1f && c < 0x80) 60 out[0] = c; 61 62 print(out); 63} 64 65/** 66 * print_hex_digit() - print hexadecimal digit 67 * 68 * @digit: digit to print 69 */ 70static void print_hex_digit(unsigned char digit) 71{ 72 if (digit < 10) 73 digit += '0'; 74 else 75 digit += 'a' - 10; 76 print_char(digit); 77} 78 79/** 80 * printx() - print hexadecimal byte 81 * 82 * @val: value to print 83 */ 84static void printx(unsigned char val) 85{ 86 print_hex_digit(val >> 4); 87 print_hex_digit(val & 0xf); 88} 89 90/** 91 * error() - print error string 92 * 93 * @string: error text 94 */ 95static void error(u16 *string) 96{ 97 cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK); 98 print(string); 99 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); 100} 101 102/** 103 * efi_input_yn() - get answer to yes/no question 104 * 105 * Return: 106 * y or Y 107 * EFI_SUCCESS 108 * n or N 109 * EFI_ACCESS_DENIED 110 * ESC 111 * EFI_ABORTED 112 */ 113static efi_status_t efi_input_yn(void) 114{ 115 struct efi_input_key key = {0}; 116 efi_uintn_t index; 117 efi_status_t ret; 118 119 /* Drain the console input */ 120 ret = cin->reset(cin, true); 121 for (;;) { 122 ret = bs->wait_for_event(1, &cin->wait_for_key, &index); 123 if (ret != EFI_SUCCESS) 124 continue; 125 ret = cin->read_key_stroke(cin, &key); 126 if (ret != EFI_SUCCESS) 127 continue; 128 switch (key.scan_code) { 129 case 0x17: /* Escape */ 130 return EFI_ABORTED; 131 default: 132 break; 133 } 134 /* Convert to lower case */ 135 switch (key.unicode_char | 0x20) { 136 case 'y': 137 return EFI_SUCCESS; 138 case 'n': 139 return EFI_ACCESS_DENIED; 140 default: 141 break; 142 } 143 } 144} 145 146/** 147 * efi_input() - read string from console 148 * 149 * @buffer: input buffer 150 * @buffer_size: buffer size 151 * Return: status code 152 */ 153static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) 154{ 155 struct efi_input_key key = {0}; 156 efi_uintn_t index; 157 efi_uintn_t pos = 0; 158 u16 outbuf[2] = u" "; 159 efi_status_t ret; 160 161 /* Drain the console input */ 162 ret = cin->reset(cin, true); 163 *buffer = 0; 164 for (;;) { 165 ret = bs->wait_for_event(1, &cin->wait_for_key, &index); 166 if (ret != EFI_SUCCESS) 167 continue; 168 ret = cin->read_key_stroke(cin, &key); 169 if (ret != EFI_SUCCESS) 170 continue; 171 switch (key.scan_code) { 172 case 0x17: /* Escape */ 173 print(u"\r\nAborted\r\n"); 174 return EFI_ABORTED; 175 default: 176 break; 177 } 178 switch (key.unicode_char) { 179 case 0x08: /* Backspace */ 180 if (pos) { 181 buffer[pos--] = 0; 182 print(u"\b \b"); 183 } 184 break; 185 case 0x0a: /* Linefeed */ 186 case 0x0d: /* Carriage return */ 187 print(u"\r\n"); 188 return EFI_SUCCESS; 189 default: 190 break; 191 } 192 /* Ignore surrogate codes */ 193 if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) 194 continue; 195 if (key.unicode_char >= 0x20 && 196 pos < buffer_size - 1) { 197 *outbuf = key.unicode_char; 198 buffer[pos++] = key.unicode_char; 199 buffer[pos] = 0; 200 print(outbuf); 201 } 202 } 203} 204 205/* 206 * Convert FDT value to host endianness. 207 * 208 * @val FDT value 209 * Return: converted value 210 */ 211static u32 f2h(fdt32_t val) 212{ 213 char *buf = (char *)&val; 214 char i; 215 216 /* Swap the bytes */ 217 i = buf[0]; buf[0] = buf[3]; buf[3] = i; 218 i = buf[1]; buf[1] = buf[2]; buf[2] = i; 219 return *(u32 *)buf; 220} 221 222/** 223 * get_dtb() - get device tree 224 * 225 * @systable: system table 226 * Return: device tree or NULL 227 */ 228static void *get_dtb(struct efi_system_table *systable) 229{ 230 void *dtb = NULL; 231 efi_uintn_t i; 232 233 for (i = 0; i < systable->nr_tables; ++i) { 234 if (!memcmp(&systable->tables[i].guid, &fdt_guid, 235 sizeof(efi_guid_t))) { 236 dtb = systable->tables[i].table; 237 break; 238 } 239 } 240 return dtb; 241} 242 243/** 244 * skip_whitespace() - skip over leading whitespace 245 * 246 * @pos: UTF-16 string 247 * Return: pointer to first non-whitespace 248 */ 249static u16 *skip_whitespace(u16 *pos) 250{ 251 for (; *pos && *pos <= 0x20; ++pos) 252 ; 253 return pos; 254} 255 256/** 257 * starts_with() - check if @string starts with @keyword 258 * 259 * @string: string to search for keyword 260 * @keyword: keyword to be searched 261 * Return: true fi @string starts with the keyword 262 */ 263static bool starts_with(u16 *string, u16 *keyword) 264{ 265 for (; *keyword; ++string, ++keyword) { 266 if (*string != *keyword) 267 return false; 268 } 269 return true; 270} 271 272/** 273 * do_help() - print help 274 */ 275static void do_help(void) 276{ 277 error(u"dump - print device-tree\r\n"); 278 error(u"load <dtb> - load device-tree from file\r\n"); 279 error(u"save <dtb> - save device-tree to file\r\n"); 280 error(u"exit - exit the shell\r\n"); 281} 282 283/** 284 * open_file_system() - open simple file system protocol 285 * 286 * file_system: interface of the simple file system protocol 287 * Return: status code 288 */ 289static efi_status_t 290open_file_system(struct efi_simple_file_system_protocol **file_system) 291{ 292 struct efi_loaded_image *loaded_image; 293 efi_status_t ret; 294 efi_handle_t *handle_buffer = NULL; 295 efi_uintn_t count; 296 297 ret = bs->open_protocol(handle, &loaded_image_guid, 298 (void **)&loaded_image, NULL, NULL, 299 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 300 if (ret != EFI_SUCCESS) { 301 error(u"Loaded image protocol not found\r\n"); 302 return ret; 303 } 304 305 /* Open the simple file system protocol on the same partition */ 306 ret = bs->open_protocol(loaded_image->device_handle, 307 &guid_simple_file_system_protocol, 308 (void **)file_system, NULL, NULL, 309 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 310 if (ret == EFI_SUCCESS) 311 return ret; 312 313 /* Open the simple file system protocol on the UEFI system partition */ 314 ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid, 315 NULL, &count, &handle_buffer); 316 if (ret == EFI_SUCCESS && handle_buffer) 317 ret = bs->open_protocol(handle_buffer[0], 318 &guid_simple_file_system_protocol, 319 (void **)file_system, NULL, NULL, 320 EFI_OPEN_PROTOCOL_GET_PROTOCOL); 321 if (ret != EFI_SUCCESS) 322 error(u"Failed to open simple file system protocol\r\n"); 323 if (handle) 324 bs->free_pool(handle_buffer); 325 326 return ret; 327} 328 329/** 330 * do_load() - load and install device-tree 331 * 332 * @filename: file name 333 * Return: status code 334 */ 335static efi_status_t do_load(u16 *filename) 336{ 337 struct efi_dt_fixup_protocol *dt_fixup_prot; 338 struct efi_simple_file_system_protocol *file_system; 339 struct efi_file_handle *root = NULL, *file = NULL; 340 u64 addr = 0; 341 struct efi_file_info *info; 342 struct fdt_header *dtb; 343 efi_uintn_t buffer_size; 344 efi_uintn_t pages; 345 efi_status_t ret, ret2; 346 347 ret = bs->locate_protocol(&efi_dt_fixup_protocol_guid, NULL, 348 (void **)&dt_fixup_prot); 349 if (ret != EFI_SUCCESS) { 350 error(u"Device-tree fix-up protocol not found\r\n"); 351 return ret; 352 } 353 354 filename = skip_whitespace(filename); 355 356 ret = open_file_system(&file_system); 357 if (ret != EFI_SUCCESS) 358 goto out; 359 360 /* Open volume */ 361 ret = file_system->open_volume(file_system, &root); 362 if (ret != EFI_SUCCESS) { 363 error(u"Failed to open volume\r\n"); 364 goto out; 365 } 366 367 /* Open file */ 368 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); 369 if (ret != EFI_SUCCESS) { 370 error(u"File not found\r\n"); 371 goto out; 372 } 373 /* Get file size */ 374 buffer_size = 0; 375 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, NULL); 376 if (ret != EFI_BUFFER_TOO_SMALL) { 377 error(u"Can't get file info size\r\n"); 378 goto out; 379 } 380 ret = bs->allocate_pool(EFI_LOADER_DATA, buffer_size, (void **)&info); 381 if (ret != EFI_SUCCESS) { 382 error(u"Out of memory\r\n"); 383 goto out; 384 } 385 ret = file->getinfo(file, &efi_file_info_guid, &buffer_size, info); 386 if (ret != EFI_SUCCESS) { 387 error(u"Can't get file info\r\n"); 388 goto out; 389 } 390 buffer_size = info->file_size; 391 pages = efi_size_in_pages(buffer_size); 392 ret = bs->free_pool(info); 393 if (ret != EFI_SUCCESS) 394 error(u"Can't free memory pool\r\n"); 395 /* Read file */ 396 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, 397 EFI_ACPI_RECLAIM_MEMORY, 398 pages, &addr); 399 if (ret != EFI_SUCCESS) { 400 error(u"Out of memory\r\n"); 401 goto out; 402 } 403 dtb = (struct fdt_header *)(uintptr_t)addr; 404 ret = file->read(file, &buffer_size, dtb); 405 if (ret != EFI_SUCCESS) { 406 error(u"Can't read file\r\n"); 407 goto out; 408 } 409 /* Fixup file, expecting EFI_BUFFER_TOO_SMALL */ 410 ret = dt_fixup_prot->fixup(dt_fixup_prot, dtb, &buffer_size, 411 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | 412 EFI_DT_INSTALL_TABLE); 413 if (ret == EFI_BUFFER_TOO_SMALL) { 414 /* Read file into larger buffer */ 415 ret = bs->free_pages(addr, pages); 416 if (ret != EFI_SUCCESS) 417 error(u"Can't free memory pages\r\n"); 418 pages = efi_size_in_pages(buffer_size); 419 ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, 420 EFI_ACPI_RECLAIM_MEMORY, 421 pages, &addr); 422 if (ret != EFI_SUCCESS) { 423 error(u"Out of memory\r\n"); 424 goto out; 425 } 426 dtb = (struct fdt_header *)(uintptr_t)addr; 427 ret = file->setpos(file, 0); 428 if (ret != EFI_SUCCESS) { 429 error(u"Can't position file\r\n"); 430 goto out; 431 } 432 ret = file->read(file, &buffer_size, dtb); 433 if (ret != EFI_SUCCESS) { 434 error(u"Can't read file\r\n"); 435 goto out; 436 } 437 buffer_size = pages << EFI_PAGE_SHIFT; 438 ret = dt_fixup_prot->fixup( 439 dt_fixup_prot, dtb, &buffer_size, 440 EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | 441 EFI_DT_INSTALL_TABLE); 442 } 443 if (ret == EFI_SUCCESS) 444 print(u"device-tree installed\r\n"); 445 else 446 error(u"Device-tree fix-up failed\r\n"); 447out: 448 if (addr) { 449 ret2 = bs->free_pages(addr, pages); 450 if (ret2 != EFI_SUCCESS) 451 error(u"Can't free memory pages\r\n"); 452 } 453 if (file) { 454 ret2 = file->close(file); 455 if (ret2 != EFI_SUCCESS) 456 error(u"Can't close file\r\n"); 457 } 458 if (root) { 459 ret2 = root->close(root); 460 if (ret2 != EFI_SUCCESS) 461 error(u"Can't close volume\r\n"); 462 } 463 return ret; 464} 465 466/** 467 * do_save() - save current device-tree 468 * 469 * @filename: file name 470 * Return: status code 471 */ 472static efi_status_t do_save(u16 *filename) 473{ 474 struct efi_simple_file_system_protocol *file_system; 475 efi_uintn_t dtb_size; 476 struct efi_file_handle *root, *file; 477 struct fdt_header *dtb; 478 efi_uintn_t ret; 479 480 dtb = get_dtb(systable); 481 if (!dtb) { 482 error(u"DTB not found\r\n"); 483 return EFI_NOT_FOUND; 484 } 485 if (f2h(dtb->magic) != FDT_MAGIC) { 486 error(u"Wrong device tree magic\r\n"); 487 return EFI_NOT_FOUND; 488 } 489 dtb_size = f2h(dtb->totalsize); 490 491 filename = skip_whitespace(filename); 492 493 ret = open_file_system(&file_system); 494 if (ret != EFI_SUCCESS) 495 return ret; 496 497 /* Open volume */ 498 ret = file_system->open_volume(file_system, &root); 499 if (ret != EFI_SUCCESS) { 500 error(u"Failed to open volume\r\n"); 501 return ret; 502 } 503 /* Check if file already exists */ 504 ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); 505 if (ret == EFI_SUCCESS) { 506 file->close(file); 507 print(u"Overwrite existing file (y/n)? "); 508 ret = efi_input_yn(); 509 print(u"\r\n"); 510 if (ret != EFI_SUCCESS) { 511 root->close(root); 512 error(u"Aborted by user\r\n"); 513 return ret; 514 } 515 } 516 517 /* Create file */ 518 ret = root->open(root, &file, filename, 519 EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | 520 EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); 521 if (ret == EFI_SUCCESS) { 522 /* Write file */ 523 ret = file->write(file, &dtb_size, dtb); 524 if (ret != EFI_SUCCESS) 525 error(u"Failed to write file\r\n"); 526 file->close(file); 527 } else { 528 error(u"Failed to open file\r\n"); 529 } 530 root->close(root); 531 532 if (ret == EFI_SUCCESS) { 533 print(filename); 534 print(u" written\r\n"); 535 } 536 537 return ret; 538} 539 540/** 541 * indent() - print a number of tabstops 542 * 543 * @level: indentation level 544 */ 545static void indent(u32 level) 546{ 547 for (; level; --level) 548 print(u"\t"); 549} 550 551/** 552 * is_string_value() - determine if property is a string 553 * 554 * If a property is a string, an x-string, or a u32 cannot be deducted 555 * from the device-tree. Therefore a heuristic is used. 556 * 557 * @str: pointer to device-tree property 558 * @len: length of the device-tree property 559 * Return: 1 for string, 0 otherwise 560 */ 561static int is_string_value(const unsigned char *str, u32 len) 562{ 563 int nonzero_flag = 0; 564 565 /* Zero length or not ending with 0x00 */ 566 if (!len || str[len - 1]) 567 return 0; 568 569 for (u32 i = 0; i < len; ++i) { 570 if (!str[i]) { 571 /* Zero length string or two consecutive 0x00 */ 572 if (!nonzero_flag) 573 return 0; 574 575 nonzero_flag = 0; 576 577 continue; 578 } 579 /* Non-printable */ 580 if (str[i] < 0x20 || str[i] >= 0x80) 581 return 0; 582 583 nonzero_flag = 1; 584 } 585 586 return 1; 587} 588 589/** 590 * print_property() - print device-tree property 591 * 592 * If a property is a string, an x-string, or a u32 cannot be deducted 593 * from the device-tree. Therefore a heuristic is used. 594 * 595 * @str: property value 596 * @len: length of property value 597 */ 598static void print_property(const unsigned char *val, u32 len) 599{ 600 if (is_string_value(val, len)) { 601 /* string */ 602 print(u"\""); 603 for (int i = 0; i < len - 1; ++i) 604 print_char(val[i]); 605 print(u"\""); 606 } else if (len & 0x3) { 607 /* byte string */ 608 print(u"["); 609 for (int i = 0; i < len; ++i) { 610 if (i) 611 print(u" "); 612 printx(val[i]); 613 } 614 print(u"]\""); 615 } else { 616 /* cell list */ 617 print(u"<"); 618 for (u32 i = 0; i < len; ++i) { 619 if ((i & 0x3) == 0) { 620 if (i > 0) 621 print(u" "); 622 print(u"0x"); 623 } 624 printx(val[i]); 625 } 626 print(u">"); 627 } 628} 629 630/** 631 * print_mem_res_block() - print memory reservation block 632 * 633 * @rsvblk: memory reservation block 634 */ 635static void print_mem_res_block(const struct fdt_reserve_entry *rsvblk) 636{ 637 for (; rsvblk->address || rsvblk->size; ++rsvblk) { 638 const unsigned char *val; 639 640 print(u"/memreserve/ 0x"); 641 val = (const unsigned char *)&rsvblk->address; 642 for (u32 i = 0; i < sizeof(u64); ++i) 643 printx(val[i]); 644 print(u" 0x"); 645 val = (const unsigned char *)&rsvblk->size; 646 for (u32 i = 0; i < sizeof(u64); ++i) 647 printx(val[i]); 648 print(u";\r\n"); 649 } 650} 651 652/** 653 * do_dump() - print device-tree 654 */ 655static efi_status_t do_dump(void) 656{ 657 const unsigned char *fdt; 658 struct fdt_header *header; 659 const u32 *end; 660 const u32 *pos; 661 const char *strings; 662 u32 level = 0; 663 664 fdt = get_dtb(systable); 665 if (!fdt) { 666 error(u"DTB not found\r\n"); 667 return EFI_NOT_FOUND; 668 } 669 670 header = (struct fdt_header *)fdt; 671 if (f2h(header->magic) != FDT_MAGIC) { 672 error(u"Wrong device tree magic\r\n"); 673 error(u"Not a device-tree\r\n"); 674 return EFI_LOAD_ERROR; 675 } 676 677 pos = (u32 *)(fdt + f2h(header->off_dt_struct)); 678 end = &pos[f2h(header->totalsize) >> 2]; 679 strings = fdt + f2h(header->off_dt_strings); 680 681 print(u"/dts-v1/;\r\n"); 682 683 print_mem_res_block((const struct fdt_reserve_entry *) 684 (fdt + f2h(header->off_mem_rsvmap))); 685 686 print(u"/"); 687 for (; pos < end;) { 688 switch (f2h(pos[0])) { 689 case FDT_BEGIN_NODE: { 690 const char *c = (char *)&pos[1]; 691 size_t i; 692 693 indent(level); 694 for (i = 0; c[i]; ++i) 695 print_char(c[i]); 696 print(u" {\n\r"); 697 698 ++level; 699 pos = &pos[2 + (i >> 2)]; 700 break; 701 } 702 case FDT_PROP: { 703 struct fdt_property *prop = (struct fdt_property *)pos; 704 const unsigned char *label = &strings[f2h(prop->nameoff)]; 705 u32 len = f2h(prop->len); 706 const unsigned char *str = (unsigned char *)&pos[3]; 707 708 indent(level); 709 for (int i = 0; label[i]; ++i) 710 print_char(label[i]); 711 712 if (len) { 713 print(u" = "); 714 print_property(str, len); 715 } 716 print(u";\r\n"); 717 718 pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)]; 719 break; 720 } 721 case FDT_NOP: 722 ++pos; 723 break; 724 case FDT_END_NODE: 725 if (!level) { 726 error(u"Extraneous end node\r\n"); 727 return EFI_LOAD_ERROR; 728 } 729 730 --level; 731 indent(level); 732 print(u"};\n\r"); 733 ++pos; 734 break; 735 case FDT_END: 736 if (level) { 737 error(u"Missing end node\r\n"); 738 return EFI_LOAD_ERROR; 739 } 740 return EFI_SUCCESS; 741 default: 742 error(u"Invalid device tree token\r\n"); 743 return EFI_LOAD_ERROR; 744 } 745 } 746 error(u"Overrun\r\n"); 747 748 return EFI_LOAD_ERROR; 749} 750 751/** 752 * efi_main() - entry point of the EFI application. 753 * 754 * @handle: handle of the loaded image 755 * @systab: system table 756 * Return: status code 757 */ 758efi_status_t EFIAPI efi_main(efi_handle_t image_handle, 759 struct efi_system_table *systab) 760{ 761 handle = image_handle; 762 systable = systab; 763 cerr = systable->std_err; 764 cout = systable->con_out; 765 cin = systable->con_in; 766 bs = systable->boottime; 767 768 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); 769 cout->clear_screen(cout); 770 cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK); 771 print(u"DTB Dump\r\n========\r\n\r\n"); 772 cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK); 773 774 for (;;) { 775 u16 command[BUFFER_SIZE]; 776 u16 *pos; 777 efi_uintn_t ret; 778 779 print(u"=> "); 780 ret = efi_input(command, sizeof(command)); 781 if (ret == EFI_ABORTED) 782 break; 783 pos = skip_whitespace(command); 784 if (starts_with(pos, u"exit")) 785 break; 786 else if (starts_with(pos, u"dump")) 787 do_dump(); 788 else if (starts_with(pos, u"load ")) 789 do_load(pos + 5); 790 else if (starts_with(pos, u"save ")) 791 do_save(pos + 5); 792 else 793 do_help(); 794 } 795 796 cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); 797 cout->clear_screen(cout); 798 return EFI_SUCCESS; 799}