diff options
Diffstat (limited to 'lib/efi_loader')
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 788 | ||||
-rw-r--r-- | lib/efi_loader/efi_console.c | 29 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path.c | 24 | ||||
-rw-r--r-- | lib/efi_loader/efi_device_path_to_text.c | 9 | ||||
-rw-r--r-- | lib/efi_loader/efi_disk.c | 26 | ||||
-rw-r--r-- | lib/efi_loader/efi_net.c | 143 |
6 files changed, 915 insertions, 104 deletions
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9e741c3cf3..f627340de4 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -7,6 +7,7 @@ */ #include <common.h> +#include <div64.h> #include <efi_loader.h> #include <environment.h> #include <malloc.h> @@ -128,58 +129,54 @@ const char *__efi_nesting_dec(void) return indent_string(--nesting_level); } -/* Low 32 bit */ -#define EFI_LOW32(a) (a & 0xFFFFFFFFULL) -/* High 32 bit */ -#define EFI_HIGH32(a) (a >> 32) - /* - * 64bit division by 10 implemented as multiplication by 1 / 10 + * Queue an EFI event. + * + * This function queues the notification function of the event for future + * execution. + * + * The notification function is called if the task priority level of the + * event is higher than the current task priority level. + * + * For the SignalEvent service see efi_signal_event_ext. * - * Decimals of one tenth: 0x1 / 0xA = 0x0.19999... + * @event event to signal */ -#define EFI_TENTH 0x199999999999999A -static u64 efi_div10(u64 a) -{ - u64 prod; - u64 rem; - u64 ret; - - ret = EFI_HIGH32(a) * EFI_HIGH32(EFI_TENTH); - prod = EFI_HIGH32(a) * EFI_LOW32(EFI_TENTH); - rem = EFI_LOW32(prod); - ret += EFI_HIGH32(prod); - prod = EFI_LOW32(a) * EFI_HIGH32(EFI_TENTH); - rem += EFI_LOW32(prod); - ret += EFI_HIGH32(prod); - prod = EFI_LOW32(a) * EFI_LOW32(EFI_TENTH); - rem += EFI_HIGH32(prod); - ret += EFI_HIGH32(rem); - /* Round to nearest integer */ - if (rem >= (1 << 31)) - ++ret; - return ret; -} - void efi_signal_event(struct efi_event *event) { if (event->notify_function) { - event->queued = 1; + event->is_queued = true; /* Check TPL */ if (efi_tpl >= event->notify_tpl) return; EFI_CALL_VOID(event->notify_function(event, event->notify_context)); } - event->queued = 0; + event->is_queued = false; } +/* + * Write a debug message for an EPI API service that is not implemented yet. + * + * @funcname function that is not yet implemented + * @return EFI_UNSUPPORTED + */ static efi_status_t efi_unsupported(const char *funcname) { debug("EFI: App called into unimplemented function %s\n", funcname); return EFI_EXIT(EFI_UNSUPPORTED); } +/* + * Raise the task priority level. + * + * This function implements the RaiseTpl service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @new_tpl new value of the task priority level + * @return old value of the task priority level + */ static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl) { UINTN old_tpl = efi_tpl; @@ -196,6 +193,15 @@ static unsigned long EFIAPI efi_raise_tpl(UINTN new_tpl) return old_tpl; } +/* + * Lower the task priority level. + * + * This function implements the RestoreTpl service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @old_tpl value of the task priority level to be restored + */ static void EFIAPI efi_restore_tpl(UINTN old_tpl) { EFI_ENTRY("0x%zx", old_tpl); @@ -209,6 +215,19 @@ static void EFIAPI efi_restore_tpl(UINTN old_tpl) EFI_EXIT(EFI_SUCCESS); } +/* + * Allocate memory pages. + * + * This function implements the AllocatePages service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @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 + */ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type, unsigned long pages, uint64_t *memory) @@ -220,6 +239,17 @@ static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type, return EFI_EXIT(r); } +/* + * Free memory pages. + * + * This function implements the FreePages service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @memory start of the memory area to be freed + * @pages number of pages to be freed + * @return status code + */ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, unsigned long pages) { @@ -230,6 +260,21 @@ static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory, return EFI_EXIT(r); } +/* + * Get map describing memory usage. + * + * This function implements the GetMemoryMap service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @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 + */ static efi_status_t EFIAPI efi_get_memory_map_ext( unsigned long *memory_map_size, struct efi_mem_desc *memory_map, @@ -246,6 +291,18 @@ static efi_status_t EFIAPI efi_get_memory_map_ext( return EFI_EXIT(r); } +/* + * Allocate memory from pool. + * + * This function implements the AllocatePool service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @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 + */ static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type, unsigned long size, void **buffer) @@ -257,6 +314,16 @@ static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type, return EFI_EXIT(r); } +/* + * Free memory from pool. + * + * This function implements the FreePool service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @buffer start of memory to be freed + * @return status code + */ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) { efi_status_t r; @@ -266,12 +333,44 @@ static efi_status_t EFIAPI efi_free_pool_ext(void *buffer) return EFI_EXIT(r); } +static efi_status_t efi_create_handle(void **handle) +{ + struct efi_object *obj; + efi_status_t r; + + r = efi_allocate_pool(EFI_ALLOCATE_ANY_PAGES, + sizeof(struct efi_object), + (void **)&obj); + if (r != EFI_SUCCESS) + return r; + memset(obj, 0, sizeof(struct efi_object)); + obj->handle = obj; + list_add_tail(&obj->link, &efi_obj_list); + *handle = obj; + return r; +} + /* * Our event capabilities are very limited. Only a small limited * number of events is allowed to coexist. */ static struct efi_event efi_events[16]; +/* + * Create an event. + * + * This function is used inside U-Boot code to create an event. + * + * For the API function implementing the CreateEvent service see + * efi_create_event_ext. + * + * @type type of the event to create + * @notify_tpl task priority level of the event + * @notify_function notification function of the event + * @notify_context pointer passed to the notification function + * @event created event + * @return status code + */ efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( struct efi_event *event, @@ -299,14 +398,28 @@ efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl, efi_events[i].notify_context = notify_context; /* Disable timers on bootup */ efi_events[i].trigger_next = -1ULL; - efi_events[i].queued = 0; - efi_events[i].signaled = 0; + efi_events[i].is_queued = false; + efi_events[i].is_signaled = false; *event = &efi_events[i]; return EFI_SUCCESS; } return EFI_OUT_OF_RESOURCES; } +/* + * Create an event. + * + * This function implements the CreateEvent service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @type type of the event to create + * @notify_tpl task priority level of the event + * @notify_function notification function of the event + * @notify_context pointer passed to the notification function + * @event created event + * @return status code + */ static efi_status_t EFIAPI efi_create_event_ext( uint32_t type, UINTN notify_tpl, void (EFIAPI *notify_function) ( @@ -322,8 +435,11 @@ static efi_status_t EFIAPI efi_create_event_ext( /* + * Check if a timer event has occurred or a queued notification function should + * be called. + * * Our timers have to work without interrupts, so we check whenever keyboard - * input or disk accesses happen if enough time elapsed for it to fire. + * input or disk accesses happen if enough time elapsed for them to fire. */ void efi_timer_check(void) { @@ -333,7 +449,7 @@ void efi_timer_check(void) for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (!efi_events[i].type) continue; - if (efi_events[i].queued) + if (efi_events[i].is_queued) efi_signal_event(&efi_events[i]); if (!(efi_events[i].type & EVT_TIMER) || now < efi_events[i].trigger_next) @@ -349,12 +465,23 @@ void efi_timer_check(void) default: continue; } - efi_events[i].signaled = 1; + efi_events[i].is_signaled = true; efi_signal_event(&efi_events[i]); } WATCHDOG_RESET(); } +/* + * Set the trigger time for a timer event or stop the event. + * + * This is the function for internal usage in U-Boot. For the API function + * implementing the SetTimer service see efi_set_timer_ext. + * + * @event event for which the timer is set + * @type type of the timer + * @trigger_time trigger period in multiples of 100ns + * @return status code + */ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time) { @@ -364,7 +491,7 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, * The parameter defines a multiple of 100ns. * We use multiples of 1000ns. So divide by 10. */ - trigger_time = efi_div10(trigger_time); + do_div(trigger_time, 10); for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (event != &efi_events[i]) @@ -386,12 +513,24 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, } event->trigger_type = type; event->trigger_time = trigger_time; - event->signaled = 0; + event->is_signaled = false; return EFI_SUCCESS; } return EFI_INVALID_PARAMETER; } +/* + * Set the trigger time for a timer event or stop the event. + * + * This function implements the SetTimer service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @event event for which the timer is set + * @type type of the timer + * @trigger_time trigger period in multiples of 100ns + * @return status code + */ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, enum efi_timer_delay type, uint64_t trigger_time) @@ -400,9 +539,21 @@ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, return EFI_EXIT(efi_set_timer(event, type, trigger_time)); } +/* + * Wait for events to be signaled. + * + * This function implements the WaitForEvent service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @num_events number of events to be waited for + * @events events to be waited for + * @index index of the event that was signaled + * @return status code + */ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, struct efi_event **event, - unsigned long *index) + size_t *index) { int i, j; @@ -423,14 +574,14 @@ static efi_status_t EFIAPI efi_wait_for_event(unsigned long num_events, known_event: if (!event[i]->type || event[i]->type & EVT_NOTIFY_SIGNAL) return EFI_EXIT(EFI_INVALID_PARAMETER); - if (!event[i]->signaled) + if (!event[i]->is_signaled) efi_signal_event(event[i]); } /* Wait for signal */ for (;;) { for (i = 0; i < num_events; ++i) { - if (event[i]->signaled) + if (event[i]->is_signaled) goto out; } /* Allow events to occur. */ @@ -442,13 +593,26 @@ out: * Reset the signal which is passed to the caller to allow periodic * events to occur. */ - event[i]->signaled = 0; + event[i]->is_signaled = false; if (index) *index = i; return EFI_EXIT(EFI_SUCCESS); } +/* + * Signal an EFI event. + * + * This function implements the SignalEvent service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * This functions sets the signaled state of the event and queues the + * notification function for execution. + * + * @event event to signal + * @return status code + */ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) { int i; @@ -457,9 +621,9 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) for (i = 0; i < ARRAY_SIZE(efi_events); ++i) { if (event != &efi_events[i]) continue; - if (event->signaled) + if (event->is_signaled) break; - event->signaled = 1; + event->is_signaled = true; if (event->type & EVT_NOTIFY_SIGNAL) efi_signal_event(event); break; @@ -467,6 +631,16 @@ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) return EFI_EXIT(EFI_SUCCESS); } +/* + * Close an EFI event. + * + * This function implements the CloseEvent service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @event event to close + * @return status code + */ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) { int i; @@ -476,14 +650,26 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) if (event == &efi_events[i]) { event->type = 0; event->trigger_next = -1ULL; - event->queued = 0; - event->signaled = 0; + event->is_queued = false; + event->is_signaled = false; return EFI_EXIT(EFI_SUCCESS); } } return EFI_EXIT(EFI_INVALID_PARAMETER); } +/* + * Check if an event is signaled. + * + * This function implements the CheckEvent service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * If an event is not signaled yet the notification function is queued. + * + * @event event to check + * @return status code + */ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) { int i; @@ -495,17 +681,31 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) continue; if (!event->type || event->type & EVT_NOTIFY_SIGNAL) break; - if (!event->signaled) + if (!event->is_signaled) efi_signal_event(event); - if (event->signaled) + if (event->is_signaled) return EFI_EXIT(EFI_SUCCESS); return EFI_EXIT(EFI_NOT_READY); } return EFI_EXIT(EFI_INVALID_PARAMETER); } +/* + * Install protocol interface. + * + * This is the function for internal calls. For the API implementation of the + * InstallProtocolInterface service see function + * efi_install_protocol_interface_ext. + * + * @handle handle on which the protocol shall be installed + * @protocol GUID of the protocol to be installed + * @protocol_interface_type type of the interface to be installed, + * always EFI_NATIVE_INTERFACE + * @protocol_interface interface of the protocol implementation + * @return status code + */ static efi_status_t EFIAPI efi_install_protocol_interface(void **handle, - efi_guid_t *protocol, int protocol_interface_type, + const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface) { struct list_head *lhandle; @@ -520,8 +720,9 @@ static efi_status_t EFIAPI efi_install_protocol_interface(void **handle, /* Create new handle if requested. */ if (!*handle) { - r = EFI_OUT_OF_RESOURCES; - goto out; + r = efi_create_handle(handle); + if (r != EFI_SUCCESS) + goto out; } /* Find object. */ list_for_each(lhandle, &efi_obj_list) { @@ -561,8 +762,22 @@ out: return r; } +/* + * Install protocol interface. + * + * This function implements the InstallProtocolInterface service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol shall be installed + * @protocol GUID of the protocol to be installed + * @protocol_interface_type type of the interface to be installed, + * always EFI_NATIVE_INTERFACE + * @protocol_interface interface of the protocol implementation + * @return status code + */ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle, - efi_guid_t *protocol, int protocol_interface_type, + const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface) { EFI_ENTRY("%p, %pUl, %d, %p", handle, protocol, protocol_interface_type, @@ -573,8 +788,22 @@ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle, protocol_interface)); } +/* + * Reinstall protocol interface. + * + * This function implements the ReinstallProtocolInterface service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol shall be + * reinstalled + * @protocol GUID of the protocol to be installed + * @old_interface interface to be removed + * @new_interface interface to be installed + * @return status code + */ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, - efi_guid_t *protocol, void *old_interface, + const efi_guid_t *protocol, void *old_interface, void *new_interface) { EFI_ENTRY("%p, %pUl, %p, %p", handle, protocol, old_interface, @@ -582,8 +811,20 @@ static efi_status_t EFIAPI efi_reinstall_protocol_interface(void *handle, return EFI_EXIT(EFI_ACCESS_DENIED); } +/* + * Uninstall protocol interface. + * + * This is the function for internal calls. For the API implementation of the + * UninstallProtocolInterface service see function + * efi_uninstall_protocol_interface_ext. + * + * @handle handle from which the protocol shall be removed + * @protocol GUID of the protocol to be removed + * @protocol_interface interface to be removed + * @return status code + */ static efi_status_t EFIAPI efi_uninstall_protocol_interface(void *handle, - efi_guid_t *protocol, void *protocol_interface) + const efi_guid_t *protocol, void *protocol_interface) { struct list_head *lhandle; int i; @@ -623,8 +864,20 @@ out: return r; } +/* + * Uninstall protocol interface. + * + * This function implements the UninstallProtocolInterface service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle from which the protocol shall be removed + * @protocol GUID of the protocol to be removed + * @protocol_interface interface to be removed + * @return status code + */ static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle, - efi_guid_t *protocol, void *protocol_interface) + const efi_guid_t *protocol, void *protocol_interface) { EFI_ENTRY("%p, %pUl, %p", handle, protocol, protocol_interface); @@ -632,16 +885,41 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle, protocol_interface)); } -static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, - struct efi_event *event, - void **registration) +/* + * Register an event for notification when a protocol is installed. + * + * This function implements the RegisterProtocolNotify service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @protocol GUID of the protocol whose installation shall be + * notified + * @event event to be signaled upon installation of the protocol + * @registration key for retrieving the registration information + * @return status code + */ +static efi_status_t EFIAPI efi_register_protocol_notify( + const efi_guid_t *protocol, + struct efi_event *event, + void **registration) { EFI_ENTRY("%pUl, %p, %p", protocol, event, registration); return EFI_EXIT(EFI_OUT_OF_RESOURCES); } +/* + * Determine if an EFI handle implements a protocol. + * + * See the documentation of the LocateHandle service in the UEFI specification. + * + * @search_type selection criterion + * @protocol GUID of the protocol + * @search_key registration key + * @efiobj handle + * @return 0 if the handle implements the protocol + */ static int efi_search(enum efi_locate_search_type search_type, - efi_guid_t *protocol, void *search_key, + const efi_guid_t *protocol, void *search_key, struct efi_object *efiobj) { int i; @@ -663,9 +941,22 @@ static int efi_search(enum efi_locate_search_type search_type, return -1; } +/* + * Locate handles implementing a protocol. + * + * This function is meant for U-Boot internal calls. For the API implementation + * of the LocateHandle service see efi_locate_handle_ext. + * + * @search_type selection criterion + * @protocol GUID of the protocol + * @search_key registration key + * @buffer_size size of the buffer to receive the handles in bytes + * @buffer buffer to receive the relevant handles + * @return status code + */ static efi_status_t efi_locate_handle( enum efi_locate_search_type search_type, - efi_guid_t *protocol, void *search_key, + const efi_guid_t *protocol, void *search_key, unsigned long *buffer_size, efi_handle_t *buffer) { struct list_head *lhandle; @@ -701,9 +992,23 @@ static efi_status_t efi_locate_handle( return EFI_SUCCESS; } +/* + * Locate handles implementing a protocol. + * + * This function implements the LocateHandle service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @search_type selection criterion + * @protocol GUID of the protocol + * @search_key registration key + * @buffer_size size of the buffer to receive the handles in bytes + * @buffer buffer to receive the relevant handles + * @return 0 if the handle implements the protocol + */ static efi_status_t EFIAPI efi_locate_handle_ext( enum efi_locate_search_type search_type, - efi_guid_t *protocol, void *search_key, + const efi_guid_t *protocol, void *search_key, unsigned long *buffer_size, efi_handle_t *buffer) { EFI_ENTRY("%d, %pUl, %p, %p, %p", search_type, protocol, search_key, @@ -713,7 +1018,20 @@ static efi_status_t EFIAPI efi_locate_handle_ext( buffer_size, buffer)); } -static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol, +/* + * Get the device path and handle of an device implementing a protocol. + * + * This function implements the LocateDevicePath service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @protocol GUID of the protocol + * @device_path device path + * @device handle of the device + * @return status code + */ +static efi_status_t EFIAPI efi_locate_device_path( + const efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device) { @@ -741,6 +1059,16 @@ static void efi_remove_configuration_table(int i) systab.nr_tables--; } +/* + * Adds, updates, or removes a configuration table. + * + * This function is used for internal calls. For the API implementation of the + * InstallConfigurationTable service see efi_install_configuration_table_ext. + * + * @guid GUID of the installed table + * @table table to be installed + * @return status code + */ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table) { int i; @@ -771,6 +1099,17 @@ efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table return EFI_SUCCESS; } +/* + * Adds, updates, or removes a configuration table. + * + * This function implements the InstallConfigurationTable service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @guid GUID of the installed table + * @table table to be installed + * @return status code + */ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, void *table) { @@ -778,8 +1117,15 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, return EFI_EXIT(efi_install_configuration_table(guid, table)); } -/* Initialize a loaded_image_info + loaded_image_info object with correct +/* + * Initialize a loaded_image_info + loaded_image_info object with correct * protocols, boot-device, etc. + * + * @info loaded image info to be passed to the entry point of the + * image + * @obj internal object associated with the loaded image + * @device_path device path of the loaded image + * @file_path file path of the loaded image */ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *obj, struct efi_device_path *device_path, @@ -809,11 +1155,19 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob (void *)&efi_device_path_to_text; info->file_path = file_path; - info->device_handle = efi_dp_find_obj(device_path, NULL); + if (device_path) + info->device_handle = efi_dp_find_obj(device_path, NULL); list_add_tail(&obj->link, &efi_obj_list); } +/* + * Load an image using a file path. + * + * @file_path the path of the image to load + * @buffer buffer containing the loaded image + * @return status code + */ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, void **buffer) { @@ -855,6 +1209,22 @@ error: return ret; } +/* + * Load an EFI image into memory. + * + * This function implements the LoadImage service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @boot_policy true for request originating from the boot manager + * @parent_image the calles's image handle + * @file_path the path of the image to load + * @source_buffer memory location from which the image is installed + * @source_size size of the memory area from which the image is + * installed + * @image_handle handle for the newly installed image + * @return status code + */ static efi_status_t EFIAPI efi_load_image(bool boot_policy, efi_handle_t parent_image, struct efi_device_path *file_path, @@ -908,6 +1278,18 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, return EFI_EXIT(EFI_SUCCESS); } +/* + * Call the entry point of an image. + * + * This function implements the StartImage service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @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 + * @return status code + */ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) @@ -936,6 +1318,19 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, return EFI_EXIT(EFI_SUCCESS); } +/* + * Leave an EFI application or driver. + * + * This function implements the Exit service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @image_handle handle of the application or driver that is exiting + * @exit_status status code + * @exit_data_size size of the buffer in bytes + * @exit_data buffer with data describing an error + * @return status code + */ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, efi_status_t exit_status, unsigned long exit_data_size, int16_t *exit_data) @@ -960,6 +1355,12 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle, panic("EFI application exited"); } +/* + * Find the internal EFI object for a handle. + * + * @handle handle to find + * @return EFI object + */ static struct efi_object *efi_search_obj(void *handle) { struct list_head *lhandle; @@ -974,6 +1375,16 @@ static struct efi_object *efi_search_obj(void *handle) return NULL; } +/* + * Unload an EFI image. + * + * This function implements the UnloadImage service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @image_handle handle of the image to be unloaded + * @return status code + */ static efi_status_t EFIAPI efi_unload_image(void *image_handle) { struct efi_object *efiobj; @@ -986,6 +1397,9 @@ static efi_status_t EFIAPI efi_unload_image(void *image_handle) return EFI_EXIT(EFI_SUCCESS); } +/* + * Fix up caches for EFI payloads if necessary. + */ static void efi_exit_caches(void) { #if defined(CONFIG_ARM) && !defined(CONFIG_ARM64) @@ -998,6 +1412,17 @@ static void efi_exit_caches(void) #endif } +/* + * Stop boot services. + * + * This function implements the ExitBootServices service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @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, unsigned long map_key) { @@ -1033,6 +1458,16 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle, return EFI_EXIT(EFI_SUCCESS); } +/* + * Get next value of the counter. + * + * This function implements the NextMonotonicCount service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @count returned value of the counter + * @return status code + */ static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) { static uint64_t mono = 0; @@ -1041,6 +1476,16 @@ static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count) return EFI_EXIT(EFI_SUCCESS); } +/* + * Sleep. + * + * This function implements the Stall sercive. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @microseconds period to sleep in microseconds + * @return status code + */ static efi_status_t EFIAPI efi_stall(unsigned long microseconds) { EFI_ENTRY("%ld", microseconds); @@ -1048,6 +1493,19 @@ static efi_status_t EFIAPI efi_stall(unsigned long microseconds) return EFI_EXIT(EFI_SUCCESS); } +/* + * Reset the watchdog timer. + * + * This function implements the WatchdogTimer service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @timeout seconds before reset by watchdog + * @watchdog_code code to be logged when resetting + * @data_size size of buffer in bytes + * @watchdog_data buffer with data describing the reset reason + * @return status code + */ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, uint64_t watchdog_code, unsigned long data_size, @@ -1058,6 +1516,19 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, return efi_unsupported(__func__); } +/* + * 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, @@ -1069,6 +1540,18 @@ static efi_status_t EFIAPI efi_connect_controller( 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) @@ -1078,8 +1561,21 @@ static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, return EFI_EXIT(EFI_INVALID_PARAMETER); } +/* + * Close a protocol. + * + * This function implements the CloseProtocol service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol shall be closed + * @protocol GUID of the protocol to close + * @agent_handle handle of the driver + * @controller_handle handle of the controller + * @return status code + */ static efi_status_t EFIAPI efi_close_protocol(void *handle, - efi_guid_t *protocol, + const efi_guid_t *protocol, void *agent_handle, void *controller_handle) { @@ -1088,8 +1584,21 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle, return EFI_EXIT(EFI_NOT_FOUND); } +/* + * Provide information about then open status of a protocol on a handle + * + * This function implements the OpenProtocolInformation service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle for which the information shall be retrieved + * @protocol GUID of the protocol + * @entry_buffer buffer to receive the open protocol information + * @entry_count number of entries available in the buffer + * @return status code + */ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, - efi_guid_t *protocol, + const efi_guid_t *protocol, struct efi_open_protocol_info_entry **entry_buffer, unsigned long *entry_count) { @@ -1098,6 +1607,18 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, return EFI_EXIT(EFI_NOT_FOUND); } +/* + * Get protocols installed on a handle. + * + * This function implements the ProtocolsPerHandleService. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle for which the information is retrieved + * @protocol_buffer buffer with protocol GUIDs + * @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, unsigned long *protocol_buffer_count) @@ -1151,9 +1672,23 @@ static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, return EFI_EXIT(EFI_SUCCESS); } +/* + * Locate handles implementing a protocol. + * + * This function implements the LocateHandleBuffer service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @search_type selection criterion + * @protocol GUID of the protocol + * @search_key registration key + * @no_handles number of returned handles + * @buffer buffer with the returned handles + * @return status code + */ static efi_status_t EFIAPI efi_locate_handle_buffer( enum efi_locate_search_type search_type, - efi_guid_t *protocol, void *search_key, + const efi_guid_t *protocol, void *search_key, unsigned long *no_handles, efi_handle_t **buffer) { efi_status_t r; @@ -1184,7 +1719,19 @@ out: return EFI_EXIT(r); } -static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, +/* + * Find an interface implementing a protocol. + * + * This function implements the LocateProtocol service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @protocol GUID of the protocol + * @registration registration key passed to the notification function + * @protocol_interface interface implementing the protocol + * @return status code + */ +static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol, void *registration, void **protocol_interface) { @@ -1219,13 +1766,25 @@ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, return EFI_EXIT(EFI_NOT_FOUND); } +/* + * Install multiple protocol interfaces. + * + * This function implements the MultipleProtocolInterfaces service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol interfaces shall be installed + * @... NULL terminated argument list with pairs of protocol GUIDS and + * interfaces + * @return status code + */ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( void **handle, ...) { EFI_ENTRY("%p", handle); va_list argptr; - efi_guid_t *protocol; + const efi_guid_t *protocol; void *protocol_interface; efi_status_t r = EFI_SUCCESS; int i = 0; @@ -1263,6 +1822,18 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( return EFI_EXIT(r); } +/* + * Uninstall multiple protocol interfaces. + * + * This function implements the UninstallMultipleProtocolInterfaces service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle from which the protocol interfaces shall be removed + * @... NULL terminated argument list with pairs of protocol GUIDS and + * interfaces + * @return status code + */ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( void *handle, ...) { @@ -1270,6 +1841,18 @@ static efi_status_t EFIAPI efi_uninstall_multiple_protocol_interfaces( return EFI_EXIT(EFI_INVALID_PARAMETER); } +/* + * Calculate cyclic redundancy code. + * + * This function implements the CalculateCrc32 service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @data buffer with data + * @data_size size of buffer in bytes + * @crc32_p cyclic redundancy code + * @return status code + */ static efi_status_t EFIAPI efi_calculate_crc32(void *data, unsigned long data_size, uint32_t *crc32_p) @@ -1279,21 +1862,60 @@ static efi_status_t EFIAPI efi_calculate_crc32(void *data, return EFI_EXIT(EFI_SUCCESS); } -static void EFIAPI efi_copy_mem(void *destination, void *source, - unsigned long length) +/* + * Copy memory. + * + * This function implements the CopyMem service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @destination destination of the copy operation + * @source source of the copy operation + * @length number of bytes to copy + */ +static void EFIAPI efi_copy_mem(void *destination, const void *source, + size_t length) { - EFI_ENTRY("%p, %p, %ld", destination, source, length); + EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length); memcpy(destination, source, length); + EFI_EXIT(EFI_SUCCESS); } -static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value) +/* + * Fill memory with a byte value. + * + * This function implements the SetMem service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @buffer buffer to fill + * @size size of buffer in bytes + * @value byte to copy to the buffer + */ +static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value) { - EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value); + EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value); memset(buffer, value, size); + EFI_EXIT(EFI_SUCCESS); } +/* + * Open protocol interface on a handle. + * + * This function implements the OpenProtocol interface. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol shall be opened + * @protocol GUID of the 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 EFIAPI efi_open_protocol( - void *handle, efi_guid_t *protocol, + void *handle, const efi_guid_t *protocol, void **protocol_interface, void *agent_handle, void *controller_handle, uint32_t attributes) { @@ -1364,8 +1986,20 @@ out: return EFI_EXIT(r); } +/* + * Get interface of a protocol on a handle. + * + * This function implements the HandleProtocol service. + * See the Unified Extensible Firmware Interface (UEFI) specification + * for details. + * + * @handle handle on which the protocol shall be opened + * @protocol GUID of the protocol + * @protocol_interface interface implementing the protocol + * @return status code + */ static efi_status_t EFIAPI efi_handle_protocol(void *handle, - efi_guid_t *protocol, + const efi_guid_t *protocol, void **protocol_interface) { return efi_open_protocol(handle, protocol, protocol_interface, NULL, diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index fd5398d61d..01732aafea 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -307,14 +307,37 @@ static efi_status_t EFIAPI efi_cout_set_mode( return EFI_EXIT(EFI_SUCCESS); } +static const struct { + unsigned int fg; + unsigned int bg; +} color[] = { + { 30, 40 }, /* 0: black */ + { 34, 44 }, /* 1: blue */ + { 32, 42 }, /* 2: green */ + { 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 */ +}; + +/* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */ static efi_status_t EFIAPI efi_cout_set_attribute( struct efi_simple_text_output_protocol *this, unsigned long attribute) { + unsigned int bold = EFI_ATTR_BOLD(attribute); + unsigned int fg = EFI_ATTR_FG(attribute); + unsigned int bg = EFI_ATTR_BG(attribute); + EFI_ENTRY("%p, %lx", this, attribute); - /* Just ignore attributes (colors) for now */ - return EFI_EXIT(EFI_UNSUPPORTED); + if (attribute) + printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg); + else + printf(ESC"[0;37;40m"); + + return EFI_EXIT(EFI_SUCCESS); } static efi_status_t EFIAPI efi_cout_clear_screen( @@ -460,7 +483,7 @@ static void EFIAPI efi_console_timer_notify(struct efi_event *event, { EFI_ENTRY("%p, %p", event, context); if (tstc()) { - efi_con_in.wait_for_key->signaled = 1; + efi_con_in.wait_for_key->is_signaled = true; efi_signal_event(efi_con_in.wait_for_key); } EFI_EXIT(EFI_SUCCESS); diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 5d5c3b3464..f6e368e029 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -538,6 +538,30 @@ struct efi_device_path *efi_dp_from_eth(void) } #endif +/* Construct a device-path for memory-mapped image */ +struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, + uint64_t start_address, + uint64_t end_address) +{ + struct efi_device_path_memory *mdp; + void *buf, *start; + + start = buf = dp_alloc(sizeof(*mdp) + sizeof(END)); + + mdp = buf; + mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY; + mdp->dp.length = sizeof(*mdp); + mdp->memory_type = memory_type; + mdp->start_address = start_address; + mdp->end_address = end_address; + buf = &mdp[1]; + + *((struct efi_device_path *)buf) = END; + + return start; +} + /* * Helper to split a full device path (containing both device and file * parts) into it's constituent parts. diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 1a5ef3919b..62771338f0 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -24,6 +24,15 @@ static char *dp_unknown(char *s, struct efi_device_path *dp) static char *dp_hardware(char *s, struct efi_device_path *dp) { switch (dp->sub_type) { + case DEVICE_PATH_SUB_TYPE_MEMORY: { + struct efi_device_path_memory *mdp = + (struct efi_device_path_memory *)dp; + s += sprintf(s, "/MemoryMapped(0x%x,0x%llx,0x%llx)", + mdp->memory_type, + mdp->start_address, + mdp->end_address); + break; + } case DEVICE_PATH_SUB_TYPE_VENDOR: { struct efi_device_path_vendor *vdp = (struct efi_device_path_vendor *)dp; diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index eb9ce772d1..e61dbc8058 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -254,18 +254,19 @@ static int efi_disk_create_eltorito(struct blk_desc *desc, #if CONFIG_IS_ENABLED(ISO_PARTITION) char devname[32] = { 0 }; /* dp->str is u16[32] long */ disk_partition_t info; - int part = 1; + int part; if (desc->part_type != PART_TYPE_ISO) return 0; /* and devices for each partition: */ - while (!part_get_info(desc, part, &info)) { + for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { + if (part_get_info(desc, part, &info)) + continue; snprintf(devname, sizeof(devname), "%s:%d", pdevname, part); efi_disk_add_dev(devname, if_typename, desc, diskid, info.start, part); - part++; disks++; } @@ -299,15 +300,16 @@ int efi_disk_register(void) struct blk_desc *desc = dev_get_uclass_platdata(dev); const char *if_typename = dev->driver->name; disk_partition_t info; - int part = 1; + int part; printf("Scanning disk %s...\n", dev->name); /* add devices for each partition: */ - while (!part_get_info(desc, part, &info)) { + for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { + if (part_get_info(desc, part, &info)) + continue; efi_disk_add_dev(dev->name, if_typename, desc, desc->devnum, 0, part); - part++; } /* ... and add block device: */ @@ -340,6 +342,8 @@ int efi_disk_register(void) for (i = 0; i < 4; i++) { struct blk_desc *desc; char devname[32] = { 0 }; /* dp->str is u16[32] long */ + disk_partition_t info; + int part; desc = blk_get_devnum_by_type(if_type, i); if (!desc) @@ -349,6 +353,16 @@ int efi_disk_register(void) snprintf(devname, sizeof(devname), "%s%d", if_typename, i); + + /* add devices for each partition: */ + for (part = 1; part <= MAX_SEARCH_PARTITIONS; part++) { + if (part_get_info(desc, part, &info)) + continue; + efi_disk_add_dev(devname, if_typename, desc, + i, 0, part); + } + + /* ... and add block device: */ efi_disk_add_dev(devname, if_typename, desc, i, 0, 0); disks++; diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 91f1e4a69e..432d9a99a2 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -19,6 +19,15 @@ static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID; static struct efi_pxe_packet *dhcp_ack; static bool new_rx_packet; static void *new_tx_packet; +/* + * The notification function of this event is called in every timer cycle + * to check if a new network packet has been received. + */ +static struct efi_event *network_timer_event; +/* + * This event is signaled when a packet has been received. + */ +static struct efi_event *wait_for_packet; struct efi_net_obj { /* Generic EFI object parent class data */ @@ -78,9 +87,7 @@ static efi_status_t EFIAPI efi_net_receive_filters( EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable, reset_mcast_filter, mcast_filter_count, mcast_filter); - /* XXX Do we care? */ - - return EFI_EXIT(EFI_SUCCESS); + return EFI_EXIT(EFI_UNSUPPORTED); } static efi_status_t EFIAPI efi_net_station_address( @@ -89,7 +96,7 @@ static efi_status_t EFIAPI efi_net_station_address( { EFI_ENTRY("%p, %x, %p", this, reset, new_mac); - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(EFI_UNSUPPORTED); } static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, @@ -98,7 +105,7 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this, { EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table); - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(EFI_UNSUPPORTED); } static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, @@ -118,7 +125,7 @@ static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this, EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size, buffer); - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(EFI_UNSUPPORTED); } static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, @@ -126,9 +133,14 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, { EFI_ENTRY("%p, %p, %p", this, int_status, txbuf); - /* We send packets synchronously, so nothing is outstanding */ - if (int_status) - *int_status = 0; + efi_timer_check(); + + if (int_status) { + /* We send packets synchronously, so nothing is outstanding */ + *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + if (new_rx_packet) + *int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + } if (txbuf) *txbuf = new_tx_packet; @@ -138,12 +150,15 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this, } static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this, - ulong header_size, ulong buffer_size, void *buffer, + size_t header_size, size_t buffer_size, void *buffer, struct efi_mac_address *src_addr, struct efi_mac_address *dest_addr, u16 *protocol) { - EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size, - buffer_size, buffer, src_addr, dest_addr, protocol); + EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this, + (unsigned long)header_size, (unsigned long)buffer_size, + buffer, src_addr, dest_addr, protocol); + + efi_timer_check(); if (header_size) { /* We would need to create the header if header_size != 0 */ @@ -166,29 +181,66 @@ static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this, static void efi_net_push(void *pkt, int len) { new_rx_packet = true; + wait_for_packet->is_signaled = true; } +/* + * Receive a packet from a network interface. + * + * This function implements the Receive service of the Simple Network Protocol. + * See the UEFI spec for details. + * + * @this the instance of the Simple Network Protocol + * @header_size size of the media header + * @buffer_size size of the buffer to receive the packet + * @buffer buffer to receive the packet + * @src_addr source MAC address + * @dest_addr destination MAC address + * @protocol protocol + * @return status code + */ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, - ulong *header_size, ulong *buffer_size, void *buffer, + size_t *header_size, size_t *buffer_size, void *buffer, struct efi_mac_address *src_addr, struct efi_mac_address *dest_addr, u16 *protocol) { + struct ethernet_hdr *eth_hdr; + size_t hdr_size = sizeof(struct ethernet_hdr); + u16 protlen; + EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size, buffer_size, buffer, src_addr, dest_addr, protocol); - push_packet = efi_net_push; - eth_rx(); - push_packet = NULL; + efi_timer_check(); if (!new_rx_packet) return EFI_EXIT(EFI_NOT_READY); - + /* Check that we at least received an Ethernet header */ + if (net_rx_packet_len < sizeof(struct ethernet_hdr)) { + new_rx_packet = false; + return EFI_EXIT(EFI_NOT_READY); + } + /* Fill export parameters */ + eth_hdr = (struct ethernet_hdr *)net_rx_packet; + protlen = ntohs(eth_hdr->et_protlen); + if (protlen == 0x8100) { + hdr_size += 4; + protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]); + } + if (header_size) + *header_size = hdr_size; + if (dest_addr) + memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN); + if (src_addr) + memcpy(src_addr, eth_hdr->et_src, ARP_HLEN); + if (protocol) + *protocol = protlen; if (*buffer_size < net_rx_packet_len) { /* Packet doesn't fit, try again with bigger buf */ *buffer_size = net_rx_packet_len; return EFI_EXIT(EFI_BUFFER_TOO_SMALL); } - + /* Copy packet */ memcpy(buffer, net_rx_packet, net_rx_packet_len); *buffer_size = net_rx_packet_len; new_rx_packet = false; @@ -206,10 +258,32 @@ void efi_net_set_dhcp_ack(void *pkt, int len) memcpy(dhcp_ack, pkt, min(len, maxsize)); } +/* + * Check if a new network packet has been received. + * + * This notification function is called in every timer cycle. + * + * @event the event for which this notification function is registered + * @context event context - not used in this function + */ +static void EFIAPI efi_network_timer_notify(struct efi_event *event, + void *context) +{ + EFI_ENTRY("%p, %p", event, context); + + if (!new_rx_packet) { + push_packet = efi_net_push; + eth_rx(); + push_packet = NULL; + } + EFI_EXIT(EFI_SUCCESS); +} + /* This gets called from do_bootefi_exec(). */ int efi_net_register(void) { struct efi_net_obj *netobj; + efi_status_t r; if (!eth_get_dev()) { /* No eth device active, don't expose any */ @@ -228,6 +302,7 @@ int efi_net_register(void) netobj->parent.protocols[2].guid = &efi_pxe_guid; netobj->parent.protocols[2].protocol_interface = &netobj->pxe; netobj->parent.handle = &netobj->net; + netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; netobj->net.start = efi_net_start; netobj->net.stop = efi_net_stop; netobj->net.initialize = efi_net_initialize; @@ -244,6 +319,7 @@ int efi_net_register(void) netobj->net.mode = &netobj->net_mode; netobj->net_mode.state = EFI_NETWORK_STARTED; memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6); + netobj->net_mode.hwaddr_size = ARP_HLEN; netobj->net_mode.max_packet_size = PKTSIZE; netobj->pxe.mode = &netobj->pxe_mode; @@ -253,5 +329,36 @@ int efi_net_register(void) /* Hook net up to the device list */ list_add_tail(&netobj->parent.link, &efi_obj_list); + /* + * Create WaitForPacket event. + */ + r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, + efi_network_timer_notify, NULL, + &wait_for_packet); + if (r != EFI_SUCCESS) { + printf("ERROR: Failed to register network event\n"); + return r; + } + netobj->net.wait_for_packet = wait_for_packet; + /* + * Create a timer event. + * + * The notification function is used to check if a new network packet + * has been received. + */ + r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + efi_network_timer_notify, NULL, + &network_timer_event); + if (r != EFI_SUCCESS) { + printf("ERROR: Failed to register network event\n"); + return r; + } + /* Network is time critical, create event in every timer cyle */ + r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0); + if (r != EFI_SUCCESS) { + printf("ERROR: Failed to set network timer\n"); + return r; + } + return 0; } |