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