| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
- *
- * initrddump.efi saves the initial RAM disk provided via the
- * EFI_LOAD_FILE2_PROTOCOL.
- *
- * Specifying 'nocolor' as load option data suppresses colored output and
- * clearing of the screen.
- */
- #include <common.h>
- #include <efi_api.h>
- #include <efi_load_initrd.h>
- #define BUFFER_SIZE 64
- #define ESC 0x17
- #define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
- static struct efi_system_table *systable;
- static struct efi_boot_services *bs;
- static struct efi_simple_text_output_protocol *cerr;
- static struct efi_simple_text_output_protocol *cout;
- static struct efi_simple_text_input_protocol *cin;
- static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
- static const efi_guid_t guid_simple_file_system_protocol =
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
- static const efi_guid_t load_file2_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
- static efi_handle_t handle;
- static bool nocolor;
- /*
- * Device path defined by Linux to identify the handle providing the
- * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
- */
- static const struct efi_initrd_dp initrd_dp = {
- .vendor = {
- {
- DEVICE_PATH_TYPE_MEDIA_DEVICE,
- DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
- sizeof(initrd_dp.vendor),
- },
- EFI_INITRD_MEDIA_GUID,
- },
- .end = {
- DEVICE_PATH_TYPE_END,
- DEVICE_PATH_SUB_TYPE_END,
- sizeof(initrd_dp.end),
- }
- };
- /**
- * color() - set foreground color
- *
- * @color: foreground color
- */
- static void color(u8 color)
- {
- if (!nocolor)
- cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK);
- }
- /**
- * print() - print string
- *
- * @string: text
- */
- static void print(u16 *string)
- {
- cout->output_string(cout, string);
- }
- /**
- * cls() - clear screen
- */
- static void cls(void)
- {
- if (nocolor)
- print(u"\r\n");
- else
- cout->clear_screen(cout);
- }
- /**
- * error() - print error string
- *
- * @string: error text
- */
- static void error(u16 *string)
- {
- color(EFI_LIGHTRED);
- print(string);
- color(EFI_LIGHTBLUE);
- }
- /*
- * printx() - print hexadecimal number
- *
- * @val: value to print;
- * @prec: minimum number of digits to print
- */
- static void printx(u64 val, u32 prec)
- {
- int i;
- u16 c;
- u16 buf[16];
- u16 *pos = buf;
- for (i = 2 * sizeof(val) - 1; i >= 0; --i) {
- c = (val >> (4 * i)) & 0x0f;
- if (c || pos != buf || !i || i < prec) {
- c += '0';
- if (c > '9')
- c += 'a' - '9' - 1;
- *pos++ = c;
- }
- }
- *pos = 0;
- print(buf);
- }
- /**
- * efi_drain_input() - drain console input
- */
- static void efi_drain_input(void)
- {
- cin->reset(cin, true);
- }
- /**
- * efi_input_yn() - get answer to yes/no question
- *
- * Return:
- * y or Y
- * EFI_SUCCESS
- * n or N
- * EFI_ACCESS_DENIED
- * ESC
- * EFI_ABORTED
- */
- static efi_status_t efi_input_yn(void)
- {
- struct efi_input_key key = {0};
- efi_uintn_t index;
- efi_status_t ret;
- for (;;) {
- ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
- if (ret != EFI_SUCCESS)
- continue;
- ret = cin->read_key_stroke(cin, &key);
- if (ret != EFI_SUCCESS)
- continue;
- switch (key.scan_code) {
- case 0x17: /* Escape */
- return EFI_ABORTED;
- default:
- break;
- }
- /* Convert to lower case */
- switch (key.unicode_char | 0x20) {
- case 'y':
- return EFI_SUCCESS;
- case 'n':
- return EFI_ACCESS_DENIED;
- default:
- break;
- }
- }
- }
- /**
- * efi_input() - read string from console
- *
- * @buffer: input buffer
- * @buffer_size: buffer size
- * Return: status code
- */
- static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size)
- {
- struct efi_input_key key = {0};
- efi_uintn_t index;
- efi_uintn_t pos = 0;
- u16 outbuf[2] = u" ";
- efi_status_t ret;
- *buffer = 0;
- for (;;) {
- ret = bs->wait_for_event(1, &cin->wait_for_key, &index);
- if (ret != EFI_SUCCESS)
- continue;
- ret = cin->read_key_stroke(cin, &key);
- if (ret != EFI_SUCCESS)
- continue;
- switch (key.scan_code) {
- case 0x17: /* Escape */
- print(u"\r\nAborted\r\n");
- return EFI_ABORTED;
- default:
- break;
- }
- switch (key.unicode_char) {
- case 0x08: /* Backspace */
- if (pos) {
- buffer[pos--] = 0;
- print(u"\b \b");
- }
- break;
- case 0x0a: /* Linefeed */
- case 0x0d: /* Carriage return */
- print(u"\r\n");
- return EFI_SUCCESS;
- default:
- break;
- }
- /* Ignore surrogate codes */
- if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF)
- continue;
- if (key.unicode_char >= 0x20 &&
- pos < buffer_size - 1) {
- *outbuf = key.unicode_char;
- buffer[pos++] = key.unicode_char;
- buffer[pos] = 0;
- print(outbuf);
- }
- }
- }
- /**
- * skip_whitespace() - skip over leading whitespace
- *
- * @pos: UTF-16 string
- * Return: pointer to first non-whitespace
- */
- static u16 *skip_whitespace(u16 *pos)
- {
- for (; *pos && *pos <= 0x20; ++pos)
- ;
- return pos;
- }
- /**
- * starts_with() - check if @string starts with @keyword
- *
- * @string: string to search for keyword
- * @keyword: keyword to be searched
- * Return: true if @string starts with the keyword
- */
- static bool starts_with(u16 *string, u16 *keyword)
- {
- if (!string || !keyword)
- return false;
- for (; *keyword; ++string, ++keyword) {
- if (*string != *keyword)
- return false;
- }
- return true;
- }
- /**
- * do_help() - print help
- */
- static void do_help(void)
- {
- error(u"load - show length and CRC32 of initial RAM disk\r\n");
- error(u"save <initrd> - save initial RAM disk to file\r\n");
- error(u"exit - exit the shell\r\n");
- }
- /**
- * get_initrd() - read initial RAM disk via EFI_LOAD_FILE2_PROTOCOL
- *
- * @initrd: on return buffer with initial RAM disk
- * @initrd_size: size of initial RAM disk
- * Return: status code
- */
- static efi_status_t get_initrd(void **initrd, efi_uintn_t *initrd_size)
- {
- struct efi_device_path *dp = (struct efi_device_path *)&initrd_dp;
- struct efi_load_file_protocol *load_file2_prot;
- u64 buffer;
- efi_handle_t handle;
- efi_status_t ret;
- *initrd = NULL;
- *initrd_size = 0;
- ret = bs->locate_device_path(&load_file2_guid, &dp, &handle);
- if (ret != EFI_SUCCESS) {
- error(u"Load File2 protocol not found\r\n");
- return ret;
- }
- ret = bs->open_protocol(handle, &load_file2_guid,
- (void **)&load_file2_prot, NULL, NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- ret = load_file2_prot->load_file(load_file2_prot, dp, false,
- initrd_size, NULL);
- if (ret != EFI_BUFFER_TOO_SMALL) {
- error(u"Load File2 protocol does not provide file length\r\n");
- return EFI_LOAD_ERROR;
- }
- ret = bs->allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_LOADER_DATA,
- efi_size_in_pages(*initrd_size), &buffer);
- if (ret != EFI_SUCCESS) {
- error(u"Out of memory\r\n");
- return ret;
- }
- *initrd = (void *)(uintptr_t)buffer;
- ret = load_file2_prot->load_file(load_file2_prot, dp, false,
- initrd_size, *initrd);
- if (ret != EFI_SUCCESS) {
- error(u"Load File2 protocol failed to provide file\r\n");
- bs->free_pages(buffer, efi_size_in_pages(*initrd_size));
- return EFI_LOAD_ERROR;
- }
- return ret;
- }
- /**
- * do_load() - load initial RAM disk and display CRC32 and length
- *
- * @filename: file name
- * Return: status code
- */
- static efi_status_t do_load(void)
- {
- void *initrd;
- efi_uintn_t initrd_size;
- u32 crc32;
- efi_uintn_t ret;
- ret = get_initrd(&initrd, &initrd_size);
- if (ret != EFI_SUCCESS)
- return ret;
- print(u"length: 0x");
- printx(initrd_size, 1);
- print(u"\r\n");
- ret = bs->calculate_crc32(initrd, initrd_size, &crc32);
- if (ret != EFI_SUCCESS) {
- error(u"Calculating CRC32 failed\r\n");
- return EFI_LOAD_ERROR;
- }
- print(u"crc32: 0x");
- printx(crc32, 8);
- print(u"\r\n");
- return EFI_SUCCESS;
- }
- /**
- * do_save() - save initial RAM disk
- *
- * @filename: file name
- * Return: status code
- */
- static efi_status_t do_save(u16 *filename)
- {
- struct efi_loaded_image *loaded_image;
- struct efi_simple_file_system_protocol *file_system;
- struct efi_file_handle *root, *file;
- void *initrd;
- efi_uintn_t initrd_size;
- efi_uintn_t ret;
- ret = get_initrd(&initrd, &initrd_size);
- if (ret != EFI_SUCCESS)
- return ret;
- filename = skip_whitespace(filename);
- ret = bs->open_protocol(handle, &loaded_image_guid,
- (void **)&loaded_image, NULL, NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (ret != EFI_SUCCESS) {
- error(u"Loaded image protocol not found\r\n");
- goto out;
- }
- /* Open the simple file system protocol */
- ret = bs->open_protocol(loaded_image->device_handle,
- &guid_simple_file_system_protocol,
- (void **)&file_system, NULL, NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (ret != EFI_SUCCESS) {
- error(u"Failed to open simple file system protocol\r\n");
- goto out;
- }
- /* Open volume */
- ret = file_system->open_volume(file_system, &root);
- if (ret != EFI_SUCCESS) {
- error(u"Failed to open volume\r\n");
- goto out;
- }
- /* Check if file already exists */
- ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0);
- if (ret == EFI_SUCCESS) {
- file->close(file);
- efi_drain_input();
- print(u"Overwrite existing file (y/n)? ");
- ret = efi_input_yn();
- print(u"\r\n");
- if (ret != EFI_SUCCESS) {
- root->close(root);
- error(u"Aborted by user\r\n");
- goto out;
- }
- }
- /* Create file */
- ret = root->open(root, &file, filename,
- EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
- EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE);
- if (ret == EFI_SUCCESS) {
- /* Write file */
- ret = file->write(file, &initrd_size, initrd);
- if (ret != EFI_SUCCESS) {
- error(u"Failed to write file\r\n");
- } else {
- print(filename);
- print(u" written\r\n");
- }
- file->close(file);
- } else {
- error(u"Failed to open file\r\n");
- }
- root->close(root);
- out:
- if (initrd)
- bs->free_pages((uintptr_t)initrd,
- efi_size_in_pages(initrd_size));
- return ret;
- }
- /**
- * get_load_options() - get load options
- *
- * Return: load options or NULL
- */
- static u16 *get_load_options(void)
- {
- efi_status_t ret;
- struct efi_loaded_image *loaded_image;
- ret = bs->open_protocol(handle, &loaded_image_guid,
- (void **)&loaded_image, NULL, NULL,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL);
- if (ret != EFI_SUCCESS) {
- error(u"Loaded image protocol not found\r\n");
- return NULL;
- }
- if (!loaded_image->load_options_size || !loaded_image->load_options)
- return NULL;
- return loaded_image->load_options;
- }
- /**
- * efi_main() - entry point of the EFI application.
- *
- * @handle: handle of the loaded image
- * @systab: system table
- * Return: status code
- */
- efi_status_t EFIAPI efi_main(efi_handle_t image_handle,
- struct efi_system_table *systab)
- {
- u16 *load_options;
- handle = image_handle;
- systable = systab;
- cerr = systable->std_err;
- cout = systable->con_out;
- cin = systable->con_in;
- bs = systable->boottime;
- load_options = get_load_options();
- if (starts_with(load_options, u"nocolor"))
- nocolor = true;
- color(EFI_WHITE);
- cls();
- print(u"INITRD Dump\r\n===========\r\n\r\n");
- color(EFI_LIGHTBLUE);
- for (;;) {
- u16 command[BUFFER_SIZE];
- u16 *pos;
- efi_uintn_t ret;
- efi_drain_input();
- print(u"=> ");
- ret = efi_input(command, sizeof(command));
- if (ret == EFI_ABORTED)
- break;
- pos = skip_whitespace(command);
- if (starts_with(pos, u"exit"))
- break;
- else if (starts_with(pos, u"load"))
- do_load();
- else if (starts_with(pos, u"save "))
- do_save(pos + 5);
- else
- do_help();
- }
- color(EFI_LIGHTGRAY);
- cls();
- return EFI_SUCCESS;
- }
|