diff options
Diffstat (limited to 'lib/efi_loader')
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 854 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 20 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 284 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path_to_text.c | 16 | ||||
-rw-r--r-- | lib/efi_loader/efi_disk.c | 137 | ||||
-rw-r--r-- | lib/efi_loader/efi_image_loader.c | 72 | ||||
-rw-r--r-- | lib/efi_loader/efi_memory.c | 44 | ||||
-rw-r--r-- | lib/efi_loader/helloworld.c | 26 |
8 files changed, 1231 insertions, 222 deletions
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b90bd0b426..39d8511fe3 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -56,6 +56,14 @@ static volatile void *efi_gd, *app_gd; static int entry_count; static int nesting_level; +/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ +const efi_guid_t efi_guid_driver_binding_protocol = + EFI_DRIVER_BINDING_PROTOCOL_GUID; + +static efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle); /* Called on every callback entry */ int __efi_entry_check(void) @@ -141,13 +149,14 @@ const char *__efi_nesting_dec(void) * For the SignalEvent service see efi_signal_event_ext. * * @event event to signal + * @check_tpl check the TPL level */ -void efi_signal_event(struct efi_event *event) +void efi_signal_event(struct efi_event *event, bool check_tpl) { if (event->notify_function) { event->is_queued = true; /* Check TPL */ - if (efi_tpl >= event->notify_tpl) + if (check_tpl && efi_tpl >= event->notify_tpl) return; EFI_CALL_VOID(event->notify_function(event, event->notify_context)); @@ -344,7 +353,7 @@ void efi_add_handle(struct efi_object *obj) * @handle new handle * @return status code */ -efi_status_t efi_create_handle(void **handle) +efi_status_t efi_create_handle(efi_handle_t *handle) { struct efi_object *obj; efi_status_t r; @@ -367,7 +376,7 @@ efi_status_t efi_create_handle(void **handle) * @handler reference to the protocol * @return status code */ -efi_status_t efi_search_protocol(const void *handle, +efi_status_t efi_search_protocol(const efi_handle_t handle, const efi_guid_t *protocol_guid, struct efi_handler **handler) { @@ -400,7 +409,8 @@ efi_status_t efi_search_protocol(const void *handle, * @protocol_interface interface of the protocol implementation * @return status code */ -efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_remove_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface) { struct efi_handler *handler; @@ -422,21 +432,18 @@ efi_status_t efi_remove_protocol(const void *handle, const efi_guid_t *protocol, * @handle handle from which the protocols shall be deleted * @return status code */ -efi_status_t efi_remove_all_protocols(const void *handle) +efi_status_t efi_remove_all_protocols(const efi_handle_t handle) { struct efi_object *efiobj; - struct list_head *lhandle; - struct list_head *pos; + struct efi_handler *protocol; + struct efi_handler *pos; efiobj = efi_search_obj(handle); if (!efiobj) return EFI_INVALID_PARAMETER; - list_for_each_safe(lhandle, pos, &efiobj->protocols) { - struct efi_handler *protocol; + list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) { efi_status_t ret; - protocol = list_entry(lhandle, struct efi_handler, link); - ret = efi_remove_protocol(handle, protocol->guid, protocol->protocol_interface); if (ret != EFI_SUCCESS) @@ -559,7 +566,7 @@ void efi_timer_check(void) if (!efi_events[i].type) continue; if (efi_events[i].is_queued) - efi_signal_event(&efi_events[i]); + efi_signal_event(&efi_events[i], true); if (!(efi_events[i].type & EVT_TIMER) || now < efi_events[i].trigger_next) continue; @@ -575,7 +582,7 @@ void efi_timer_check(void) continue; } efi_events[i].is_signaled = true; - efi_signal_event(&efi_events[i]); + efi_signal_event(&efi_events[i], true); } WATCHDOG_RESET(); } @@ -684,7 +691,7 @@ known_event: if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); if (!event[i]->is_signaled) - efi_signal_event(event[i]); + efi_signal_event(event[i], true); } /* Wait for signal */ @@ -734,7 +741,7 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) break; event->is_signaled = true; if (event->type & EVT_NOTIFY_SIGNAL) - efi_signal_event(event); + efi_signal_event(event, true); break; } return EFI_EXIT(EFI_SUCCESS); @@ -791,7 +798,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) if (!event->type || event->type & EVT_NOTIFY_SIGNAL) break; if (!event->is_signaled) - efi_signal_event(event); + efi_signal_event(event, true); if (event->is_signaled) return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_NOT_READY); @@ -805,7 +812,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) * @handle handle to find * @return EFI object */ -struct efi_object *efi_search_obj(const void *handle) +struct efi_object *efi_search_obj(const efi_handle_t handle) { struct efi_object *efiobj; @@ -818,6 +825,40 @@ struct efi_object *efi_search_obj(const void *handle) } /* + * Create open protocol info entry and add it to a protocol. + * + * @handler handler of a protocol + * @return open protocol info entry + */ +static struct efi_open_protocol_info_entry *efi_create_open_info( + struct efi_handler *handler) +{ + struct efi_open_protocol_info_item *item; + + item = calloc(1, sizeof(struct efi_open_protocol_info_item)); + if (!item) + return NULL; + /* Append the item to the open protocol info list. */ + list_add_tail(&item->link, &handler->open_infos); + + return &item->info; +} + +/* + * Remove an open protocol info entry from a protocol. + * + * @handler handler of a protocol + * @return status code + */ +static efi_status_t efi_delete_open_info( + struct efi_open_protocol_info_item *item) +{ + list_del(&item->link); + free(item); + return EFI_SUCCESS; +} + +/* * Install new protocol on a handle. * * @handle handle on which the protocol shall be installed @@ -825,7 +866,8 @@ struct efi_object *efi_search_obj(const void *handle) * @protocol_interface interface of the protocol implementation * @return status code */ -efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, +efi_status_t efi_add_protocol(const efi_handle_t handle, + const efi_guid_t *protocol, void *protocol_interface) { struct efi_object *efiobj; @@ -843,7 +885,10 @@ efi_status_t efi_add_protocol(const void *handle, const efi_guid_t *protocol, return EFI_OUT_OF_RESOURCES; handler->guid = protocol; handler->protocol_interface = protocol_interface; + INIT_LIST_HEAD(&handler->open_infos); list_add_tail(&handler->link, &efiobj->protocols); + if (!guidcmp(&efi_guid_device_path, protocol)) + EFI_PRINT("installed device path '%pD'\n", protocol_interface); return EFI_SUCCESS; } @@ -907,9 +952,9 @@ out: * @new_interface interface to be installed * @return status code */ -static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, - const efi_guid_t *protocol, void *old_interface, - void *new_interface) +static efi_status_t EFIAPI efi_reinstall_protocol_interface( + efi_handle_t handle, const efi_guid_t *protocol, + void *old_interface, void *new_interface) { EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface, new_interface); @@ -917,6 +962,109 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, } /* + * Get all drivers associated to a controller. + * The allocated buffer has to be freed with free(). + * + * @efiobj handle of the controller + * @protocol protocol guid (optional) + * @number_of_drivers number of child controllers + * @driver_handle_buffer handles of the the drivers + * @return status code + */ +static efi_status_t efi_get_drivers(struct efi_object *efiobj, + const efi_guid_t *protocol, + efi_uintn_t *number_of_drivers, + efi_handle_t **driver_handle_buffer) +{ + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_uintn_t count = 0, i; + bool duplicate; + + /* Count all driver associations */ + list_for_each_entry(handler, &efiobj->protocols, link) { + if (protocol && guidcmp(handler->guid, protocol)) + continue; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_DRIVER) + ++count; + } + } + /* + * Create buffer. In case of duplicate driver assignments the buffer + * will be too large. But that does not harm. + */ + *number_of_drivers = 0; + *driver_handle_buffer = calloc(count, sizeof(efi_handle_t)); + if (!*driver_handle_buffer) + return EFI_OUT_OF_RESOURCES; + /* Collect unique driver handles */ + list_for_each_entry(handler, &efiobj->protocols, link) { + if (protocol && guidcmp(handler->guid, protocol)) + continue; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_DRIVER) { + /* Check this is a new driver */ + duplicate = false; + for (i = 0; i < *number_of_drivers; ++i) { + if ((*driver_handle_buffer)[i] == + item->info.agent_handle) + duplicate = true; + } + /* Copy handle to buffer */ + if (!duplicate) { + i = (*number_of_drivers)++; + (*driver_handle_buffer)[i] = + item->info.agent_handle; + } + } + } + } + return EFI_SUCCESS; +} + +/* + * Disconnect all drivers from a controller. + * + * This function implements the DisconnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @efiobj handle of the controller + * @protocol protocol guid (optional) + * @child_handle handle of the child to destroy + * @return status code + */ +static efi_status_t efi_disconnect_all_drivers( + struct efi_object *efiobj, + const efi_guid_t *protocol, + efi_handle_t child_handle) +{ + efi_uintn_t number_of_drivers; + efi_handle_t *driver_handle_buffer; + efi_status_t r, ret; + + ret = efi_get_drivers(efiobj, protocol, &number_of_drivers, + &driver_handle_buffer); + if (ret != EFI_SUCCESS) + return ret; + + ret = EFI_NOT_FOUND; + while (number_of_drivers) { + r = EFI_CALL(efi_disconnect_controller( + efiobj->handle, + driver_handle_buffer[--number_of_drivers], + child_handle)); + if (r == EFI_SUCCESS) + ret = r; + } + free(driver_handle_buffer); + return ret; +} + +/* * Uninstall protocol interface. * * This function implements the UninstallProtocolInterface service. @@ -929,29 +1077,46 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, * @return status code */ static efi_status_t EFIAPI efi_uninstall_protocol_interface( - void *handle, const efi_guid_t *protocol, + efi_handle_t handle, const efi_guid_t *protocol, void *protocol_interface) { + struct efi_object *efiobj; struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_item *pos; efi_status_t r; EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); - if (!handle || !protocol) { + /* Check handle */ + efiobj = efi_search_obj(handle); + if (!efiobj) { r = EFI_INVALID_PARAMETER; goto out; } - /* Find the protocol on the handle */ r = efi_search_protocol(handle, protocol, &handler); if (r != EFI_SUCCESS) goto out; - if (handler->protocol_interface) { - /* TODO disconnect controllers */ + /* Disconnect controllers */ + efi_disconnect_all_drivers(efiobj, protocol, NULL); + if (!list_empty(&handler->open_infos)) { r = EFI_ACCESS_DENIED; - } else { - r = efi_remove_protocol(handle, protocol, protocol_interface); + goto out; + } + /* Close protocol */ + list_for_each_entry_safe(item, pos, &handler->open_infos, link) { + if (item->info.attributes == + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL || + item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL || + item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + list_del(&item->link); } + if (!list_empty(&handler->open_infos)) { + r = EFI_ACCESS_DENIED; + goto out; + } + r = efi_remove_protocol(handle, protocol, protocol_interface); out: return EFI_EXIT(r); } @@ -1310,7 +1475,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, struct efi_object *obj; efi_status_t ret; - EFI_ENTRY("%d, %p, %p, %p, %ld, %p", boot_policy, parent_image, + EFI_ENTRY("%d, %p, %pD, %p, %ld, %p", boot_policy, parent_image, file_path, source_buffer, source_size, image_handle); info = calloc(1, sizeof(*info)); @@ -1369,8 +1534,10 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) { - ulong (*entry)(void *image_handle, struct efi_system_table *st); + asmlinkage ulong (*entry)(efi_handle_t image_handle, + struct efi_system_table *st); struct efi_loaded_image *info = image_handle; + efi_status_t ret; EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); entry = info->reserved; @@ -1379,18 +1546,37 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, /* call the image! */ if (setjmp(&info->exit_jmp)) { - /* We returned from the child image */ + /* + * We called the entry point of the child image with EFI_CALL + * in the lines below. The child image called the Exit() boot + * service efi_exit() which executed the long jump that brought + * us to the current line. This implies that the second half + * of the EFI_CALL macro has not been executed. + */ +#ifdef CONFIG_ARM + /* + * efi_exit() called efi_restore_gd(). We have to undo this + * otherwise __efi_entry_check() will put the wrong value into + * app_gd. + */ + gd = app_gd; +#endif + /* + * To get ready to call EFI_EXIT below we have to execute the + * missed out steps of EFI_CALL. + */ + assert(__efi_entry_check()); + debug("%sEFI: %lu returned by started image\n", + __efi_nesting_dec(), + (unsigned long)((uintptr_t)info->exit_status & + ~EFI_ERROR_MASK)); return EFI_EXIT(info->exit_status); } - __efi_nesting_dec(); - __efi_exit_check(); - entry(image_handle, &systab); - __efi_entry_check(); - __efi_nesting_inc(); + ret = EFI_CALL(entry(image_handle, &systab)); /* Should usually never get here */ - return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(ret); } /* @@ -1427,7 +1613,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, exit_data_size, exit_data); /* Make sure entry/exit counts for EFI world cross-overs match */ - __efi_exit_check(); + EFI_EXIT(exit_status); /* * But longjmp out with the U-Boot gd, not the application's, as @@ -1451,7 +1637,7 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, * @image_handle handle of the image to be unloaded * @return status code */ -static efi_status_t EFIAPI efi_unload_image(void *image_handle) +static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle) { struct efi_object *efiobj; @@ -1479,33 +1665,43 @@ static void efi_exit_caches(void) } /* - * Stop boot services. + * Stop all boot services. * * This function implements the ExitBootServices service. * See the Unified Extensible Firmware Interface (UEFI) specification * for details. * + * All timer events are disabled. + * For exit boot services events the notification function is called. + * The boot services are disabled in the system table. + * * @image_handle handle of the loaded image * @map_key key of the memory map * @return status code */ -static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, +static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, unsigned long map_key) { int i; EFI_ENTRY("%p, %ld", image_handle, map_key); + /* Make sure that notification functions are not called anymore */ + efi_tpl = TPL_HIGH_LEVEL; + + /* Check if ExitBootServices has already been called */ + if (!systab.boottime) + return EFI_EXIT(EFI_SUCCESS); + /* Notify that ExitBootServices is invoked. */ for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (efi_events[i].type != EVT_SIGNAL_EXIT_BOOT_SERVICES) continue; - efi_signal_event(&efi_events[i]); + efi_events[i].is_signaled = true; + efi_signal_event(&efi_events[i], false); } - /* Make sure that notification functions are not called anymore */ - efi_tpl = TPL_HIGH_LEVEL; - /* XXX Should persist EFI variables here */ + /* TODO Should persist EFI variables here */ board_quiesce_devices(); @@ -1515,6 +1711,20 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, /* This stops all lingering devices */ bootm_disable_interrupts(); + /* Disable boottime services */ + systab.con_in_handle = NULL; + systab.con_in = NULL; + systab.con_out_handle = NULL; + systab.con_out = NULL; + systab.stderr_handle = NULL; + systab.std_err = NULL; + systab.boottime = NULL; + + /* Recalculate CRC32 */ + systab.hdr.crc32 = 0; + systab.hdr.crc32 = crc32(0, (const unsigned char *)&systab, + sizeof(struct efi_system_table)); + /* Give the payload some time to boot */ efi_set_watchdog(0); WATCHDOG_RESET(); @@ -1581,51 +1791,6 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, } /* - * Connect a controller to a driver. - * - * This function implements the ConnectController service. - * See the Unified Extensible Firmware Interface (UEFI) specification - * for details. - * - * @controller_handle handle of the controller - * @driver_image_handle handle of the driver - * @remain_device_path device path of a child controller - * @recursive true to connect all child controllers - * @return status code - */ -static efi_status_t EFIAPI efi_connect_controller( - efi_handle_t controller_handle, - efi_handle_t *driver_image_handle, - struct efi_device_path *remain_device_path, - bool recursive) -{ - EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, - remain_device_path, recursive); - return EFI_EXIT(EFI_NOT_FOUND); -} - -/* - * Disconnect a controller from a driver. - * - * This function implements the DisconnectController service. - * See the Unified Extensible Firmware Interface (UEFI) specification - * for details. - * - * @controller_handle handle of the controller - * @driver_image_handle handle of the driver - * @child_handle handle of the child to destroy - * @return status code - */ -static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, - void *driver_image_handle, - void *child_handle) -{ - EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, - child_handle); - return EFI_EXIT(EFI_INVALID_PARAMETER); -} - -/* * Close a protocol. * * This function implements the CloseProtocol service. @@ -1638,14 +1803,38 @@ static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, * @controller_handle handle of the controller * @return status code */ -static efi_status_t EFIAPI efi_close_protocol(void *handle, +static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, const efi_guid_t *protocol, - void *agent_handle, - void *controller_handle) + efi_handle_t agent_handle, + efi_handle_t controller_handle) { + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_item *pos; + efi_status_t r; + EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, agent_handle, controller_handle); - return EFI_EXIT(EFI_NOT_FOUND); + + if (!agent_handle) { + r = EFI_INVALID_PARAMETER; + goto out; + } + r = efi_search_protocol(handle, protocol, &handler); + if (r != EFI_SUCCESS) + goto out; + + r = EFI_NOT_FOUND; + list_for_each_entry_safe(item, pos, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle && + item->info.controller_handle == controller_handle) { + efi_delete_open_info(item); + r = EFI_SUCCESS; + break; + } + } +out: + return EFI_EXIT(r); } /* @@ -1666,9 +1855,49 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, struct efi_open_protocol_info_entry **entry_buffer, efi_uintn_t *entry_count) { + unsigned long buffer_size; + unsigned long count; + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_status_t r; + EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, entry_buffer, entry_count); - return EFI_EXIT(EFI_NOT_FOUND); + + /* Check parameters */ + if (!entry_buffer) { + r = EFI_INVALID_PARAMETER; + goto out; + } + r = efi_search_protocol(handle, protocol, &handler); + if (r != EFI_SUCCESS) + goto out; + + /* Count entries */ + count = 0; + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.open_count) + ++count; + } + *entry_count = count; + *entry_buffer = NULL; + if (!count) { + r = EFI_SUCCESS; + goto out; + } + + /* Copy entries */ + buffer_size = count * sizeof(struct efi_open_protocol_info_entry); + r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, buffer_size, + (void **)entry_buffer); + if (r != EFI_SUCCESS) + goto out; + list_for_each_entry_reverse(item, &handler->open_infos, link) { + if (item->info.open_count) + (*entry_buffer)[--count] = item->info; + } +out: + return EFI_EXIT(r); } /* @@ -1683,8 +1912,8 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, * @protocol_buffer_count number of entries in the buffer * @return status code */ -static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, - efi_guid_t ***protocol_buffer, +static efi_status_t EFIAPI efi_protocols_per_handle( + efi_handle_t handle, efi_guid_t ***protocol_buffer, efi_uintn_t *protocol_buffer_count) { unsigned long buffer_size; @@ -1774,7 +2003,7 @@ static efi_status_t EFIAPI efi_locate_handle_buffer( r = efi_locate_handle(search_type, protocol, search_key, &buffer_size, *buffer); if (r == EFI_SUCCESS) - *no_handles = buffer_size / sizeof(void *); + *no_handles = buffer_size / sizeof(efi_handle_t); out: return EFI_EXIT(r); } @@ -2071,6 +2300,101 @@ static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value) /* * Open protocol interface on a handle. * + * @handler handler of a protocol + * @protocol_interface interface implementing the protocol + * @agent_handle handle of the driver + * @controller_handle handle of the controller + * @attributes attributes indicating how to open the protocol + * @return status code + */ +static efi_status_t efi_protocol_open( + struct efi_handler *handler, + void **protocol_interface, void *agent_handle, + void *controller_handle, uint32_t attributes) +{ + struct efi_open_protocol_info_item *item; + struct efi_open_protocol_info_entry *match = NULL; + bool opened_by_driver = false; + bool opened_exclusive = false; + + /* If there is no agent, only return the interface */ + if (!agent_handle) + goto out; + + /* For TEST_PROTOCOL ignore interface attribute */ + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + *protocol_interface = NULL; + + /* + * Check if the protocol is already opened by a driver with the same + * attributes or opened exclusively + */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle) { + if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) && + (item->info.attributes == attributes)) + return EFI_ALREADY_STARTED; + } + if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) + opened_exclusive = true; + } + + /* Only one controller can open the protocol exclusively */ + if (opened_exclusive && attributes & + (EFI_OPEN_PROTOCOL_EXCLUSIVE | EFI_OPEN_PROTOCOL_BY_DRIVER)) + return EFI_ACCESS_DENIED; + + /* Prepare exclusive opening */ + if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) { + /* Try to disconnect controllers */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes == + EFI_OPEN_PROTOCOL_BY_DRIVER) + EFI_CALL(efi_disconnect_controller( + item->info.controller_handle, + item->info.agent_handle, + NULL)); + } + opened_by_driver = false; + /* Check if all controllers are disconnected */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) + opened_by_driver = true; + } + /* Only one controller can be conncected */ + if (opened_by_driver) + return EFI_ACCESS_DENIED; + } + + /* Find existing entry */ + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == agent_handle && + item->info.controller_handle == controller_handle) + match = &item->info; + } + /* None found, create one */ + if (!match) { + match = efi_create_open_info(handler); + if (!match) + return EFI_OUT_OF_RESOURCES; + } + + match->agent_handle = agent_handle; + match->controller_handle = controller_handle; + match->attributes = attributes; + match->open_count++; + +out: + /* For TEST_PROTOCOL ignore interface attribute. */ + if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) + *protocol_interface = handler->protocol_interface; + + return EFI_SUCCESS; +} + +/* + * Open protocol interface on a handle. + * * This function implements the OpenProtocol interface. * See the Unified Extensible Firmware Interface (UEFI) specification * for details. @@ -2109,12 +2433,16 @@ static efi_status_t EFIAPI efi_open_protocol( case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER: if (controller_handle == handle) goto out; + /* fall-through */ case EFI_OPEN_PROTOCOL_BY_DRIVER: case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE: - if (controller_handle == NULL) + /* Check that the controller handle is valid */ + if (!efi_search_obj(controller_handle)) goto out; + /* fall-through */ case EFI_OPEN_PROTOCOL_EXCLUSIVE: - if (agent_handle == NULL) + /* Check that the agent handle is valid */ + if (!efi_search_obj(agent_handle)) goto out; break; default: @@ -2125,8 +2453,8 @@ static efi_status_t EFIAPI efi_open_protocol( if (r != EFI_SUCCESS) goto out; - if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL) - *protocol_interface = handler->protocol_interface; + r = efi_protocol_open(handler, protocol_interface, agent_handle, + controller_handle, attributes); out: return EFI_EXIT(r); } @@ -2143,7 +2471,7 @@ out: * @protocol_interface interface implementing the protocol * @return status code */ -static efi_status_t EFIAPI efi_handle_protocol(void *handle, +static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, const efi_guid_t *protocol, void **protocol_interface) { @@ -2151,6 +2479,321 @@ static efi_status_t EFIAPI efi_handle_protocol(void *handle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); } +static efi_status_t efi_bind_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + struct efi_device_path *remain_device_path) +{ + struct efi_driver_binding_protocol *binding_protocol; + efi_status_t r; + + r = EFI_CALL(efi_open_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + (void **)&binding_protocol, + driver_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (r != EFI_SUCCESS) + return r; + r = EFI_CALL(binding_protocol->supported(binding_protocol, + controller_handle, + remain_device_path)); + if (r == EFI_SUCCESS) + r = EFI_CALL(binding_protocol->start(binding_protocol, + controller_handle, + remain_device_path)); + EFI_CALL(efi_close_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + driver_image_handle, NULL)); + return r; +} + +static efi_status_t efi_connect_single_controller( + efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path) +{ + efi_handle_t *buffer; + size_t count; + size_t i; + efi_status_t r; + size_t connected = 0; + + /* Get buffer with all handles with driver binding protocol */ + r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, + &efi_guid_driver_binding_protocol, + NULL, &count, &buffer)); + if (r != EFI_SUCCESS) + return r; + + /* Context Override */ + if (driver_image_handle) { + for (; *driver_image_handle; ++driver_image_handle) { + for (i = 0; i < count; ++i) { + if (buffer[i] == *driver_image_handle) { + buffer[i] = NULL; + r = efi_bind_controller( + controller_handle, + *driver_image_handle, + remain_device_path); + /* + * For drivers that do not support the + * controller or are already connected + * we receive an error code here. + */ + if (r == EFI_SUCCESS) + ++connected; + } + } + } + } + + /* + * TODO: Some overrides are not yet implemented: + * - Platform Driver Override + * - Driver Family Override Search + * - Bus Specific Driver Override + */ + + /* Driver Binding Search */ + for (i = 0; i < count; ++i) { + if (buffer[i]) { + r = efi_bind_controller(controller_handle, + buffer[i], + remain_device_path); + if (r == EFI_SUCCESS) + ++connected; + } + } + + efi_free_pool(buffer); + if (!connected) + return EFI_NOT_FOUND; + return EFI_SUCCESS; +} + +/* + * Connect a controller to a driver. + * + * This function implements the ConnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * First all driver binding protocol handles are tried for binding drivers. + * Afterwards all handles that have openened a protocol of the controller + * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers. + * + * @controller_handle handle of the controller + * @driver_image_handle handle of the driver + * @remain_device_path device path of a child controller + * @recursive true to connect all child controllers + * @return status code + */ +static efi_status_t EFIAPI efi_connect_controller( + efi_handle_t controller_handle, + efi_handle_t *driver_image_handle, + struct efi_device_path *remain_device_path, + bool recursive) +{ + efi_status_t r; + efi_status_t ret = EFI_NOT_FOUND; + struct efi_object *efiobj; + + EFI_ENTRY("%p, %p, %p, %d", controller_handle, driver_image_handle, + remain_device_path, recursive); + + efiobj = efi_search_obj(controller_handle); + if (!efiobj) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + r = efi_connect_single_controller(controller_handle, + driver_image_handle, + remain_device_path); + if (r == EFI_SUCCESS) + ret = EFI_SUCCESS; + if (recursive) { + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) { + r = EFI_CALL(efi_connect_controller( + item->info.controller_handle, + driver_image_handle, + remain_device_path, + recursive)); + if (r == EFI_SUCCESS) + ret = EFI_SUCCESS; + } + } + } + } + /* Check for child controller specified by end node */ + if (ret != EFI_SUCCESS && remain_device_path && + remain_device_path->type == DEVICE_PATH_TYPE_END) + ret = EFI_SUCCESS; +out: + return EFI_EXIT(ret); +} + +/* + * Get all child controllers associated to a driver. + * The allocated buffer has to be freed with free(). + * + * @efiobj handle of the controller + * @driver_handle handle of the driver + * @number_of_children number of child controllers + * @child_handle_buffer handles of the the child controllers + */ +static efi_status_t efi_get_child_controllers( + struct efi_object *efiobj, + efi_handle_t driver_handle, + efi_uintn_t *number_of_children, + efi_handle_t **child_handle_buffer) +{ + struct efi_handler *handler; + struct efi_open_protocol_info_item *item; + efi_uintn_t count = 0, i; + bool duplicate; + + /* Count all child controller associations */ + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == driver_handle && + item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + ++count; + } + } + /* + * Create buffer. In case of duplicate child controller assignments + * the buffer will be too large. But that does not harm. + */ + *number_of_children = 0; + *child_handle_buffer = calloc(count, sizeof(efi_handle_t)); + if (!*child_handle_buffer) + return EFI_OUT_OF_RESOURCES; + /* Copy unique child handles */ + list_for_each_entry(handler, &efiobj->protocols, link) { + list_for_each_entry(item, &handler->open_infos, link) { + if (item->info.agent_handle == driver_handle && + item->info.attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) { + /* Check this is a new child controller */ + duplicate = false; + for (i = 0; i < *number_of_children; ++i) { + if ((*child_handle_buffer)[i] == + item->info.controller_handle) + duplicate = true; + } + /* Copy handle to buffer */ + if (!duplicate) { + i = (*number_of_children)++; + (*child_handle_buffer)[i] = + item->info.controller_handle; + } + } + } + } + return EFI_SUCCESS; +} + +/* + * Disconnect a controller from a driver. + * + * This function implements the DisconnectController service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @controller_handle handle of the controller + * @driver_image_handle handle of the driver + * @child_handle handle of the child to destroy + * @return status code + */ +static efi_status_t EFIAPI efi_disconnect_controller( + efi_handle_t controller_handle, + efi_handle_t driver_image_handle, + efi_handle_t child_handle) +{ + struct efi_driver_binding_protocol *binding_protocol; + efi_handle_t *child_handle_buffer = NULL; + size_t number_of_children = 0; + efi_status_t r; + size_t stop_count = 0; + struct efi_object *efiobj; + + EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle, + child_handle); + + efiobj = efi_search_obj(controller_handle); + if (!efiobj) { + r = EFI_INVALID_PARAMETER; + goto out; + } + + if (child_handle && !efi_search_obj(child_handle)) { + r = EFI_INVALID_PARAMETER; + goto out; + } + + /* If no driver handle is supplied, disconnect all drivers */ + if (!driver_image_handle) { + r = efi_disconnect_all_drivers(efiobj, NULL, child_handle); + goto out; + } + + /* Create list of child handles */ + if (child_handle) { + number_of_children = 1; + child_handle_buffer = &child_handle; + } else { + efi_get_child_controllers(efiobj, + driver_image_handle, + &number_of_children, + &child_handle_buffer); + } + + /* Get the driver binding protocol */ + r = EFI_CALL(efi_open_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + (void **)&binding_protocol, + driver_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL)); + if (r != EFI_SUCCESS) + goto out; + /* Remove the children */ + if (number_of_children) { + r = EFI_CALL(binding_protocol->stop(binding_protocol, + controller_handle, + number_of_children, + child_handle_buffer)); + if (r == EFI_SUCCESS) + ++stop_count; + } + /* Remove the driver */ + if (!child_handle) + r = EFI_CALL(binding_protocol->stop(binding_protocol, + controller_handle, + 0, NULL)); + if (r == EFI_SUCCESS) + ++stop_count; + EFI_CALL(efi_close_protocol(driver_image_handle, + &efi_guid_driver_binding_protocol, + driver_image_handle, NULL)); + + if (stop_count) + r = EFI_SUCCESS; + else + r = EFI_NOT_FOUND; +out: + if (!child_handle) + free(child_handle_buffer); + return EFI_EXIT(r); +} + static const struct efi_boot_services efi_boot_services = { .hdr = { .headersize = sizeof(struct efi_table_hdr), @@ -2201,8 +2844,7 @@ static const struct efi_boot_services efi_boot_services = { }; -static uint16_t __efi_runtime_data firmware_vendor[] = - { 'D','a','s',' ','U','-','b','o','o','t',0 }; +static uint16_t __efi_runtime_data firmware_vendor[] = L"Das U-Boot"; struct efi_system_table __efi_runtime_data systab = { .hdr = { diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 98497db612..28d63635ec 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -482,18 +482,26 @@ static void EFIAPI efi_key_notify(struct efi_event *event, void *context) { } +/* + * Notification function of the console timer event. + * + * 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); + + /* 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); - } + efi_signal_event(efi_con_in.wait_for_key, true); + } EFI_EXIT(EFI_SUCCESS); } - /* This gets called from do_bootefi_exec(). */ int efi_console_register(void) { @@ -503,21 +511,21 @@ int efi_console_register(void) struct efi_object *efi_console_input_obj; /* Create handles */ - r = efi_create_handle((void **)&efi_console_control_obj); + r = efi_create_handle((efi_handle_t *)&efi_console_control_obj); if (r != EFI_SUCCESS) goto out_of_memory; r = efi_add_protocol(efi_console_control_obj->handle, &efi_guid_console_control, &efi_console_control); if (r != EFI_SUCCESS) goto out_of_memory; - r = efi_create_handle((void **)&efi_console_output_obj); + 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; - r = efi_create_handle((void **)&efi_console_input_obj); + 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, diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ccb59337f1..ecc4eda3f8 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -6,6 +6,8 @@ * SPDX-License-Identifier: GPL-2.0+ */ +#define LOG_CATEGORY LOGL_ERR + #include <common.h> #include <blk.h> #include <dm.h> @@ -58,8 +60,11 @@ static void *dp_alloc(size_t sz) { void *buf; - if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != EFI_SUCCESS) + if (efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, sz, &buf) != + EFI_SUCCESS) { + debug("EFI: ERROR: out of memory in %s\n", __func__); return NULL; + } return buf; } @@ -108,7 +113,6 @@ int efi_dp_match(const struct efi_device_path *a, } } - /* * See UEFI spec (section 3.1.2, about short-form device-paths.. * tl;dr: we can have a device-path that starts with a USB WWID @@ -181,7 +185,6 @@ static struct efi_object *find_obj(struct efi_device_path *dp, bool short_path, return NULL; } - /* * Find an efiobj from device-path, if 'rem' is not NULL, returns the * remaining part of the device path after the matched object. @@ -205,6 +208,26 @@ struct efi_object *efi_dp_find_obj(struct efi_device_path *dp, return efiobj; } +/* + * Determine the last device path node that is not the end node. + * + * @dp device path + * @return last node before the end node if it exists + * otherwise NULL + */ +const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp) +{ + struct efi_device_path *ret; + + if (!dp || dp->type == DEVICE_PATH_TYPE_END) + return NULL; + while (dp) { + ret = (struct efi_device_path *)dp; + dp = efi_dp_next(dp); + } + return ret; +} + /* return size not including End node: */ unsigned efi_dp_size(const struct efi_device_path *dp) { @@ -227,6 +250,8 @@ struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) return NULL; ndp = dp_alloc(sz); + if (!ndp) + return NULL; memcpy(ndp, dp, sz); return ndp; @@ -246,6 +271,8 @@ struct efi_device_path *efi_dp_append(const struct efi_device_path *dp1, unsigned sz1 = efi_dp_size(dp1); unsigned sz2 = efi_dp_size(dp2); void *p = dp_alloc(sz1 + sz2 + sizeof(END)); + if (!p) + return NULL; memcpy(p, dp1, sz1); memcpy(p + sz1, dp2, sz2); memcpy(p + sz1 + sz2, &END, sizeof(END)); @@ -267,6 +294,8 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, } else if (!dp) { unsigned sz = node->length; void *p = dp_alloc(sz + sizeof(END)); + if (!p) + return NULL; memcpy(p, node, sz); memcpy(p + sz, &END, sizeof(END)); ret = p; @@ -274,6 +303,8 @@ struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, /* both dp and node are non-null */ unsigned sz = efi_dp_size(dp); void *p = dp_alloc(sz + node->length + sizeof(END)); + if (!p) + return NULL; memcpy(p, dp, sz); memcpy(p + sz, node, node->length); memcpy(p + sz + node->length, &END, sizeof(END)); @@ -297,9 +328,36 @@ static unsigned dp_size(struct udevice *dev) case UCLASS_SIMPLE_BUS: /* stop traversing parents at this point: */ return sizeof(ROOT); + case UCLASS_ETH: + return dp_size(dev->parent) + + sizeof(struct efi_device_path_mac_addr); +#ifdef CONFIG_BLK + case UCLASS_BLK: + switch (dev->parent->uclass->uc_drv->id) { +#ifdef CONFIG_IDE + case UCLASS_IDE: + return dp_size(dev->parent) + + sizeof(struct efi_device_path_atapi); +#endif +#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) + case UCLASS_SCSI: + return dp_size(dev->parent) + + sizeof(struct efi_device_path_scsi); +#endif +#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) + case UCLASS_MMC: + return dp_size(dev->parent) + + sizeof(struct efi_device_path_sd_mmc_path); +#endif + default: + return dp_size(dev->parent); + } +#endif +#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) case UCLASS_MMC: return dp_size(dev->parent) + sizeof(struct efi_device_path_sd_mmc_path); +#endif case UCLASS_MASS_STORAGE: case UCLASS_USB_HUB: return dp_size(dev->parent) + @@ -310,6 +368,13 @@ static unsigned dp_size(struct udevice *dev) } } +/* + * Recursively build a device path. + * + * @buf pointer to the end of the device path + * @dev device + * @return pointer to the end of the device path + */ static void *dp_fill(void *buf, struct udevice *dev) { if (!dev || !dev->driver) @@ -323,6 +388,79 @@ static void *dp_fill(void *buf, struct udevice *dev) *vdp = ROOT; return &vdp[1]; } +#ifdef CONFIG_DM_ETH + case UCLASS_ETH: { + struct efi_device_path_mac_addr *dp = + dp_fill(buf, dev->parent); + struct eth_pdata *pdata = dev->platdata; + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; + dp->dp.length = sizeof(*dp); + memset(&dp->mac, 0, sizeof(dp->mac)); + /* We only support IPv4 */ + memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN); + /* Ethernet */ + dp->if_type = 1; + return &dp[1]; + } +#endif +#ifdef CONFIG_BLK + case UCLASS_BLK: + switch (dev->parent->uclass->uc_drv->id) { +#ifdef CONFIG_IDE + case UCLASS_IDE: { + struct efi_device_path_atapi *dp = + dp_fill(buf, dev->parent); + struct blk_desc *desc = dev_get_uclass_platdata(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; + dp->dp.length = sizeof(*dp); + dp->logical_unit_number = desc->devnum; + dp->primary_secondary = IDE_BUS(desc->devnum); + dp->slave_master = desc->devnum % + (CONFIG_SYS_IDE_MAXDEVICE / + CONFIG_SYS_IDE_MAXBUS); + return &dp[1]; + } +#endif +#if defined(CONFIG_SCSI) && defined(CONFIG_DM_SCSI) + case UCLASS_SCSI: { + struct efi_device_path_scsi *dp = + dp_fill(buf, dev->parent); + struct blk_desc *desc = dev_get_uclass_platdata(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; + dp->dp.length = sizeof(*dp); + dp->logical_unit_number = desc->lun; + dp->target_id = desc->target; + return &dp[1]; + } +#endif +#if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) + case UCLASS_MMC: { + struct efi_device_path_sd_mmc_path *sddp = + dp_fill(buf, dev->parent); + struct blk_desc *desc = dev_get_uclass_platdata(dev); + + sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + sddp->dp.sub_type = is_sd(desc) ? + DEVICE_PATH_SUB_TYPE_MSG_SD : + DEVICE_PATH_SUB_TYPE_MSG_MMC; + sddp->dp.length = sizeof(*sddp); + sddp->slot_number = dev->seq; + return &sddp[1]; + } +#endif + default: + debug("%s(%u) %s: unhandled parent class: %s (%u)\n", + __FILE__, __LINE__, __func__, + dev->name, dev->parent->uclass->uc_drv->id); + return dp_fill(buf, dev->parent); + } +#endif #if defined(CONFIG_DM_MMC) && defined(CONFIG_MMC) case UCLASS_MMC: { struct efi_device_path_sd_mmc_path *sddp = @@ -359,7 +497,8 @@ static void *dp_fill(void *buf, struct udevice *dev) return &udp[1]; } default: - debug("unhandled device class: %s (%u)\n", + debug("%s(%u) %s: unhandled device class: %s (%u)\n", + __FILE__, __LINE__, __func__, dev->name, dev->driver->id); return dp_fill(buf, dev->parent); } @@ -371,6 +510,8 @@ struct efi_device_path *efi_dp_from_dev(struct udevice *dev) void *buf, *start; start = buf = dp_alloc(dp_size(dev) + sizeof(END)); + if (!buf) + return NULL; buf = dp_fill(buf, dev); *((struct efi_device_path *)buf) = END; @@ -383,7 +524,14 @@ static unsigned dp_part_size(struct blk_desc *desc, int part) unsigned dpsize; #ifdef CONFIG_BLK - dpsize = dp_size(desc->bdev->parent); + { + struct udevice *dev; + int ret = blk_find_device(desc->if_type, desc->devnum, &dev); + + if (ret) + dev = desc->bdev->parent; + dpsize = dp_size(dev); + } #else dpsize = sizeof(ROOT) + sizeof(struct efi_device_path_usb); #endif @@ -400,43 +548,16 @@ static unsigned dp_part_size(struct blk_desc *desc, int part) } /* - * Create a device path for a block device or one of its partitions. + * Create a device node for a block device partition. * * @buf buffer to which the device path is wirtten * @desc block device descriptor * @part partition number, 0 identifies a block device */ -static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) +static void *dp_part_node(void *buf, struct blk_desc *desc, int part) { disk_partition_t info; -#ifdef CONFIG_BLK - buf = dp_fill(buf, desc->bdev->parent); -#else - /* - * We *could* make a more accurate path, by looking at if_type - * and handling all the different cases like we do for non- - * legacy (ie CONFIG_BLK=y) case. But most important thing - * is just to have a unique device-path for if_type+devnum. - * So map things to a fictitious USB device. - */ - struct efi_device_path_usb *udp; - - memcpy(buf, &ROOT, sizeof(ROOT)); - buf += sizeof(ROOT); - - udp = buf; - udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; - udp->dp.length = sizeof(*udp); - udp->parent_port_number = desc->if_type; - udp->usb_interface = desc->devnum; - buf = &udp[1]; -#endif - - if (part == 0) /* the actual disk, not a partition */ - return buf; - part_get_info(desc, part, &info); if (desc->part_type == PART_TYPE_ISO) { @@ -491,6 +612,51 @@ static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) return buf; } +/* + * Create a device path for a block device or one of its partitions. + * + * @buf buffer to which the device path is wirtten + * @desc block device descriptor + * @part partition number, 0 identifies a block device + */ +static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) +{ +#ifdef CONFIG_BLK + { + struct udevice *dev; + int ret = blk_find_device(desc->if_type, desc->devnum, &dev); + + if (ret) + dev = desc->bdev->parent; + buf = dp_fill(buf, dev); + } +#else + /* + * We *could* make a more accurate path, by looking at if_type + * and handling all the different cases like we do for non- + * legacy (ie CONFIG_BLK=y) case. But most important thing + * is just to have a unique device-path for if_type+devnum. + * So map things to a fictitious USB device. + */ + struct efi_device_path_usb *udp; + + memcpy(buf, &ROOT, sizeof(ROOT)); + buf += sizeof(ROOT); + + udp = buf; + udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; + udp->dp.length = sizeof(*udp); + udp->parent_port_number = desc->if_type; + udp->usb_interface = desc->devnum; + buf = &udp[1]; +#endif + + if (part == 0) /* the actual disk, not a partition */ + return buf; + + return dp_part_node(buf, desc, part); +} /* Construct a device-path from a partition on a blk device: */ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) @@ -498,6 +664,8 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) void *buf, *start; start = buf = dp_alloc(dp_part_size(desc, part) + sizeof(END)); + if (!buf) + return NULL; buf = dp_part_fill(buf, desc, part); @@ -506,6 +674,29 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) return start; } +/* + * Create a device node for a block device partition. + * + * @buf buffer to which the device path is wirtten + * @desc block device descriptor + * @part partition number, 0 identifies a block device + */ +struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part) +{ + efi_uintn_t dpsize; + void *buf; + + if (desc->part_type == PART_TYPE_ISO) + dpsize = sizeof(struct efi_device_path_cdrom_path); + else + dpsize = sizeof(struct efi_device_path_hard_drive_path); + buf = dp_alloc(dpsize); + + dp_part_node(buf, desc, part); + + return buf; +} + /* convert path to an UEFI style path (ie. DOS style backslashes and utf16) */ static void path_to_uefi(u16 *uefi, const char *path) { @@ -536,6 +727,8 @@ struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, dpsize += fpsize; start = buf = dp_alloc(dpsize + sizeof(END)); + if (!buf) + return NULL; if (desc) buf = dp_part_fill(buf, desc, part); @@ -570,6 +763,8 @@ struct efi_device_path *efi_dp_from_eth(void) dpsize += sizeof(*ndp); start = buf = dp_alloc(dpsize + sizeof(END)); + if (!buf) + return NULL; #ifdef CONFIG_DM_ETH buf = dp_fill(buf, eth_get_dev()); @@ -600,6 +795,8 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, void *buf, *start; start = buf = dp_alloc(sizeof(*mdp) + sizeof(END)); + if (!buf) + return NULL; mdp = buf; mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; @@ -619,22 +816,31 @@ struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, * Helper to split a full device path (containing both device and file * parts) into it's constituent parts. */ -void efi_dp_split_file_path(struct efi_device_path *full_path, - struct efi_device_path **device_path, - struct efi_device_path **file_path) +efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, + struct efi_device_path **device_path, + struct efi_device_path **file_path) { struct efi_device_path *p, *dp, *fp; + *device_path = NULL; + *file_path = NULL; dp = efi_dp_dup(full_path); + if (!dp) + return EFI_OUT_OF_RESOURCES; p = dp; - while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) + while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) { p = efi_dp_next(p); + if (!p) + return EFI_OUT_OF_RESOURCES; + } fp = efi_dp_dup(p); - + if (!fp) + return EFI_OUT_OF_RESOURCES; p->type = DEVICE_PATH_TYPE_END; p->sub_type = DEVICE_PATH_SUB_TYPE_END; p->length = sizeof(*p); *device_path = dp; *file_path = fp; + return EFI_SUCCESS; } diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 50d9e911c0..a79e60a4ee 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -87,6 +87,20 @@ static char *dp_acpi(char *s, struct efi_device_path *dp) static char *dp_msging(char *s, struct efi_device_path *dp) { switch (dp->sub_type) { + case DEVICE_PATH_SUB_TYPE_MSG_ATAPI: { + struct efi_device_path_atapi *ide = + (struct efi_device_path_atapi *)dp; + s += sprintf(s, "Ata(%d,%d,%d)", ide->primary_secondary, + ide->slave_master, ide->logical_unit_number); + break; + } + case DEVICE_PATH_SUB_TYPE_MSG_SCSI: { + struct efi_device_path_scsi *ide = + (struct efi_device_path_scsi *)dp; + s += sprintf(s, "Scsi(%u,%u)", ide->target_id, + ide->logical_unit_number); + break; + } case DEVICE_PATH_SUB_TYPE_MSG_USB: { struct efi_device_path_usb *udp = (struct efi_device_path_usb *)dp; @@ -231,6 +245,8 @@ static char *efi_convert_single_device_node_to_text( case DEVICE_PATH_TYPE_MEDIA_DEVICE: str = dp_media(str, dp); break; + case DEVICE_PATH_TYPE_END: + break; default: str = dp_unknown(str, dp); } diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index d299fc8dea..ac39a65ee8 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -14,7 +14,7 @@ #include <part.h> #include <malloc.h> -static const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; +const efi_guid_t efi_block_io_guid = BLOCK_IO_GUID; struct efi_disk_obj { /* Generic EFI object parent class data */ @@ -91,7 +91,7 @@ static efi_status_t efi_disk_rw_blocks(struct efi_block_io *this, } static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, - u32 media_id, u64 lba, unsigned long buffer_size, + u32 media_id, u64 lba, efi_uintn_t buffer_size, void *buffer) { void *real_buffer = buffer; @@ -112,7 +112,7 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, real_buffer = efi_bounce_buffer; #endif - EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, + EFI_ENTRY("%p, %x, %" PRIx64 ", %zx, %p", this, media_id, lba, buffer_size, buffer); r = efi_disk_rw_blocks(this, media_id, lba, buffer_size, real_buffer, @@ -126,7 +126,7 @@ static efi_status_t EFIAPI efi_disk_read_blocks(struct efi_block_io *this, } static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, - u32 media_id, u64 lba, unsigned long buffer_size, + u32 media_id, u64 lba, efi_uintn_t buffer_size, void *buffer) { void *real_buffer = buffer; @@ -147,7 +147,7 @@ static efi_status_t EFIAPI efi_disk_write_blocks(struct efi_block_io *this, real_buffer = efi_bounce_buffer; #endif - EFI_ENTRY("%p, %x, %"PRIx64", %lx, %p", this, media_id, lba, + EFI_ENTRY("%p, %x, %" PRIx64 ", %zx, %p", this, media_id, lba, buffer_size, buffer); /* Populate bounce buffer if necessary */ @@ -175,49 +175,72 @@ static const struct efi_block_io block_io_disk_template = { }; /* - * Find filesystem from a device-path. The passed in path 'p' probably - * contains one or more /File(name) nodes, so the comparison stops at - * the first /File() node, and returns the pointer to that via 'rp'. - * This is mostly intended to be a helper to map a device-path to an - * efi_file_handle object. + * Get the simple file system protocol for a file device path. + * + * The full path provided is split into device part and into a file + * part. The device part is used to find the handle on which the + * simple file system protocol is installed. + * + * @full_path device path including device and file + * @return simple file system protocol */ struct efi_simple_file_system_protocol * -efi_fs_from_path(struct efi_device_path *fp) +efi_fs_from_path(struct efi_device_path *full_path) { struct efi_object *efiobj; - struct efi_disk_obj *diskobj; + struct efi_handler *handler; + struct efi_device_path *device_path; + struct efi_device_path *file_path; + efi_status_t ret; - efiobj = efi_dp_find_obj(fp, NULL); + /* Split the path into a device part and a file part */ + ret = efi_dp_split_file_path(full_path, &device_path, &file_path); + if (ret != EFI_SUCCESS) + return NULL; + efi_free_pool(file_path); + + /* Get the EFI object for the partition */ + efiobj = efi_dp_find_obj(device_path, NULL); + efi_free_pool(device_path); if (!efiobj) return NULL; - diskobj = container_of(efiobj, struct efi_disk_obj, parent); + /* Find the simple file system protocol */ + ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid, + &handler); + if (ret != EFI_SUCCESS) + return NULL; - return diskobj->volume; + /* Return the simple file system protocol for the partition */ + return handler->protocol_interface; } /* - * Create a device for a disk + * Create a handle for a partition or disk * - * @name not used + * @parent parent handle + * @dp_parent parent device path * @if_typename interface name for block device * @desc internal block device * @dev_index device index for block device * @offset offset into disk for simple partitions + * @return disk object */ -static void efi_disk_add_dev(const char *name, - const char *if_typename, - struct blk_desc *desc, - int dev_index, - lbaint_t offset, - unsigned int part) +static struct efi_disk_obj *efi_disk_add_dev( + efi_handle_t parent, + struct efi_device_path *dp_parent, + const char *if_typename, + struct blk_desc *desc, + int dev_index, + lbaint_t offset, + unsigned int part) { struct efi_disk_obj *diskobj; efi_status_t ret; /* Don't add empty devices */ if (!desc->lba) - return; + return NULL; diskobj = calloc(1, sizeof(*diskobj)); if (!diskobj) @@ -227,7 +250,14 @@ static void efi_disk_add_dev(const char *name, efi_add_handle(&diskobj->parent); /* Fill in object data */ - diskobj->dp = efi_dp_from_part(desc, part); + if (part) { + struct efi_device_path *node = efi_dp_part_node(desc, part); + + diskobj->dp = efi_dp_append_node(dp_parent, node); + efi_free_pool(node); + } else { + diskobj->dp = efi_dp_from_part(desc, part); + } diskobj->part = part; ret = efi_add_protocol(diskobj->parent.handle, &efi_block_io_guid, &diskobj->ops); @@ -242,7 +272,7 @@ static void efi_disk_add_dev(const char *name, diskobj->dp); ret = efi_add_protocol(diskobj->parent.handle, &efi_simple_file_system_protocol_guid, - &diskobj->volume); + diskobj->volume); if (ret != EFI_SUCCESS) goto out_of_memory; } @@ -261,20 +291,38 @@ static void efi_disk_add_dev(const char *name, if (part != 0) diskobj->media.logical_partition = 1; diskobj->ops.media = &diskobj->media; - return; + return diskobj; out_of_memory: printf("ERROR: Out of memory\n"); + return NULL; } -static int efi_disk_create_partitions(struct blk_desc *desc, - const char *if_typename, - int diskid, - const char *pdevname) +/* + * Create handles and protocols for the partitions of a block device + * + * @parent handle of the parent disk + * @blk_desc block device + * @if_typename interface type + * @diskid device number + * @pdevname device name + * @return number of partitions created + */ +int efi_disk_create_partitions(efi_handle_t parent, struct blk_desc *desc, + const char *if_typename, int diskid, + const char *pdevname) { int disks = 0; char devname[32] = { 0 }; /* dp->str is u16[32] long */ disk_partition_t info; int part; + struct efi_device_path *dp = NULL; + efi_status_t ret; + struct efi_handler *handler; + + /* Get the device path of the parent */ + ret = efi_search_protocol(parent, &efi_guid_device_path, &handler); + if (ret == EFI_SUCCESS) + dp = handler->protocol_interface; /* Add devices for each partition */ for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { @@ -282,7 +330,7 @@ static int efi_disk_create_partitions(struct blk_desc *desc, continue; snprintf(devname, sizeof(devname), "%s:%d", pdevname, part); - efi_disk_add_dev(devname, if_typename, desc, diskid, + efi_disk_add_dev(parent, dp, if_typename, desc, diskid, info.start, part); disks++; } @@ -303,6 +351,7 @@ static int efi_disk_create_partitions(struct blk_desc *desc, */ int efi_disk_register(void) { + struct efi_disk_obj *disk; int disks = 0; #ifdef CONFIG_BLK struct udevice *dev; @@ -311,19 +360,21 @@ int efi_disk_register(void) dev; uclass_next_device_check(&dev)) { struct blk_desc *desc = dev_get_uclass_platdata(dev); - const char *if_typename = dev->driver->name; + const char *if_typename = blk_get_if_type_name(desc->if_type); printf("Scanning disk %s...\n", dev->name); /* Add block device for the full device */ - efi_disk_add_dev(dev->name, if_typename, desc, - desc->devnum, 0, 0); - + disk = efi_disk_add_dev(NULL, NULL, if_typename, + desc, desc->devnum, 0, 0); + if (!disk) + return -ENOMEM; disks++; /* Partitions show up as block devices in EFI */ - disks += efi_disk_create_partitions(desc, if_typename, - desc->devnum, dev->name); + disks += efi_disk_create_partitions( + disk->parent.handle, desc, if_typename, + desc->devnum, dev->name); } #else int i, if_type; @@ -353,12 +404,16 @@ int efi_disk_register(void) if_typename, i); /* Add block device for the full device */ - efi_disk_add_dev(devname, if_typename, desc, i, 0, 0); + disk = efi_disk_add_dev(NULL, NULL, if_typename, desc, + i, 0, 0); + if (!disk) + return -ENOMEM; disks++; /* Partitions show up as block devices in EFI */ - disks += efi_disk_create_partitions(desc, if_typename, - i, devname); + disks += efi_disk_create_partitions( + disk->parent.handle, desc, + if_typename, i, devname); } } #endif diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index af29cc4f04..9d2214b481 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -74,6 +74,40 @@ void __weak invalidate_icache_all(void) } /* + * Determine the memory types to be used for code and data. + * + * @loaded_image_info image descriptor + * @image_type field Subsystem of the optional header for + * Windows specific field + */ +static void efi_set_code_and_data_type( + struct efi_loaded_image *loaded_image_info, + uint16_t image_type) +{ + switch (image_type) { + case IMAGE_SUBSYSTEM_EFI_APPLICATION: + loaded_image_info->image_code_type = EFI_LOADER_CODE; + loaded_image_info->image_data_type = EFI_LOADER_DATA; + break; + case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: + loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE; + loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA; + break; + case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: + case IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER: + loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE; + loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA; + break; + default: + printf("%s: invalid image type: %u\n", __func__, image_type); + /* Let's assume it is an application */ + loaded_image_info->image_code_type = EFI_LOADER_CODE; + loaded_image_info->image_data_type = EFI_LOADER_DATA; + break; + } +} + +/* * This function loads all sections from a PE binary into a newly reserved * piece of memory. On successful load it then returns the entry point for * the binary. Otherwise NULL. @@ -94,7 +128,6 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) unsigned long virt_size = 0; bool can_run_nt64 = true; bool can_run_nt32 = true; - uint16_t image_type; #if defined(CONFIG_ARM64) can_run_nt32 = false; @@ -131,55 +164,38 @@ void *efi_load_pe(void *efi, struct efi_loaded_image *loaded_image_info) IMAGE_NT_HEADERS64 *nt64 = (void *)nt; IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; image_size = opt->SizeOfImage; - efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + efi_set_code_and_data_type(loaded_image_info, opt->Subsystem); + efi_reloc = efi_alloc(virt_size, + loaded_image_info->image_code_type); if (!efi_reloc) { - printf("%s: Could not allocate %ld bytes\n", - __func__, virt_size); + printf("%s: Could not allocate %lu bytes\n", + __func__, virt_size); return NULL; } entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; - image_type = opt->Subsystem; } else if (can_run_nt32 && (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)) { IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; image_size = opt->SizeOfImage; - efi_reloc = efi_alloc(virt_size, EFI_LOADER_DATA); + efi_set_code_and_data_type(loaded_image_info, opt->Subsystem); + efi_reloc = efi_alloc(virt_size, + loaded_image_info->image_code_type); if (!efi_reloc) { - printf("%s: Could not allocate %ld bytes\n", - __func__, virt_size); + printf("%s: Could not allocate %lu bytes\n", + __func__, virt_size); return NULL; } entry = efi_reloc + opt->AddressOfEntryPoint; rel_size = opt->DataDirectory[rel_idx].Size; rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress; - image_type = opt->Subsystem; } else { printf("%s: Invalid optional header magic %x\n", __func__, nt->OptionalHeader.Magic); return NULL; } - switch (image_type) { - case IMAGE_SUBSYSTEM_EFI_APPLICATION: - loaded_image_info->image_code_type = EFI_LOADER_CODE; - loaded_image_info->image_data_type = EFI_LOADER_DATA; - break; - case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: - loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE; - loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA; - break; - case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: - case IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER: - loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE; - loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA; - break; - default: - printf("%s: invalid image type: %u\n", __func__, image_type); - break; - } - /* Load sections into RAM */ for (i = num_sections - 1; i >= 0; i--) { IMAGE_SECTION_HEADER *sec = §ions[i]; diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c index 0aa3e0881d..aaf64421a3 100644 --- a/lib/efi_loader/efi_memory.c +++ b/lib/efi_loader/efi_memory.c @@ -275,6 +275,15 @@ static uint64_t efi_find_free_memory(uint64_t len, uint64_t max_addr) return 0; } +/* + * Allocate memory pages. + * + * @type type of allocation to be performed + * @memory_type usage type of the allocated memory + * @pages number of pages to be allocated + * @memory allocated memory + * @return status code + */ efi_status_t efi_allocate_pages(int type, int memory_type, efi_uintn_t pages, uint64_t *memory) { @@ -338,6 +347,13 @@ void *efi_alloc(uint64_t len, int memory_type) return NULL; } +/* + * Free memory pages. + * + * @memory start of the memory area to be freed + * @pages number of pages to be freed + * @return status code + */ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) { uint64_t r = 0; @@ -351,8 +367,15 @@ efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages) return EFI_NOT_FOUND; } -efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, - void **buffer) +/* + * Allocate memory from pool. + * + * @pool_type type of the pool from which memory is to be allocated + * @size number of bytes to be allocated + * @buffer allocated memory + * @return status code + */ +efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, void **buffer) { efi_status_t r; efi_physical_addr_t t; @@ -375,6 +398,12 @@ efi_status_t efi_allocate_pool(int pool_type, efi_uintn_t size, return r; } +/* + * Free memory from pool. + * + * @buffer start of memory to be freed + * @return status code + */ efi_status_t efi_free_pool(void *buffer) { efi_status_t r; @@ -392,6 +421,17 @@ efi_status_t efi_free_pool(void *buffer) return r; } +/* + * Get map describing memory usage. + * + * @memory_map_size on entry the size, in bytes, of the memory map buffer, + * on exit the size of the copied memory map + * @memory_map buffer to which the memory map is written + * @map_key key for the memory map + * @descriptor_size size of an individual memory descriptor + * @descriptor_version version number of the memory descriptor structure + * @return status code + */ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size, struct efi_mem_desc *memory_map, efi_uintn_t *map_key, diff --git a/lib/efi_loader/helloworld.c b/lib/efi_loader/helloworld.c index b8c147d7f2..1ec0179226 100644 --- a/lib/efi_loader/helloworld.c +++ b/lib/efi_loader/helloworld.c @@ -14,6 +14,22 @@ #include <efi_api.h> static const efi_guid_t loaded_image_guid = LOADED_IMAGE_GUID; +static const efi_guid_t fdt_guid = EFI_FDT_GUID; +static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; + +static int hw_memcmp(const void *buf1, const void *buf2, size_t length) +{ + const u8 *pos1 = buf1; + const u8 *pos2 = buf2; + + for (; length; --length) { + if (*pos1 != *pos2) + return *pos1 - *pos2; + ++pos1; + ++pos2; + } + return 0; +} /* * Entry point of the EFI application. @@ -29,6 +45,7 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, struct efi_boot_services *boottime = systable->boottime; struct efi_loaded_image *loaded_image; efi_status_t ret; + efi_uintn_t i; con_out->output_string(con_out, L"Hello, world!\n"); @@ -40,6 +57,15 @@ efi_status_t EFIAPI efi_main(efi_handle_t handle, L"Cannot open loaded image protocol\n"); goto out; } + /* Find configuration tables */ + for (i = 0; i < systable->nr_tables; ++i) { + if (!hw_memcmp(&systable->tables[i].guid, &fdt_guid, + sizeof(efi_guid_t))) + con_out->output_string(con_out, L"Have device tree\n"); + if (!hw_memcmp(&systable->tables[i].guid, &smbios_guid, + sizeof(efi_guid_t))) + con_out->output_string(con_out, L"Have SMBIOS table\n"); + } /* Output the load options */ con_out->output_string(con_out, L"Load options: "); if (loaded_image->load_options_size && loaded_image->load_options) |