uboot/lib/efi_selftest/initrddump.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
   4 *
   5 * initrddump.efi saves the initial RAM disk provided via the
   6 * EFI_LOAD_FILE2_PROTOCOL.
   7 */
   8
   9#include <common.h>
  10#include <efi_api.h>
  11#include <efi_load_initrd.h>
  12
  13#define BUFFER_SIZE 64
  14#define ESC 0x17
  15
  16#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
  17
  18static struct efi_system_table *systable;
  19static struct efi_boot_services *bs;
  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 const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
  24static const efi_guid_t guid_simple_file_system_protocol =
  25                                        EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
  26static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
  27static efi_handle_t handle;
  28
  29/*
  30 * Device path defined by Linux to identify the handle providing the
  31 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
  32 */
  33static const struct efi_initrd_dp initrd_dp = {
  34        .vendor = {
  35                {
  36                   DEVICE_PATH_TYPE_MEDIA_DEVICE,
  37                   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
  38                   sizeof(initrd_dp.vendor),
  39                },
  40                EFI_INITRD_MEDIA_GUID,
  41        },
  42        .end = {
  43                DEVICE_PATH_TYPE_END,
  44                DEVICE_PATH_SUB_TYPE_END,
  45                sizeof(initrd_dp.end),
  46        }
  47};
  48
  49/**
  50 * print() - print string
  51 *
  52 * @string:     text
  53 */
  54static void print(u16 *string)
  55{
  56        cout->output_string(cout, string);
  57}
  58
  59/**
  60 * error() - print error string
  61 *
  62 * @string:     error text
  63 */
  64static void error(u16 *string)
  65{
  66        cout->set_attribute(cout, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
  67        print(string);
  68        cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
  69}
  70
  71/*
  72 * printx() - print hexadecimal number
  73 *
  74 * @val:        value to print;
  75 * @prec:       minimum number of digits to print
  76 */
  77static void printx(u64 val, u32 prec)
  78{
  79        int i;
  80        u16 c;
  81        u16 buf[16];
  82        u16 *pos = buf;
  83
  84        for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
  85                c = (val >> (4 * i)) & 0x0f;
  86                if (c || pos != buf || !i || i < prec) {
  87                        c += '0';
  88                        if (c > '9')
  89                                c += 'a' - '9' - 1;
  90                        *pos++ = c;
  91                }
  92        }
  93        *pos = 0;
  94        print(buf);
  95}
  96
  97/**
  98 * efi_input_yn() - get answer to yes/no question
  99 *
 100 * Return:
 101 * y or Y
 102 *     EFI_SUCCESS
 103 * n or N
 104 *     EFI_ACCESS_DENIED
 105 * ESC
 106 *     EFI_ABORTED
 107 */
 108static efi_status_t efi_input_yn(void)
 109{
 110        struct efi_input_key key = {0};
 111        efi_uintn_t index;
 112        efi_status_t ret;
 113
 114        /* Drain the console input */
 115        ret = cin->reset(cin, true);
 116        for (;;) {
 117                ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
 118                if (ret != EFI_SUCCESS)
 119                        continue;
 120                ret = cin->read_key_stroke(cin, &key);
 121                if (ret != EFI_SUCCESS)
 122                        continue;
 123                switch (key.scan_code) {
 124                case 0x17: /* Escape */
 125                        return EFI_ABORTED;
 126                default:
 127                        break;
 128                }
 129                /* Convert to lower case */
 130                switch (key.unicode_char | 0x20) {
 131                case 'y':
 132                        return EFI_SUCCESS;
 133                case 'n':
 134                        return EFI_ACCESS_DENIED;
 135                default:
 136                        break;
 137                }
 138        }
 139}
 140
 141/**
 142 * efi_input() - read string from console
 143 *
 144 * @buffer:             input buffer
 145 * @buffer_size:        buffer size
 146 * Return:              status code
 147 */
 148static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
 149{
 150        struct efi_input_key key = {0};
 151        efi_uintn_t index;
 152        efi_uintn_t pos = 0;
 153        u16 outbuf[2] = L" ";
 154        efi_status_t ret;
 155
 156        /* Drain the console input */
 157        ret = cin->reset(cin, true);
 158        *buffer = 0;
 159        for (;;) {
 160                ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
 161                if (ret != EFI_SUCCESS)
 162                        continue;
 163                ret = cin->read_key_stroke(cin, &key);
 164                if (ret != EFI_SUCCESS)
 165                        continue;
 166                switch (key.scan_code) {
 167                case 0x17: /* Escape */
 168                        print(L"\r\nAborted\r\n");
 169                        return EFI_ABORTED;
 170                default:
 171                        break;
 172                }
 173                switch (key.unicode_char) {
 174                case 0x08: /* Backspace */
 175                        if (pos) {
 176                                buffer[pos--] = 0;
 177                                print(L"\b \b");
 178                        }
 179                        break;
 180                case 0x0a: /* Linefeed */
 181                case 0x0d: /* Carriage return */
 182                        print(L"\r\n");
 183                        return EFI_SUCCESS;
 184                default:
 185                        break;
 186                }
 187                /* Ignore surrogate codes */
 188                if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
 189                        continue;
 190                if (key.unicode_char >= 0x20 &&
 191                    pos < buffer_size - 1) {
 192                        *outbuf = key.unicode_char;
 193                        buffer[pos++] = key.unicode_char;
 194                        buffer[pos] = 0;
 195                        print(outbuf);
 196                }
 197        }
 198}
 199
 200/**
 201 * skip_whitespace() - skip over leading whitespace
 202 *
 203 * @pos:        UTF-16 string
 204 * Return:      pointer to first non-whitespace
 205 */
 206static u16 *skip_whitespace(u16 *pos)
 207{
 208        for (; *pos && *pos <= 0x20; ++pos)
 209                ;
 210        return pos;
 211}
 212
 213/**
 214 * starts_with() - check if @string starts with @keyword
 215 *
 216 * @string:     string to search for keyword
 217 * @keyword:    keyword to be searched
 218 * Return:      true fi @string starts with the keyword
 219 */
 220static bool starts_with(u16 *string, u16 *keyword)
 221{
 222        for (; *keyword; ++string, ++keyword) {
 223                if (*string != *keyword)
 224                        return false;
 225        }
 226        return true;
 227}
 228
 229/**
 230 * do_help() - print help
 231 */
 232static void do_help(void)
 233{
 234        error(L"load          - show length and CRC32 of initial RAM disk\r\n");
 235        error(L"save <initrd> - save initial RAM disk to file\r\n");
 236        error(L"exit          - exit the shell\r\n");
 237}
 238
 239/**
 240 * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
 241 *
 242 * @initrd:             on return buffer with initial RAM disk
 243 * @initrd_size:        size of initial RAM disk
 244 * Return:              status code
 245 */
 246static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
 247{
 248        struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
 249        struct efi_load_file_protocol *load_file2_prot;
 250        u64 buffer;
 251        efi_handle_t handle;
 252        efi_status_t ret;
 253
 254        *initrd = NULL;
 255        *initrd_size = 0;
 256        ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
 257        if (ret != EFI_SUCCESS) {
 258                error(L"Load File2 protocol not found\r\n");
 259                return ret;
 260        }
 261        ret = bs->handle_protocol(handle, &load_file2_guid,
 262                                 (void **)&load_file2_prot);
 263        ret = load_file2_prot->load_file(load_file2_prot, dp, false,
 264                                         initrd_size, NULL);
 265        if (ret != EFI_BUFFER_TOO_SMALL) {
 266                error(L"Load File2 protocol does not provide file length\r\n");
 267                return EFI_LOAD_ERROR;
 268        }
 269        ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
 270                                 efi_size_in_pages(*initrd_size), &buffer);
 271        if (ret != EFI_SUCCESS) {
 272                error(L"Out of memory\r\n");
 273                return ret;
 274        }
 275        *initrd = (void *)(uintptr_t)buffer;
 276        ret = load_file2_prot->load_file(load_file2_prot, dp, false,
 277                                         initrd_size, *initrd);
 278        if (ret != EFI_SUCCESS) {
 279                error(L"Load File2 protocol failed to provide file\r\n");
 280                bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
 281                return EFI_LOAD_ERROR;
 282        }
 283        return ret;
 284}
 285
 286/**
 287 * do_load() - load initial RAM disk and display CRC32 and length
 288 *
 289 * @filename:   file name
 290 * Return:      status code
 291 */
 292static efi_status_t do_load(void)
 293{
 294        void *initrd;
 295        efi_uintn_t initrd_size;
 296        u32 crc32;
 297        efi_uintn_t ret;
 298
 299        ret =  get_initrd(&initrd, &initrd_size);
 300        if (ret != EFI_SUCCESS)
 301                return ret;
 302        print(L"length: 0x");
 303        printx(initrd_size, 1);
 304        print(L"\r\n");
 305
 306        ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
 307        if (ret != EFI_SUCCESS) {
 308                error(L"Calculating CRC32 failed\r\n");
 309                return EFI_LOAD_ERROR;
 310        }
 311        print(L"crc32: 0x");
 312        printx(crc32, 8);
 313        print(L"\r\n");
 314
 315        return EFI_SUCCESS;
 316}
 317
 318/**
 319 * do_save() - save initial RAM disk
 320 *
 321 * @filename:   file name
 322 * Return:      status code
 323 */
 324static efi_status_t do_save(u16 *filename)
 325{
 326        struct efi_loaded_image *loaded_image;
 327        struct efi_simple_file_system_protocol *file_system;
 328        struct efi_file_handle *root, *file;
 329        void *initrd;
 330        efi_uintn_t initrd_size;
 331        efi_uintn_t ret;
 332
 333        ret = get_initrd(&initrd, &initrd_size);
 334        if (ret != EFI_SUCCESS)
 335                return ret;
 336
 337        filename = skip_whitespace(filename);
 338
 339        ret = bs->open_protocol(handle, &loaded_image_guid,
 340                                (void **)&loaded_image, NULL, NULL,
 341                                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 342        if (ret != EFI_SUCCESS) {
 343                error(L"Loaded image protocol not found\r\n");
 344                goto out;
 345        }
 346
 347        /* Open the simple file system protocol */
 348        ret = bs->open_protocol(loaded_image->device_handle,
 349                                &guid_simple_file_system_protocol,
 350                                (void **)&file_system, NULL, NULL,
 351                                EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 352        if (ret != EFI_SUCCESS) {
 353                error(L"Failed to open simple file system protocol\r\n");
 354                goto out;
 355        }
 356
 357        /* Open volume */
 358        ret = file_system->open_volume(file_system, &root);
 359        if (ret != EFI_SUCCESS) {
 360                error(L"Failed to open volume\r\n");
 361                goto out;
 362        }
 363        /* Check if file already exists */
 364        ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
 365        if (ret == EFI_SUCCESS) {
 366                file->close(file);
 367                print(L"Overwrite existing file (y/n)? ");
 368                ret = efi_input_yn();
 369                print(L"\r\n");
 370                if (ret != EFI_SUCCESS) {
 371                        root->close(root);
 372                        error(L"Aborted by user\r\n");
 373                        goto out;
 374                }
 375        }
 376
 377        /* Create file */
 378        ret = root->open(root, &file, filename,
 379                         EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
 380                         EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
 381        if (ret == EFI_SUCCESS) {
 382                /* Write file */
 383                ret = file->write(file, &initrd_size, initrd);
 384                if (ret != EFI_SUCCESS) {
 385                        error(L"Failed to write file\r\n");
 386                } else {
 387                        print(filename);
 388                        print(L" written\r\n");
 389                }
 390                file->close(file);
 391        } else {
 392                error(L"Failed to open file\r\n");
 393        }
 394        root->close(root);
 395
 396out:
 397        if (initrd)
 398                bs->free_pages((uintptr_t)initrd,
 399                               efi_size_in_pages(initrd_size));
 400        return ret;
 401}
 402
 403/**
 404 * efi_main() - entry point of the EFI application.
 405 *
 406 * @handle:     handle of the loaded image
 407 * @systab:     system table
 408 * @return:     status code
 409 */
 410efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
 411                             struct efi_system_table *systab)
 412{
 413        handle = image_handle;
 414        systable = systab;
 415        cerr = systable->std_err;
 416        cout = systable->con_out;
 417        cin = systable->con_in;
 418        bs = systable->boottime;
 419
 420        cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
 421        cout->clear_screen(cout);
 422        cout->set_attribute(cout, EFI_WHITE | EFI_BACKGROUND_BLACK);
 423        print(L"INITRD Dump\r\n========\r\n\r\n");
 424        cout->set_attribute(cout, EFI_LIGHTBLUE | EFI_BACKGROUND_BLACK);
 425
 426        for (;;) {
 427                u16 command[BUFFER_SIZE];
 428                u16 *pos;
 429                efi_uintn_t ret;
 430
 431                print(L"=> ");
 432                ret = efi_input(command, sizeof(command));
 433                if (ret == EFI_ABORTED)
 434                        break;
 435                pos = skip_whitespace(command);
 436                if (starts_with(pos, L"exit"))
 437                        break;
 438                else if (starts_with(pos, L"load"))
 439                        do_load();
 440                else if (starts_with(pos, L"save "))
 441                        do_save(pos + 5);
 442                else
 443                        do_help();
 444        }
 445
 446        cout->set_attribute(cout, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
 447        cout->clear_screen(cout);
 448        return EFI_SUCCESS;
 449}
 450