diff options
Diffstat (limited to 'lib/efi_loader')
-rw-r--r-- | lib/efi_loader/Kconfig | 12 | ||||
-rw-r--r-- | lib/efi_loader/Makefile | 16 | ||||
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 2 | ||||
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 177 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 650 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 4 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path_to_text.c | 10 | ||||
-rw-r--r-- | lib/efi_loader/efi_file.c | 88 | ||||
-rw-r--r-- | lib/efi_loader/efi_image_loader.c | 23 | ||||
-rw-r--r-- | lib/efi_loader/efi_memory.c | 47 | ||||
-rw-r--r-- | lib/efi_loader/efi_root_node.c | 79 | ||||
-rw-r--r-- | lib/efi_loader/efi_runtime.c | 17 | ||||
-rw-r--r-- | lib/efi_loader/efi_unicode_collation.c | 329 | ||||
-rw-r--r-- | lib/efi_loader/efi_variable.c | 52 |
14 files changed, 1242 insertions, 264 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index ce6a09f0b4..b921ea8821 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -1,6 +1,6 @@ config EFI_LOADER bool "Support running EFI Applications in U-Boot" - depends on (ARM || X86 || RISCV) && OF_LIBFDT + depends on (ARM || X86 || RISCV || SANDBOX) && OF_LIBFDT # We need EFI_STUB_64BIT to be set on x86_64 with EFI_STUB depends on !EFI_STUB || !X86_64 || EFI_STUB_64BIT # We need EFI_STUB_32BIT to be set on x86_32 with EFI_STUB @@ -15,6 +15,16 @@ config EFI_LOADER interfaces to a loaded EFI application, enabling it to reuse U-Boot's device drivers. +config EFI_UNICODE_CAPITALIZATION + bool "Support Unicode capitalization" + depends on EFI_LOADER + default y + help + Select this option to enable correct handling of the capitalization of + Unicode codepoints in the range 0x0000-0xffff. If this option is not + set, only the the correct handling of the letters of the codepage + used by the FAT file system is ensured. + config EFI_LOADER_BOUNCE_BUFFER bool "EFI Applications use bounce buffers for DMA operations" depends on EFI_LOADER && ARM64 diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 1ffbf52a89..6703435947 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -17,9 +17,19 @@ always += helloworld.efi endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o -obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o -obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o -obj-y += efi_device_path_utilities.o efi_file.o efi_variable.o efi_bootmgr.o +obj-y += efi_bootmgr.o +obj-y += efi_boottime.o +obj-y += efi_console.o +obj-y += efi_device_path.o +obj-y += efi_device_path_to_text.o +obj-y += efi_device_path_utilities.o +obj-y += efi_file.o +obj-y += efi_image_loader.o +obj-y += efi_memory.o +obj-y += efi_root_node.o +obj-y += efi_runtime.o +obj-y += efi_unicode_collation.o +obj-y += efi_variable.o obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 853358ab93..0c5764db12 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -60,7 +60,7 @@ static void parse_load_option(struct load_option *lo, void *ptr) ptr += sizeof(u16); lo->label = ptr; - ptr += (utf16_strlen(lo->label) + 1) * 2; + ptr += (u16_strlen(lo->label) + 1) * 2; lo->file_path = ptr; ptr += lo->file_path_length; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index ca61e1ad41..97eb19cd14 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -26,14 +26,6 @@ LIST_HEAD(efi_obj_list); /* List of all events */ LIST_HEAD(efi_events); -/* - * If we're running on nasty systems (32bit ARM booting into non-EFI Linux) - * we need to do trickery with caches. Since we don't want to break the EFI - * aware boot path, only apply hacks when loading exiting directly (breaking - * direct Linux EFI booting along the way - oh well). - */ -static bool efi_is_direct_boot = true; - #ifdef CONFIG_ARM /* * The "gd" pointer lives in a register on ARM and AArch64 that we declare @@ -105,8 +97,8 @@ void efi_save_gd(void) /* * Special case handler for error/abort that just forces things back to u-boot - * world so we can dump out an abort msg, without any care about returning back - * to UEFI world. + * world so we can dump out an abort message, without any care about returning + * back to UEFI world. */ void efi_restore_gd(void) { @@ -183,7 +175,7 @@ static void efi_queue_event(struct efi_event *event, bool check_tpl) * is_valid_tpl() - check if the task priority level is valid * * @tpl: TPL level to check - * ReturnValue: status code + * Return: status code */ efi_status_t is_valid_tpl(efi_uintn_t tpl) { @@ -626,7 +618,7 @@ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, evt->notify_function = notify_function; evt->notify_context = notify_context; evt->group = group; - /* Disable timers on bootup */ + /* Disable timers on boot up */ evt->trigger_next = -1ULL; evt->is_queued = false; evt->is_signaled = false; @@ -732,7 +724,7 @@ void efi_timer_check(void) * efi_set_timer() - set the trigger time for a timer event or stop the event * @event: event for which the timer is set * @type: type of the timer - * @trigger_time: trigger period in multiples of 100ns + * @trigger_time: trigger period in multiples of 100 ns * * This is the function for internal usage in U-Boot. For the API function * implementing the SetTimer service see efi_set_timer_ext. @@ -747,8 +739,8 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, return EFI_INVALID_PARAMETER; /* - * The parameter defines a multiple of 100ns. - * We use multiples of 1000ns. So divide by 10. + * The parameter defines a multiple of 100 ns. + * We use multiples of 1000 ns. So divide by 10. */ do_div(trigger_time, 10); @@ -774,7 +766,7 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, * event * @event: event for which the timer is set * @type: type of the timer - * @trigger_time: trigger period in multiples of 100ns + * @trigger_time: trigger period in multiples of 100 ns * * This function implements the SetTimer service. * @@ -1061,7 +1053,7 @@ out: /** * efi_get_drivers() - get all drivers associated to a controller * @efiobj: handle of the controller - * @protocol: protocol guid (optional) + * @protocol: protocol GUID (optional) * @number_of_drivers: number of child controllers * @driver_handle_buffer: handles of the the drivers * @@ -1126,7 +1118,7 @@ static efi_status_t efi_get_drivers(struct efi_object *efiobj, /** * efi_disconnect_all_drivers() - disconnect all drivers from a controller * @efiobj: handle of the controller - * @protocol: protocol guid (optional) + * @protocol: protocol GUID (optional) * @child_handle: handle of the child to destroy * * This function implements the DisconnectController service. @@ -1408,7 +1400,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, if (!guid) return EFI_INVALID_PARAMETER; - /* Check for guid override */ + /* Check for GUID override */ for (i = 0; i < systab.nr_tables; i++) { if (!guidcmp(guid, &systab.tables[i].guid)) { if (table) @@ -1432,7 +1424,7 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, systab.nr_tables = i + 1; out: - /* systab.nr_tables may have changed. So we need to update the crc32 */ + /* systab.nr_tables may have changed. So we need to update the CRC32 */ efi_update_table_header_crc32(&systab.hdr); /* Notify that the configuration table was changed */ @@ -1478,20 +1470,35 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, * * Return: status code */ -efi_status_t efi_setup_loaded_image( - struct efi_loaded_image *info, struct efi_object *obj, - struct efi_device_path *device_path, - struct efi_device_path *file_path) +efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path, + struct efi_device_path *file_path, + struct efi_loaded_image_obj **handle_ptr, + struct efi_loaded_image **info_ptr) { efi_status_t ret; + struct efi_loaded_image *info; + struct efi_loaded_image_obj *obj; + + info = calloc(1, sizeof(*info)); + if (!info) + return EFI_OUT_OF_RESOURCES; + obj = calloc(1, sizeof(*obj)); + if (!obj) { + free(info); + return EFI_OUT_OF_RESOURCES; + } /* Add internal object to object list */ - efi_add_handle(obj); - /* efi_exit() assumes that the handle points to the info */ - obj->handle = info; + efi_add_handle(&obj->parent); + + if (info_ptr) + *info_ptr = info; + if (handle_ptr) + *handle_ptr = obj; info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; info->file_path = file_path; + info->system_table = &systab; if (device_path) { info->device_handle = efi_dp_find_obj(device_path, NULL); @@ -1499,8 +1506,8 @@ efi_status_t efi_setup_loaded_image( * When asking for the device path interface, return * bootefi_device_path */ - ret = efi_add_protocol(obj->handle, &efi_guid_device_path, - device_path); + ret = efi_add_protocol(obj->parent.handle, + &efi_guid_device_path, device_path); if (ret != EFI_SUCCESS) goto failure; } @@ -1509,19 +1516,8 @@ efi_status_t efi_setup_loaded_image( * When asking for the loaded_image interface, just * return handle which points to loaded_image_info */ - ret = efi_add_protocol(obj->handle, &efi_guid_loaded_image, info); - if (ret != EFI_SUCCESS) - goto failure; - - ret = efi_add_protocol(obj->handle, - &efi_guid_device_path_to_text_protocol, - (void *)&efi_device_path_to_text); - if (ret != EFI_SUCCESS) - goto failure; - - ret = efi_add_protocol(obj->handle, - &efi_guid_device_path_utilities_protocol, - (void *)&efi_device_path_utilities); + ret = efi_add_protocol(obj->parent.handle, + &efi_guid_loaded_image, info); if (ret != EFI_SUCCESS) goto failure; @@ -1604,7 +1600,8 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_handle_t *image_handle) { struct efi_loaded_image *info; - struct efi_object *obj; + struct efi_loaded_image_obj **image_obj = + (struct efi_loaded_image_obj **)image_handle; efi_status_t ret; EFI_ENTRY("%d, %p, %pD, %p, %zd, %p", boot_policy, parent_image, @@ -1620,18 +1617,6 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, goto error; } - info = calloc(1, sizeof(*info)); - if (!info) { - ret = EFI_OUT_OF_RESOURCES; - goto error; - } - obj = calloc(1, sizeof(*obj)); - if (!obj) { - free(info); - ret = EFI_OUT_OF_RESOURCES; - goto error; - } - if (!source_buffer) { struct efi_device_path *dp, *fp; @@ -1643,35 +1628,35 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, * file parts: */ efi_dp_split_file_path(file_path, &dp, &fp); - ret = efi_setup_loaded_image(info, obj, dp, fp); + ret = efi_setup_loaded_image(dp, fp, image_obj, &info); if (ret != EFI_SUCCESS) goto failure; } else { - /* In this case, file_path is the "device" path, ie. + /* In this case, file_path is the "device" path, i.e. * something like a HARDWARE_DEVICE:MEMORY_MAPPED */ - ret = efi_setup_loaded_image(info, obj, file_path, NULL); + ret = efi_setup_loaded_image(file_path, NULL, image_obj, &info); if (ret != EFI_SUCCESS) - goto failure; + goto error; } - info->reserved = efi_load_pe(source_buffer, info); - if (!info->reserved) { + (*image_obj)->entry = efi_load_pe(*image_obj, source_buffer, info); + if (!(*image_obj)->entry) { ret = EFI_UNSUPPORTED; goto failure; } info->system_table = &systab; info->parent_handle = parent_image; - *image_handle = obj->handle; return EFI_EXIT(EFI_SUCCESS); failure: + efi_delete_handle(*image_handle); + *image_handle = NULL; free(info); - efi_delete_handle(obj); error: return EFI_EXIT(ret); } /** - * efi_start_image() - dall the entry point of an image + * efi_start_image() - call the entry point of an image * @image_handle: handle of the image * @exit_data_size: size of the buffer * @exit_data: buffer to receive the exit data of the called image @@ -1687,18 +1672,14 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) { - EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, - struct efi_system_table *st); - struct efi_loaded_image *info = image_handle; + struct efi_loaded_image_obj *image_obj = + (struct efi_loaded_image_obj *)image_handle; efi_status_t ret; EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); - entry = info->reserved; - - efi_is_direct_boot = false; /* call the image! */ - if (setjmp(&info->exit_jmp)) { + if (setjmp(&image_obj->exit_jmp)) { /* * We called the entry point of the child image with EFI_CALL * in the lines below. The child image called the Exit() boot @@ -1721,16 +1702,16 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, assert(__efi_entry_check()); debug("%sEFI: %lu returned by started image\n", __efi_nesting_dec(), - (unsigned long)((uintptr_t)info->exit_status & + (unsigned long)((uintptr_t)image_obj->exit_status & ~EFI_ERROR_MASK)); - return EFI_EXIT(info->exit_status); + return EFI_EXIT(image_obj->exit_status); } - ret = EFI_CALL(entry(image_handle, &systab)); + ret = EFI_CALL(image_obj->entry(image_handle, &systab)); /* * Usually UEFI applications call Exit() instead of returning. - * But because the world doesn not consist of ponies and unicorns, + * But because the world doesn't consist of ponies and unicorns, * we're happy to emulate that behavior on behalf of a payload * that forgot. */ @@ -1757,17 +1738,11 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, int16_t *exit_data) { /* - * We require that the handle points to the original loaded - * image protocol interface. - * - * For getting the longjmp address this is safer than locating - * the protocol because the protocol may have been reinstalled - * pointing to another memory location. - * * TODO: We should call the unload procedure of the loaded * image protocol. */ - struct efi_loaded_image *loaded_image_info = (void *)image_handle; + struct efi_loaded_image_obj *image_obj = + (struct efi_loaded_image_obj *)image_handle; EFI_ENTRY("%p, %ld, %ld, %p", image_handle, exit_status, exit_data_size, exit_data); @@ -1781,8 +1756,8 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, */ efi_restore_gd(); - loaded_image_info->exit_status = exit_status; - longjmp(&loaded_image_info->exit_jmp, 1); + image_obj->exit_status = exit_status; + longjmp(&image_obj->exit_jmp, 1); panic("EFI application exited"); } @@ -1811,21 +1786,6 @@ static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) } /** - * efi_exit_caches() - fix up caches for EFI payloads if necessary - */ -static void efi_exit_caches(void) -{ -#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) - /* - * Grub on 32bit ARM needs to have caches disabled before jumping into - * a zImage, but does not know of all cache layers. Give it a hand. - */ - if (efi_is_direct_boot) - cleanup_before_linux(); -#endif -} - -/** * efi_exit_boot_services() - stop all boot services * @image_handle: handle of the loaded image * @map_key: key of the memory map @@ -1874,17 +1834,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, } } - /* TODO Should persist EFI variables here */ + /* TODO: Should persist EFI variables here */ board_quiesce_devices(); - /* Fix up caches for EFI payloads if necessary */ - efi_exit_caches(); - /* This stops all lingering devices */ bootm_disable_interrupts(); - /* Disable boottime services */ + /* Disable boot time services */ systab.con_in_handle = NULL; systab.con_in = NULL; systab.con_out_handle = NULL; @@ -2118,7 +2075,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle( ++*protocol_buffer_count; } - /* Copy guids */ + /* Copy GUIDs */ if (*protocol_buffer_count) { size_t j = 0; @@ -2709,7 +2666,7 @@ static efi_status_t efi_bind_controller( * efi_connect_single_controller() - connect a single driver to a controller * @controller_handle: controller * @driver_image_handle: driver - * @remain_device_path: remainting path + * @remain_device_path: remaining path * * Return: status code */ @@ -2790,7 +2747,7 @@ static efi_status_t efi_connect_single_controller( * details. * * First all driver binding protocol handles are tried for binding drivers. - * Afterwards all handles that have openened a protocol of the controller + * Afterwards all handles that have opened a protocol of the controller * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers. * * Return: status code @@ -3123,7 +3080,7 @@ struct efi_system_table __efi_runtime_data systab = { /** * efi_initialize_system_table() - Initialize system table * - * Return Value: status code + * Return: status code */ efi_status_t efi_initialize_system_table(void) { @@ -3135,7 +3092,7 @@ efi_status_t efi_initialize_system_table(void) sizeof(struct efi_configuration_table), (void **)&systab.tables); - /* Set crc32 field in table headers */ + /* Set CRC32 field in table headers */ efi_update_table_header_crc32(&systab.hdr); efi_update_table_header_crc32(&efi_runtime_services.hdr); efi_update_table_header_crc32(&efi_boot_services.hdr); diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index b487288785..7ecdbb1666 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -42,10 +42,12 @@ static struct cout_mode efi_cout_modes[] = { }, }; -const efi_guid_t efi_guid_text_output_protocol = - EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_input_ex_protocol = + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; const efi_guid_t efi_guid_text_input_protocol = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; +const efi_guid_t efi_guid_text_output_protocol = + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; #define cESC '\x1b' #define ESC "\x1b" @@ -111,23 +113,28 @@ static efi_status_t EFIAPI efi_cout_output_string( { struct simple_text_output_mode *con = &efi_con_mode; struct cout_mode *mode = &efi_cout_modes[con->mode]; - - EFI_ENTRY("%p, %p", this, string); - - unsigned int n16 = utf16_strlen(string); - char buf[MAX_UTF8_PER_UTF16 * n16 + 1]; + char *buf, *pos; u16 *p; + efi_status_t ret = EFI_SUCCESS; - *utf16_to_utf8((u8 *)buf, string, n16) = '\0'; + EFI_ENTRY("%p, %p", this, string); + buf = malloc(utf16_utf8_strlen(string) + 1); + if (!buf) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + pos = buf; + utf16_utf8_strcpy(&pos, string); fputs(stdout, buf); + free(buf); /* * Update the cursor position. * * The UEFI spec provides advance rules for U+0000, U+0008, U+000A, * and U000D. All other characters, including control characters - * U+0007 (bel) and U+0009 (tab), have to increase the column by one. + * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one. */ for (p = string; *p; ++p) { switch (*p) { @@ -158,7 +165,8 @@ static efi_status_t EFIAPI efi_cout_output_string( con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1); } - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } static efi_status_t EFIAPI efi_cout_test_string( @@ -177,32 +185,56 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) return (mode->rows == rows) && (mode->columns == cols); } +/** + * query_console_serial() - query console size + * + * @rows pointer to return number of rows + * @columns pointer to return number of columns + * Returns 0 on success + */ static int query_console_serial(int *rows, int *cols) { - /* Ask the terminal about its size */ - int n[3]; + int ret = 0; + int n[2]; u64 timeout; /* Empty input buffer */ while (tstc()) getc(); - printf(ESC"[18t"); + /* + * Not all terminals understand CSI [18t for querying the console size. + * We should adhere to escape sequences documented in the console_codes + * manpage and the ECMA-48 standard. + * + * So here we follow a different approach. We position the cursor to the + * bottom right and query its position. Before leaving the function we + * restore the original cursor position. + */ + printf(ESC "7" /* Save cursor position */ + ESC "[r" /* Set scrolling region to full window */ + ESC "[999;999H" /* Move to bottom right corner */ + ESC "[6n"); /* Query cursor position */ - /* Check if we have a terminal that understands */ + /* Allow up to one second for a response */ timeout = timer_get_us() + 1000000; while (!tstc()) - if (timer_get_us() > timeout) - return -1; - - /* Read {depth,rows,cols} */ - if (term_read_reply(n, 3, 't')) - return -1; + if (timer_get_us() > timeout) { + ret = -1; + goto out; + } - *cols = n[2]; - *rows = n[1]; + /* Read {rows,cols} */ + if (term_read_reply(n, 2, 'R')) { + ret = 1; + goto out; + } - return 0; + *cols = n[1]; + *rows = n[0]; +out: + printf(ESC "8"); /* Restore cursor position */ + return ret; } /* @@ -298,8 +330,8 @@ static const struct { { 36, 46 }, /* 3: cyan */ { 31, 41 }, /* 4: red */ { 35, 45 }, /* 5: magenta */ - { 33, 43 }, /* 6: brown, map to yellow as edk2 does*/ - { 37, 47 }, /* 7: light grey, map to white */ + { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/ + { 37, 47 }, /* 7: light gray, map to white */ }; /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */ @@ -351,13 +383,31 @@ static efi_status_t EFIAPI efi_cout_set_cursor_position( struct efi_simple_text_output_protocol *this, unsigned long column, unsigned long row) { + efi_status_t ret = EFI_SUCCESS; + struct simple_text_output_mode *con = &efi_con_mode; + struct cout_mode *mode = &efi_cout_modes[con->mode]; + EFI_ENTRY("%p, %ld, %ld", this, column, row); - printf(ESC"[%d;%df", (int)row, (int)column); + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (row >= mode->rows || column >= mode->columns) { + ret = EFI_UNSUPPORTED; + goto out; + } + + /* + * Set cursor position by sending CSI H. + * EFI origin is [0, 0], terminal origin is [1, 1]. + */ + printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1); efi_con_mode.cursor_column = column; efi_con_mode.cursor_row = row; - - return EFI_EXIT(EFI_SUCCESS); +out: + return EFI_EXIT(ret); } static efi_status_t EFIAPI efi_cout_enable_cursor( @@ -384,29 +434,58 @@ struct efi_simple_text_output_protocol efi_con_out = { .mode = (void*)&efi_con_mode, }; -static efi_status_t EFIAPI efi_cin_reset( - struct efi_simple_input_interface *this, - bool extended_verification) -{ - EFI_ENTRY("%p, %d", this, extended_verification); +/** + * struct efi_cin_notify_function - registered console input notify function + * + * @link: link to list + * @data: key to notify + * @function: function to call + */ +struct efi_cin_notify_function { + struct list_head link; + struct efi_key_data key; + efi_status_t (EFIAPI *function) + (struct efi_key_data *key_data); +}; - /* Empty input buffer */ - while (tstc()) - getc(); +static bool key_available; +static struct efi_key_data next_key; +static LIST_HEAD(cin_notify_functions); - return EFI_EXIT(EFI_SUCCESS); +/** + * set_shift_mask() - set shift mask + * + * @mod: Xterm shift mask + */ +void set_shift_mask(int mod, struct efi_key_state *key_state) +{ + key_state->key_shift_state = EFI_SHIFT_STATE_VALID; + if (mod) { + --mod; + if (mod & 1) + key_state->key_shift_state |= EFI_LEFT_SHIFT_PRESSED; + if (mod & 2) + key_state->key_shift_state |= EFI_LEFT_ALT_PRESSED; + if (mod & 4) + key_state->key_shift_state |= EFI_LEFT_CONTROL_PRESSED; + if (mod & 8) + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } else { + key_state->key_shift_state |= EFI_LEFT_LOGO_PRESSED; + } } -/* - * Analyze modifiers (shift, alt, ctrl) for function keys. +/** + * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys + * * This gets called when we have already parsed CSI. * * @modifiers: bitmask (shift, alt, ctrl) * @return: the unmodified code */ -static char skip_modifiers(int *modifiers) +static int analyze_modifiers(struct efi_key_state *key_state) { - char c, mod = 0, ret = 0; + int c, mod = 0, ret = 0; c = getc(); @@ -430,37 +509,38 @@ static char skip_modifiers(int *modifiers) } } out: - if (mod) - --mod; - if (modifiers) - *modifiers = mod; + set_shift_mask(mod, key_state); if (!ret) ret = c; return ret; } -static efi_status_t EFIAPI efi_cin_read_key_stroke( - struct efi_simple_input_interface *this, - struct efi_input_key *key) +/** + * efi_cin_read_key() - read a key from the console input + * + * @key: - key received + * Return: - status code + */ +static efi_status_t efi_cin_read_key(struct efi_key_data *key) { struct efi_input_key pressed_key = { .scan_code = 0, .unicode_char = 0, }; - char ch; + s32 ch; - EFI_ENTRY("%p, %p", this, key); + if (console_read_unicode(&ch)) + return EFI_NOT_READY; - /* We don't do interrupts, so check for timers cooperatively */ - efi_timer_check(); + key->key_state.key_shift_state = EFI_SHIFT_STATE_INVALID; + key->key_state.key_toggle_state = EFI_TOGGLE_STATE_INVALID; - if (!tstc()) { - /* No key pressed */ - return EFI_EXIT(EFI_NOT_READY); - } + /* We do not support multi-word codes */ + if (ch >= 0x10000) + ch = '?'; - ch = getc(); - if (ch == cESC) { + switch (ch) { + case 0x1b: /* * Xterm Control Sequences * https://www.xfree86.org/4.8.0/ctlseqs.html @@ -472,14 +552,13 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( break; case 'O': /* F1 - F4 */ ch = getc(); - /* skip modifiers */ - if (ch <= '9') + /* consider modifiers */ + if (ch < 'P') { + set_shift_mask(ch - '0', &key->key_state); ch = getc(); + } pressed_key.scan_code = ch - 'P' + 11; break; - case 'a'...'z': - ch = ch - 'a'; - break; case '[': ch = getc(); switch (ch) { @@ -493,7 +572,7 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( pressed_key.scan_code = 5; break; case '1': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '1'...'5': /* F1 - F5 */ pressed_key.scan_code = ch - '1' + 11; @@ -513,7 +592,7 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( } break; case '2': - ch = skip_modifiers(NULL); + ch = analyze_modifiers(&key->key_state); switch (ch) { case '0'...'1': /* F9 - F10 */ pressed_key.scan_code = ch - '0' + 19; @@ -528,31 +607,406 @@ static efi_status_t EFIAPI efi_cin_read_key_stroke( break; case '3': /* DEL */ pressed_key.scan_code = 8; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '5': /* PG UP */ pressed_key.scan_code = 9; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; case '6': /* PG DOWN */ pressed_key.scan_code = 10; - skip_modifiers(NULL); + analyze_modifiers(&key->key_state); break; - } + } /* [ */ break; + default: + /* ALT key */ + set_shift_mask(3, &key->key_state); } - } else if (ch == 0x7f) { + break; + case 0x7f: /* Backspace */ ch = 0x08; } - if (!pressed_key.scan_code) + if (pressed_key.scan_code) { + key->key_state.key_shift_state |= EFI_SHIFT_STATE_VALID; + } else { pressed_key.unicode_char = ch; - *key = pressed_key; - return EFI_EXIT(EFI_SUCCESS); + /* + * Assume left control key for control characters typically + * entered using the control key. + */ + if (ch >= 0x01 && ch <= 0x1f) { + key->key_state.key_shift_state |= + EFI_SHIFT_STATE_VALID; + switch (ch) { + case 0x01 ... 0x07: + case 0x0b ... 0x0c: + case 0x0e ... 0x1f: + key->key_state.key_shift_state |= + EFI_LEFT_CONTROL_PRESSED; + } + } + } + key->key = pressed_key; + + return EFI_SUCCESS; +} + +/** + * efi_cin_notify() - notify registered functions + */ +static void efi_cin_notify(void) +{ + struct efi_cin_notify_function *item; + + list_for_each_entry(item, &cin_notify_functions, link) { + bool match = true; + + /* We do not support toggle states */ + if (item->key.key.unicode_char || item->key.key.scan_code) { + if (item->key.key.unicode_char != + next_key.key.unicode_char || + item->key.key.scan_code != next_key.key.scan_code) + match = false; + } + if (item->key.key_state.key_shift_state && + item->key.key_state.key_shift_state != + next_key.key_state.key_shift_state) + match = false; + + if (match) + /* We don't bother about the return code */ + EFI_CALL(item->function(&next_key)); + } +} + +/** + * efi_cin_check() - check if keyboard input is available + */ +static void efi_cin_check(void) +{ + efi_status_t ret; + + if (key_available) { + efi_signal_event(efi_con_in.wait_for_key, true); + return; + } + + if (tstc()) { + ret = efi_cin_read_key(&next_key); + if (ret == EFI_SUCCESS) { + key_available = true; + + /* Notify registered functions */ + efi_cin_notify(); + + /* Queue the wait for key event */ + if (key_available) + efi_signal_event(efi_con_in.wait_for_key, true); + } + } +} + +/** + * efi_cin_empty_buffer() - empty input buffer + */ +static void efi_cin_empty_buffer(void) +{ + while (tstc()) + getc(); + key_available = false; +} + +/** + * efi_cin_reset_ex() - reset console input + * + * @this: - the extended simple text input protocol + * @extended_verification: - extended verification + * + * This function implements the reset service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: old value of the task priority level + */ +static efi_status_t EFIAPI efi_cin_reset_ex( + struct efi_simple_text_input_ex_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + efi_cin_empty_buffer(); +out: + return EFI_EXIT(ret); } -struct efi_simple_input_interface efi_con_in = { +/** + * efi_cin_read_key_stroke_ex() - read key stroke + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key read from console + * Return: status code + * + * This function implements the ReadKeyStrokeEx service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke_ex( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key_data); + + /* Check parameters */ + if (!this || !key_data) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key_data = next_key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_set_state() - set toggle key state + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_toggle_state: key toggle state + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_set_state( + struct efi_simple_text_input_ex_protocol *this, + u8 key_toggle_state) +{ + EFI_ENTRY("%p, %u", this, key_toggle_state); + /* + * U-Boot supports multiple console input sources like serial and + * net console for which a key toggle state cannot be set at all. + * + * According to the UEFI specification it is allowable to not implement + * this service. + */ + return EFI_EXIT(EFI_UNSUPPORTED); +} + +/** + * efi_cin_register_key_notify() - register key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key_data: key to be notified + * @key_notify_function: function to be called if the key is pressed + * @notify_handle: handle for unregistering the notification + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_register_key_notify( + struct efi_simple_text_input_ex_protocol *this, + struct efi_key_data *key_data, + efi_status_t (EFIAPI *key_notify_function)( + struct efi_key_data *key_data), + void **notify_handle) +{ + efi_status_t ret = EFI_SUCCESS; + struct efi_cin_notify_function *notify_function; + + EFI_ENTRY("%p, %p, %p, %p", + this, key_data, key_notify_function, notify_handle); + + /* Check parameters */ + if (!this || !key_data || !key_notify_function || !notify_handle) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n", + key_data->key.unicode_char, + key_data->key.scan_code, + key_data->key_state.key_shift_state, + key_data->key_state.key_toggle_state); + + notify_function = calloc(1, sizeof(struct efi_cin_notify_function)); + if (!notify_function) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + notify_function->key = *key_data; + notify_function->function = key_notify_function; + list_add_tail(¬ify_function->link, &cin_notify_functions); + *notify_handle = notify_function; +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_unregister_key_notify() - unregister key notification function + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @notification_handle: handle received when registering + * Return: status code + * + * This function implements the SetState service of the + * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_unregister_key_notify( + struct efi_simple_text_input_ex_protocol *this, + void *notification_handle) +{ + efi_status_t ret = EFI_INVALID_PARAMETER; + struct efi_cin_notify_function *item, *notify_function = + notification_handle; + + EFI_ENTRY("%p, %p", this, notification_handle); + + /* Check parameters */ + if (!this || !notification_handle) + goto out; + + list_for_each_entry(item, &cin_notify_functions, link) { + if (item == notify_function) { + ret = EFI_SUCCESS; + break; + } + } + if (ret != EFI_SUCCESS) + goto out; + + /* Remove the notify function */ + list_del(¬ify_function->link); + free(notify_function); +out: + return EFI_EXIT(ret); +} + + +/** + * efi_cin_reset() - drain the input buffer + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @extended_verification: allow for exhaustive verification + * Return: status code + * + * This function implements the Reset service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_reset + (struct efi_simple_text_input_protocol *this, + bool extended_verification) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %d", this, extended_verification); + + /* Check parameters */ + if (!this) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + efi_cin_empty_buffer(); +out: + return EFI_EXIT(ret); +} + +/** + * efi_cin_read_key_stroke() - read key stroke + * + * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + * @key: key read from console + * Return: status code + * + * This function implements the ReadKeyStroke service of the + * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + */ +static efi_status_t EFIAPI efi_cin_read_key_stroke + (struct efi_simple_text_input_protocol *this, + struct efi_input_key *key) +{ + efi_status_t ret = EFI_SUCCESS; + + EFI_ENTRY("%p, %p", this, key); + + /* Check parameters */ + if (!this || !key) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* We don't do interrupts, so check for timers cooperatively */ + efi_timer_check(); + + /* Enable console input after ExitBootServices */ + efi_cin_check(); + + if (!key_available) { + ret = EFI_NOT_READY; + goto out; + } + *key = next_key.key; + key_available = false; + efi_con_in.wait_for_key->is_signaled = false; +out: + return EFI_EXIT(ret); +} + +static struct efi_simple_text_input_ex_protocol efi_con_in_ex = { + .reset = efi_cin_reset_ex, + .read_key_stroke_ex = efi_cin_read_key_stroke_ex, + .wait_for_key_ex = NULL, + .set_state = efi_cin_set_state, + .register_key_notify = efi_cin_register_key_notify, + .unregister_key_notify = efi_cin_unregister_key_notify, +}; + +struct efi_simple_text_input_protocol efi_con_in = { .reset = efi_cin_reset, .read_key_stroke = efi_cin_read_key_stroke, .wait_for_key = NULL, @@ -560,31 +1014,38 @@ struct efi_simple_input_interface efi_con_in = { static struct efi_event *console_timer_event; -static void EFIAPI efi_key_notify(struct efi_event *event, void *context) -{ -} - /* - * Notification function of the console timer event. + * efi_console_timer_notify() - notify the console timer event * - * event: console timer event - * context: not used + * @event: console timer event + * @context: not used */ static void EFIAPI efi_console_timer_notify(struct efi_event *event, void *context) { EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); + EFI_EXIT(EFI_SUCCESS); +} - /* Check if input is available */ - if (tstc()) { - /* Queue the wait for key event */ - efi_con_in.wait_for_key->is_signaled = true; - efi_signal_event(efi_con_in.wait_for_key, true); - } +/** + * efi_key_notify() - notify the wait for key event + * + * @event: wait for key event + * @context: not used + */ +static void EFIAPI efi_key_notify(struct efi_event *event, void *context) +{ + EFI_ENTRY("%p, %p", event, context); + efi_cin_check(); EFI_EXIT(EFI_SUCCESS); } -/* This gets called from do_bootefi_exec(). */ +/** + * efi_console_register() - install the console protocols + * + * This function is called from do_bootefi_exec(). + */ int efi_console_register(void) { efi_status_t r; @@ -598,17 +1059,27 @@ int efi_console_register(void) r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); if (r != EFI_SUCCESS) goto out_of_memory; + r = efi_add_protocol(efi_console_output_obj->handle, &efi_guid_text_output_protocol, &efi_con_out); if (r != EFI_SUCCESS) goto out_of_memory; + systab.con_out_handle = efi_console_output_obj->handle; + systab.stderr_handle = efi_console_output_obj->handle; + r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); if (r != EFI_SUCCESS) goto out_of_memory; + r = efi_add_protocol(efi_console_input_obj->handle, &efi_guid_text_input_protocol, &efi_con_in); if (r != EFI_SUCCESS) goto out_of_memory; + systab.con_in_handle = efi_console_input_obj->handle; + r = efi_add_protocol(efi_console_input_obj->handle, + &efi_guid_text_input_ex_protocol, &efi_con_in_ex); + if (r != EFI_SUCCESS) + goto out_of_memory; /* Create console events */ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, @@ -617,6 +1088,7 @@ int efi_console_register(void) printf("ERROR: Failed to register WaitForKey event\n"); return r; } + efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key; r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_console_timer_notify, NULL, NULL, &console_timer_event); @@ -630,6 +1102,6 @@ int efi_console_register(void) printf("ERROR: Failed to set console timer\n"); return r; out_of_memory: - printf("ERROR: Out of meemory\n"); + printf("ERROR: Out of memory\n"); return r; } diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 9d776a6d99..5a61a1c1dc 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -22,10 +22,6 @@ static const struct efi_device_path END = { .length = sizeof(END), }; -#define U_BOOT_GUID \ - EFI_GUID(0xe61d73b9, 0xa384, 0x4acc, \ - 0xae, 0xab, 0x82, 0xe8, 0x28, 0xf3, 0x62, 0x8b) - /* template ROOT node: */ static const struct efi_device_path_vendor ROOT = { .dp = { diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index ca8037def2..0082236359 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -17,6 +17,15 @@ const efi_guid_t efi_guid_device_path_to_text_protocol = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; +/** + * efi_str_to_u16() - convert ASCII string to UTF-16 + * + * A u16 buffer is allocated from pool. The ASCII string is copied to the u16 + * buffer. + * + * @str: ASCII string + * Return: UTF-16 string. NULL if out of memory. + */ static u16 *efi_str_to_u16(char *str) { efi_uintn_t len; @@ -29,7 +38,6 @@ static u16 *efi_str_to_u16(char *str) if (ret != EFI_SUCCESS) return NULL; ascii2unicode(out, str); - out[len - 1] = 0; return out; } diff --git a/lib/efi_loader/efi_file.c b/lib/efi_loader/efi_file.c index e6a15bcb52..0753a36a20 100644 --- a/lib/efi_loader/efi_file.c +++ b/lib/efi_loader/efi_file.c @@ -9,6 +9,7 @@ #include <charset.h> #include <efi_loader.h> #include <malloc.h> +#include <mapmem.h> #include <fs.h> /* GUID for file system information */ @@ -126,11 +127,22 @@ static int sanitize_path(char *path) return 0; } -/* NOTE: despite what you would expect, 'file_name' is actually a path. - * With windoze style backlashes, ofc. +/** + * file_open() - open a file handle + * + * @fs: file system + * @parent: directory relative to which the file is to be opened + * @file_name: path of the file to be opened. '\', '.', or '..' may + * be used as modifiers. A leading backslash indicates an + * absolute path. + * @mode: bit mask indicating the access mode (read, write, + * create) + * @attributes: attributes for newly created file + * Returns: handle to the opened file or NULL */ static struct efi_file_handle *file_open(struct file_system *fs, - struct file_handle *parent, s16 *file_name, u64 mode) + struct file_handle *parent, s16 *file_name, u64 mode, + u64 attributes) { struct file_handle *fh; char f0[MAX_UTF8_PER_UTF16] = {0}; @@ -139,7 +151,7 @@ static struct efi_file_handle *file_open(struct file_system *fs, if (file_name) { utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1); - flen = utf16_strlen((u16 *)file_name); + flen = u16_strlen((u16 *)file_name); } /* we could have a parent, but also an absolute path: */ @@ -173,7 +185,12 @@ static struct efi_file_handle *file_open(struct file_system *fs, if (set_blk_dev(fh)) goto error; - if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path))) + if ((mode & EFI_FILE_MODE_CREATE) && + (attributes & EFI_FILE_DIRECTORY)) { + if (fs_mkdir(fh->path)) + goto error; + } else if (!((mode & EFI_FILE_MODE_CREATE) || + fs_exists(fh->path))) goto error; /* figure out if file is a directory: */ @@ -195,15 +212,46 @@ static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, s16 *file_name, u64 open_mode, u64 attributes) { struct file_handle *fh = to_fh(file); + efi_status_t ret; EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name, open_mode, attributes); - *new_handle = file_open(fh->fs, fh, file_name, open_mode); - if (!*new_handle) - return EFI_EXIT(EFI_NOT_FOUND); + /* Check parameters */ + if (!file || !new_handle || !file_name) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + if (open_mode != EFI_FILE_MODE_READ && + open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) && + open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | + EFI_FILE_MODE_CREATE)) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + /* + * The UEFI spec requires that attributes are only set in create mode. + * The SCT does not care about this and sets EFI_FILE_DIRECTORY in + * read mode. EDK2 does not check that attributes are zero if not in + * create mode. + * + * So here we only check attributes in create mode and do not check + * that they are zero otherwise. + */ + if ((open_mode & EFI_FILE_MODE_CREATE) && + (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) { + ret = EFI_INVALID_PARAMETER; + goto out; + } - return EFI_EXIT(EFI_SUCCESS); + /* Open file */ + *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes); + if (*new_handle) + ret = EFI_SUCCESS; + else + ret = EFI_NOT_FOUND; +out: + return EFI_EXIT(ret); } static efi_status_t file_close(struct file_handle *fh) @@ -223,9 +271,21 @@ static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file) static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file) { struct file_handle *fh = to_fh(file); + efi_status_t ret = EFI_SUCCESS; + EFI_ENTRY("%p", file); + + if (set_blk_dev(fh)) { + ret = EFI_DEVICE_ERROR; + goto error; + } + + if (fs_unlink(fh->path)) + ret = EFI_DEVICE_ERROR; file_close(fh); - return EFI_EXIT(EFI_WARN_DELETE_FAILURE); + +error: + return EFI_EXIT(ret); } static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, @@ -233,7 +293,7 @@ static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, { loff_t actread; - if (fs_read(fh->path, (ulong)buffer, fh->offset, + if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size, &actread)) return EFI_DEVICE_ERROR; @@ -363,7 +423,7 @@ static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, goto error; } - if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size, + if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size, &actwrite)) { ret = EFI_DEVICE_ERROR; goto error; @@ -438,7 +498,7 @@ static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, struct file_handle *fh = to_fh(file); efi_status_t ret = EFI_SUCCESS; - EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer); + EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer); if (!guidcmp(info_type, &efi_file_info_guid)) { struct efi_file_info *info = buffer; @@ -598,7 +658,7 @@ efi_open_volume(struct efi_simple_file_system_protocol *this, EFI_ENTRY("%p, %p", this, root); - *root = file_open(fs, NULL, NULL, 0); + *root = file_open(fs, NULL, NULL, 0, 0); return EFI_EXIT(EFI_SUCCESS); } diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index fdf40a62c8..a18ce0a570 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -48,20 +48,21 @@ static int machines[] = { * If the program counter is located within the image the offset to the base * address is shown. * + * @obj: EFI object * @image: loaded image * @pc: program counter (use NULL to suppress offset output) * @return: status code */ -efi_status_t efi_print_image_info(struct efi_loaded_image *image, void *pc) +static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj, + struct efi_loaded_image *image, + void *pc) { - if (!image) - return EFI_INVALID_PARAMETER; printf("UEFI image"); printf(" [0x%p:0x%p]", - image->reloc_base, image->reloc_base + image->reloc_size - 1); - if (pc && pc >= image->reloc_base && - pc < image->reloc_base + image->reloc_size) - printf(" pc=0x%zx", pc - image->reloc_base); + obj->reloc_base, obj->reloc_base + obj->reloc_size - 1); + if (pc && pc >= obj->reloc_base && + pc < obj->reloc_base + obj->reloc_size) + printf(" pc=0x%zx", pc - obj->reloc_base); if (image->file_path) printf(" '%pD'", image->file_path); printf("\n"); @@ -82,6 +83,7 @@ void efi_print_image_infos(void *pc) list_for_each_entry(handler, &efiobj->protocols, link) { if (!guidcmp(handler->guid, &efi_guid_loaded_image)) { efi_print_image_info( + (struct efi_loaded_image_obj *)efiobj, handler->protocol_interface, pc); } } @@ -196,7 +198,8 @@ static void efi_set_code_and_data_type( * piece of memory. On successful load it then returns the entry point for * the binary. Otherwise NULL. */ -void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) +void *efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, + struct efi_loaded_image *loaded_image_info) { IMAGE_NT_HEADERS32 *nt; IMAGE_DOS_HEADER *dos; @@ -314,8 +317,8 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) /* Populate the loaded image interface bits */ loaded_image_info->image_base = efi; loaded_image_info->image_size = image_size; - loaded_image_info->reloc_base = efi_reloc; - loaded_image_info->reloc_size = virt_size; + handle->reloc_base = efi_reloc; + handle->reloc_size = virt_size; return entry; } diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 0ac4ff554b..5bd4f4d7fc 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -65,9 +65,54 @@ static int efi_mem_cmp(void *priv, struct list_head *a, struct list_head *b) return -1; } +static uint64_t desc_get_end(struct efi_mem_desc *desc) +{ + return desc->physical_start + (desc->num_pages << EFI_PAGE_SHIFT); +} + static void efi_mem_sort(void) { + struct list_head *lhandle; + struct efi_mem_list *prevmem = NULL; + bool merge_again = true; + list_sort(NULL, &efi_mem, efi_mem_cmp); + + /* Now merge entries that can be merged */ + while (merge_again) { + merge_again = false; + list_for_each(lhandle, &efi_mem) { + struct efi_mem_list *lmem; + struct efi_mem_desc *prev = &prevmem->desc; + struct efi_mem_desc *cur; + uint64_t pages; + + lmem = list_entry(lhandle, struct efi_mem_list, link); + if (!prevmem) { + prevmem = lmem; + continue; + } + + cur = &lmem->desc; + + if ((desc_get_end(cur) == prev->physical_start) && + (prev->type == cur->type) && + (prev->attribute == cur->attribute)) { + /* There is an existing map before, reuse it */ + pages = cur->num_pages; + prev->num_pages += pages; + prev->physical_start -= pages << EFI_PAGE_SHIFT; + prev->virtual_start -= pages << EFI_PAGE_SHIFT; + list_del(&lmem->link); + free(lmem); + + merge_again = true; + break; + } + + prevmem = lmem; + } + } } /** efi_mem_carve_out - unmap memory region @@ -303,7 +348,7 @@ efi_status_t efi_allocate_pages(int type, int memory_type, switch (type) { case EFI_ALLOCATE_ANY_PAGES: /* Any page */ - addr = efi_find_free_memory(len, gd->start_addr_sp); + addr = efi_find_free_memory(len, -1ULL); if (!addr) { r = EFI_NOT_FOUND; break; diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c new file mode 100644 index 0000000000..b056ba3ee8 --- /dev/null +++ b/lib/efi_loader/efi_root_node.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Root node for system services + * + * Copyright (c) 2018 Heinrich Schuchardt + */ + +#include <common.h> +#include <malloc.h> +#include <efi_loader.h> + +const efi_guid_t efi_u_boot_guid = U_BOOT_GUID; + +struct efi_root_dp { + struct efi_device_path_vendor vendor; + struct efi_device_path end; +} __packed; + +/** + * efi_root_node_register() - create root node + * + * Create the root node on which we install all protocols that are + * not related to a loaded image or a driver. + * + * Return: status code + */ +efi_status_t efi_root_node_register(void) +{ + efi_handle_t root; + efi_status_t ret; + struct efi_root_dp *dp; + + /* Create handle */ + ret = efi_create_handle(&root); + if (ret != EFI_SUCCESS) + return ret; + + /* Install device path protocol */ + dp = calloc(1, sizeof(*dp)); + if (!dp) + return EFI_OUT_OF_RESOURCES; + + /* Fill vendor node */ + dp->vendor.dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + dp->vendor.dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + dp->vendor.dp.length = sizeof(struct efi_device_path_vendor); + dp->vendor.guid = efi_u_boot_guid; + + /* Fill end node */ + dp->end.type = DEVICE_PATH_TYPE_END; + dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END; + dp->end.length = sizeof(struct efi_device_path); + + /* Install device path protocol */ + ret = efi_add_protocol(root, &efi_guid_device_path, dp); + if (ret != EFI_SUCCESS) + goto failure; + + /* Install device path to text protocol */ + ret = efi_add_protocol(root, &efi_guid_device_path_to_text_protocol, + (void *)&efi_device_path_to_text); + if (ret != EFI_SUCCESS) + goto failure; + + /* Install device path utilities protocol */ + ret = efi_add_protocol(root, &efi_guid_device_path_utilities_protocol, + (void *)&efi_device_path_utilities); + if (ret != EFI_SUCCESS) + goto failure; + + /* Install Unicode collation protocol */ + ret = efi_add_protocol(root, &efi_guid_unicode_collation_protocol, + (void *)&efi_unicode_collation_protocol); + if (ret != EFI_SUCCESS) + goto failure; + +failure: + return ret; +} diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 27136cbedd..c5fbd91fa3 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -30,8 +30,9 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void); static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void); /* - * TODO(sjg@chromium.org): These defines and structs should come from the elf - * header for each arch (or a generic header) rather than being repeated here. + * TODO(sjg@chromium.org): These defines and structures should come from the ELF + * header for each architecture (or a generic header) rather than being repeated + * here. */ #if defined(__aarch64__) #define R_RELATIVE R_AARCH64_RELATIVE @@ -79,7 +80,7 @@ struct elf_rela { }; /* - * EFI Runtime code lives in 2 stages. In the first stage, U-Boot and an EFI + * EFI runtime code lives in two stages. In the first stage, U-Boot and an EFI * payload are running concurrently at the same time. In this mode, we can * handle a good number of runtime callbacks */ @@ -97,7 +98,7 @@ void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table) } /** - * efi_reset_system_boottime() - reset system at boottime + * efi_reset_system_boottime() - reset system at boot time * * This function implements the ResetSystem() runtime service before * SetVirtualAddressMap() is called. @@ -144,7 +145,7 @@ static void EFIAPI efi_reset_system_boottime( } /** - * efi_get_time_boottime() - get current time at boottime + * efi_get_time_boottime() - get current time at boot time * * This function implements the GetTime runtime service before * SetVirtualAddressMap() is called. @@ -335,7 +336,7 @@ static void efi_runtime_detach(ulong offset) *p = newaddr; } - /* Update crc32 */ + /* Update CRC32 */ efi_update_table_header_crc32(&efi_runtime_services.hdr); } @@ -489,7 +490,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map( * available at runtime. * * @mmio_ptr: address of the memory-mapped IO region - * @len: size of thememory-mapped IO region + * @len: size of the memory-mapped IO region * Returns: status code */ efi_status_t efi_add_runtime_mmio(void *mmio_ptr, u64 len) @@ -607,7 +608,7 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule( * * @capsule_header_array: pointer to array of virtual pointers * @capsule_count: number of pointers in capsule_header_array - * @capsule_size: maximum capsule size + * @maximum_capsule_size: maximum capsule size * @reset_type: type of reset needed for capsule update * Returns: status code */ diff --git a/lib/efi_loader/efi_unicode_collation.c b/lib/efi_loader/efi_unicode_collation.c new file mode 100644 index 0000000000..7f3ea3c77e --- /dev/null +++ b/lib/efi_loader/efi_unicode_collation.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Unicode collation protocol + * + * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <charset.h> +#include <cp1250.h> +#include <cp437.h> +#include <efi_loader.h> + +/* Characters that may not be used in file names */ +static const char illegal[] = "<>:\"/\\|?*"; + +/* + * EDK2 assumes codepage 1250 when creating FAT 8.3 file names. + * Linux defaults to codepage 437 for FAT 8.3 file names. + */ +#if CONFIG_FAT_DEFAULT_CODEPAGE == 1250 +/* Unicode code points for code page 1250 characters 0x80 - 0xff */ +static const u16 codepage[] = CP1250; +#else +/* Unicode code points for code page 437 characters 0x80 - 0xff */ +static const u16 codepage[] = CP437; +#endif + +/* GUID of the EFI_UNICODE_COLLATION_PROTOCOL */ +const efi_guid_t efi_guid_unicode_collation_protocol = + EFI_UNICODE_COLLATION_PROTOCOL2_GUID; + +/** + * efi_stri_coll() - compare utf-16 strings case-insenitively + * + * @this: unicode collation protocol instance + * @s1: first string + * @s2: second string + * + * This function implements the StriColl() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * TODO: + * The implementation does not follow the Unicode collation algorithm. + * For ASCII characters it results in the same sort order as EDK2. + * We could use table UNICODE_CAPITALIZATION_TABLE for better results. + * + * Return: 0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2 + */ +static efi_intn_t EFIAPI efi_stri_coll( + struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2) +{ + s32 c1, c2; + efi_intn_t ret = 0; + + EFI_ENTRY("%p, %ls, %ls", this, s1, s2); + for (; *s1 | *s2; ++s1, ++s2) { + c1 = utf_to_upper(*s1); + c2 = utf_to_upper(*s2); + if (c1 < c2) { + ret = -1; + goto out; + } else if (c1 > c2) { + ret = 1; + goto out; + } + } +out: + EFI_EXIT(EFI_SUCCESS); + return ret; +} + +/** + * metai_match() - compare utf-16 string with a pattern string case-insenitively + * + * @s: string to compare + * @p: pattern string + * + * The pattern string may use these: + * - * matches >= 0 characters + * - ? matches 1 character + * - [<char1><char2>...<charN>] match any character in the set + * - [<char1>-<char2>] matches any character in the range + * + * This function is called my efi_metai_match(). + * + * For '*' pattern searches this function calls itself recursively. + * Performance-wise this is suboptimal, especially for multiple '*' wildcards. + * But it results in simple code. + * + * Return: true if the string is matched. + */ +static bool metai_match(const u16 *s, const u16 *p) +{ + u16 first; + + for (; *s && *p; ++s, ++p) { + switch (*p) { + case '*': + /* Match 0 or more characters */ + ++p; + for (;; ++s) { + if (metai_match(s, p)) + return true; + if (!*s) + return false; + } + case '?': + /* Match any one character */ + break; + case '[': + /* Match any character in the set */ + ++p; + first = *p; + if (first == ']') + /* Empty set */ + return false; + ++p; + if (*p == '-') { + /* Range */ + ++p; + if (*s < first || *s > *p) + return false; + ++p; + if (*p != ']') + return false; + } else { + /* Set */ + bool hit = false; + + if (*s == first) + hit = true; + for (; *p && *p != ']'; ++p) { + if (*p == *s) + hit = true; + } + if (!hit || *p != ']') + return false; + } + break; + default: + /* Match one character */ + if (*p != *s) + return false; + } + } + if (!*p && !*s) + return true; + return false; +} + +/** + * efi_metai_match() - compare utf-16 string with a pattern string + * case-insenitively + * + * @this: unicode collation protocol instance + * @s: string to compare + * @p: pattern string + * + * The pattern string may use these: + * - * matches >= 0 characters + * - ? matches 1 character + * - [<char1><char2>...<charN>] match any character in the set + * - [<char1>-<char2>] matches any character in the range + * + * This function implements the MetaMatch() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + * + * Return: true if the string is matched. + */ +static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this, + const u16 *string, const u16 *pattern) +{ + bool ret; + + EFI_ENTRY("%p, %ls, %ls", this, string, pattern); + ret = metai_match(string, pattern); + EFI_EXIT(EFI_SUCCESS); + return ret; +} + +/** + * efi_str_lwr() - convert to lower case + * + * @this: unicode collation protocol instance + * @string: string to convert + * @p: pattern string + * + * The conversion is done in place. As long as upper and lower letters use the + * same number of words this does not pose a problem. + * + * This function implements the StrLwr() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + */ +static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this, + u16 *string) +{ + EFI_ENTRY("%p, %ls", this, string); + for (; *string; ++string) + *string = utf_to_lower(*string); + EFI_EXIT(EFI_SUCCESS); +} + +/** + * efi_str_upr() - convert to upper case + * + * @this: unicode collation protocol instance + * @string: string to convert + * @p: pattern string + * + * The conversion is done in place. As long as upper and lower letters use the + * same number of words this does not pose a problem. + * + * This function implements the StrUpr() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + */ +static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this, + u16 *string) +{ + EFI_ENTRY("%p, %ls", this, string); + for (; *string; ++string) + *string = utf_to_upper(*string); + EFI_EXIT(EFI_SUCCESS); +} + +/** + * efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode + * + * @this: unicode collation protocol instance + * @fat_size: size of the string to convert + * @fat: string to convert + * @string: converted string + * + * This function implements the FatToStr() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + */ +static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this, + efi_uintn_t fat_size, char *fat, u16 *string) +{ + efi_uintn_t i; + u16 c; + + EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string); + for (i = 0; i < fat_size; ++i) { + c = (unsigned char)fat[i]; + if (c > 0x80) + c = codepage[i - 0x80]; + string[i] = c; + if (!c) + break; + } + string[i] = 0; + EFI_EXIT(EFI_SUCCESS); +} + +/** + * efi_fat_to_str() - convert a utf-16 string to legal characters for a FAT + * file name in an OEM code page + * + * @this: unicode collation protocol instance + * @string: Unicode string to convert + * @fat_size: size of the target buffer + * @fat: converted string + * + * This function implements the StrToFat() service of the + * EFI_UNICODE_COLLATION_PROTOCOL. + * + * Return: true if an illegal character was substituted by '_'. + */ +static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this, + const u16 *string, efi_uintn_t fat_size, + char *fat) +{ + efi_uintn_t i; + s32 c; + bool ret = false; + + EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat); + for (i = 0; i < fat_size;) { + c = utf16_get(&string); + switch (c) { + /* Ignore period and space */ + case '.': + case ' ': + continue; + case 0: + break; + } + c = utf_to_upper(c); + if (c >= 0x80) { + int j; + + /* Look for codepage translation */ + for (j = 0; j < 0x80; ++j) { + if (c == codepage[j]) { + c = j + 0x80; + break; + } + } + if (j >= 0x80) { + c = '_'; + ret = true; + } + } else if (c && (c < 0x20 || strchr(illegal, c))) { + c = '_'; + ret = true; + } + + fat[i] = c; + if (!c) + break; + ++i; + } + EFI_EXIT(EFI_SUCCESS); + return ret; +} + +const struct efi_unicode_collation_protocol efi_unicode_collation_protocol = { + .stri_coll = efi_stri_coll, + .metai_match = efi_metai_match, + .str_lwr = efi_str_lwr, + .str_upr = efi_str_upr, + .fat_to_str = efi_fat_to_str, + .str_to_fat = efi_str_to_fat, + .supported_languages = "en", +}; diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 90b637215e..a1313fa215 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -44,10 +44,7 @@ * converted to utf16? */ -#define MAX_VAR_NAME 31 -#define MAX_NATIVE_VAR_NAME \ - (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx_") + \ - (MAX_VAR_NAME * MAX_UTF8_PER_UTF16)) +#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_")) static int hex(int ch) { @@ -101,18 +98,20 @@ static char *mem2hex(char *hexstr, const u8 *mem, int count) return hexstr; } -static efi_status_t efi_to_native(char *native, u16 *variable_name, +static efi_status_t efi_to_native(char **native, const u16 *variable_name, efi_guid_t *vendor) { size_t len; + char *pos; - len = utf16_strlen((u16 *)variable_name); - if (len >= MAX_VAR_NAME) - return EFI_DEVICE_ERROR; + len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1; + *native = malloc(len); + if (!*native) + return EFI_OUT_OF_RESOURCES; - native += sprintf(native, "efi_%pUl_", vendor); - native = (char *)utf16_to_utf8((u8 *)native, (u16 *)variable_name, len); - *native = '\0'; + pos = *native; + pos += sprintf(pos, "efi_%pUl_", vendor); + utf16_utf8_strcpy(&pos, variable_name); return EFI_SUCCESS; } @@ -168,7 +167,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor, u32 *attributes, efi_uintn_t *data_size, void *data) { - char native_name[MAX_NATIVE_VAR_NAME + 1]; + char *native_name; efi_status_t ret; unsigned long in_size; const char *val, *s; @@ -180,13 +179,14 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, efi_guid_t *vendor, if (!variable_name || !vendor || !data_size) return EFI_EXIT(EFI_INVALID_PARAMETER); - ret = efi_to_native(native_name, variable_name, vendor); + ret = efi_to_native(&native_name, variable_name, vendor); if (ret) return EFI_EXIT(ret); debug("%s: get '%s'\n", __func__, native_name); val = env_get(native_name); + free(native_name); if (!val) return EFI_EXIT(EFI_NOT_FOUND); @@ -256,35 +256,41 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, u32 attributes, efi_uintn_t data_size, void *data) { - char native_name[MAX_NATIVE_VAR_NAME + 1]; + char *native_name = NULL, *val = NULL, *s; efi_status_t ret = EFI_SUCCESS; - char *val, *s; u32 attr; EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, data_size, data); - if (!variable_name || !vendor) - return EFI_EXIT(EFI_INVALID_PARAMETER); + if (!variable_name || !vendor) { + ret = EFI_INVALID_PARAMETER; + goto out; + } - ret = efi_to_native(native_name, variable_name, vendor); + ret = efi_to_native(&native_name, variable_name, vendor); if (ret) - return EFI_EXIT(ret); + goto out; #define ACCESS_ATTR (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS) if ((data_size == 0) || !(attributes & ACCESS_ATTR)) { /* delete the variable: */ env_set(native_name, NULL); - return EFI_EXIT(EFI_SUCCESS); + ret = EFI_SUCCESS; + goto out; } val = env_get(native_name); if (val) { parse_attr(val, &attr); - if (attr & READ_ONLY) - return EFI_EXIT(EFI_WRITE_PROTECTED); + if (attr & READ_ONLY) { + /* We should not free val */ + val = NULL; + ret = EFI_WRITE_PROTECTED; + goto out; + } } val = malloc(2 * data_size + strlen("{ro,run,boot}(blob)") + 1); @@ -320,6 +326,8 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, efi_guid_t *vendor, if (env_set(native_name, val)) ret = EFI_DEVICE_ERROR; +out: + free(native_name); free(val); return EFI_EXIT(ret); |