From 842a8e434e48e3d4d7a862c4a1676a698aa0954d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 27 Aug 2017 00:51:04 +0200 Subject: [PATCH 01/37] efi_loader: support 16 protocols per efi_object 8 protocols per efi_object is insufficient for iPXE. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Rob Clark Signed-off-by: Alexander Graf --- include/efi_loader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 2f081f8996..90db790001 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -112,8 +112,8 @@ struct efi_handler { struct efi_object { /* Every UEFI object is part of a global object list */ struct list_head link; - /* We support up to 8 "protocols" an object can be accessed through */ - struct efi_handler protocols[8]; + /* We support up to 16 "protocols" an object can be accessed through */ + struct efi_handler protocols[16]; /* The object spawner can either use this for data or as identifier */ void *handle; }; From 3cc6e3fe9509d2b5eee6a698126acdde4746f0c6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 27 Aug 2017 00:51:09 +0200 Subject: [PATCH 02/37] efi_loader: allow creating new handles In efi_install_protocol_interface support creating a new handle. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Tested-by: Rob Clark Reviewed-by: Rob Clark Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9e741c3cf3..c8f39b5b10 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -266,6 +266,23 @@ 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. @@ -520,8 +537,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) { From 4a59ada5e60d25ea43d3c5f2baf1860ed3c32770 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 20 Sep 2017 21:14:37 +0200 Subject: [PATCH 03/37] efi_selftest: enable CONFIG_CMD_BOOTEFI_SELFTEST The EFI selftest has been broken by a patch on efi-next. We should enable CONFIG_CMD_BOOTEFI_SELFTEST on qemu-x86_defconfig and qemu-x86_64_defconfig by default to catch this type of problem in the Travis CI tests. These systems typically have abundant memory so that enabling this option should not pose a problem. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- configs/qemu-x86_64_defconfig | 1 + configs/qemu-x86_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/configs/qemu-x86_64_defconfig b/configs/qemu-x86_64_defconfig index 67e9a45fbc..c12d530b83 100644 --- a/configs/qemu-x86_64_defconfig +++ b/configs/qemu-x86_64_defconfig @@ -64,3 +64,4 @@ CONFIG_USB_KEYBOARD=y CONFIG_FRAMEBUFFER_SET_VESA_MODE=y CONFIG_FRAMEBUFFER_VESA_MODE_111=y CONFIG_CONSOLE_SCROLL_LINES=5 +CONFIG_CMD_BOOTEFI_SELFTEST=y diff --git a/configs/qemu-x86_defconfig b/configs/qemu-x86_defconfig index 7ce97ff091..d84e1d7dc9 100644 --- a/configs/qemu-x86_defconfig +++ b/configs/qemu-x86_defconfig @@ -44,3 +44,4 @@ CONFIG_USB_KEYBOARD=y CONFIG_FRAMEBUFFER_SET_VESA_MODE=y CONFIG_FRAMEBUFFER_VESA_MODE_111=y CONFIG_CONSOLE_SCROLL_LINES=5 +CONFIG_CMD_BOOTEFI_SELFTEST=y From 037ee6f91bb06fb664d57668effdea7656edce25 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Oct 2017 12:37:02 +0200 Subject: [PATCH 04/37] efi_selftest: use efi_st_error for all error messages All error messages in the selftests should use efi_st_error. efi_st_error will print the file name and line number of the error. Splitting message texts due to lines being over 80 characters is avoided. This resolves the issue reported by Simon Glass in https://lists.denx.de/pipermail/u-boot/2017-September/307387.html Reported-by: Simon Glass Fixes: 623b3a579765 efi_selftest: provide an EFI selftest application Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c index efec832e98..ff00254c21 100644 --- a/lib/efi_selftest/efi_selftest.c +++ b/lib/efi_selftest/efi_selftest.c @@ -35,8 +35,8 @@ void efi_st_exit_boot_services(void) ret = boottime->get_memory_map(&map_size, NULL, &map_key, &desc_size, &desc_version); if (ret != EFI_BUFFER_TOO_SMALL) { - efi_st_printf("ERROR: GetMemoryMap did not return " - "EFI_BUFFER_TOO_SMALL\n"); + efi_st_error( + "GetMemoryMap did not return EFI_BUFFER_TOO_SMALL\n"); return; } /* Allocate extra space for newly allocated memory */ @@ -44,21 +44,18 @@ void efi_st_exit_boot_services(void) ret = boottime->allocate_pool(EFI_BOOT_SERVICES_DATA, map_size, (void **)&memory_map); if (ret != EFI_SUCCESS) { - efi_st_printf("ERROR: AllocatePool did not return " - "EFI_SUCCESS\n"); + efi_st_error("AllocatePool did not return EFI_SUCCESS\n"); return; } ret = boottime->get_memory_map(&map_size, memory_map, &map_key, &desc_size, &desc_version); if (ret != EFI_SUCCESS) { - efi_st_printf("ERROR: GetMemoryMap did not return " - "EFI_SUCCESS\n"); + efi_st_error("GetMemoryMap did not return EFI_SUCCESS\n"); return; } ret = boottime->exit_boot_services(handle, map_key); if (ret != EFI_SUCCESS) { - efi_st_printf("ERROR: ExitBootServices did not return " - "EFI_SUCCESS\n"); + efi_st_error("ExitBootServices did not return EFI_SUCCESS\n"); return; } efi_st_printf("\nBoot services terminated\n"); @@ -79,7 +76,7 @@ static int setup(struct efi_unit_test *test, unsigned int *failures) efi_st_printf("\nSetting up '%s'\n", test->name); ret = test->setup(handle, systable); if (ret) { - efi_st_printf("ERROR: Setting up '%s' failed\n", test->name); + efi_st_error("Setting up '%s' failed\n", test->name); ++*failures; } else { efi_st_printf("Setting up '%s' succeeded\n", test->name); @@ -102,7 +99,7 @@ static int execute(struct efi_unit_test *test, unsigned int *failures) efi_st_printf("\nExecuting '%s'\n", test->name); ret = test->execute(); if (ret) { - efi_st_printf("ERROR: Executing '%s' failed\n", test->name); + efi_st_error("Executing '%s' failed\n", test->name); ++*failures; } else { efi_st_printf("Executing '%s' succeeded\n", test->name); @@ -125,7 +122,7 @@ static int teardown(struct efi_unit_test *test, unsigned int *failures) efi_st_printf("\nTearing down '%s'\n", test->name); ret = test->teardown(); if (ret) { - efi_st_printf("ERROR: Tearing down '%s' failed\n", test->name); + efi_st_error("Tearing down '%s' failed\n", test->name); ++*failures; } else { efi_st_printf("Tearing down '%s' succeeded\n", test->name); @@ -213,7 +210,8 @@ efi_status_t EFIAPI efi_selftest(efi_handle_t image_handle, efi_st_get_key(); runtime->reset_system(EFI_RESET_WARM, EFI_NOT_READY, sizeof(reset_message), reset_message); - efi_st_printf("\nERROR: reset failed.\n"); + efi_st_printf("\n"); + efi_st_error("Reset failed.\n"); return EFI_UNSUPPORTED; } From e190e8972faf4d5b09e2a92fefc54a1832fd67da Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Oct 2017 15:03:24 +0200 Subject: [PATCH 05/37] efi_loader: use type bool for event states Queued and signaled describe boolean states of events. So let's use type bool and rename the structure members to is_queued and is_signaled. Update the comments for is_queued and is_signaled. Reported-by: Simon Glass Signed-off-by: Heinrich Schuchardt Reviewed-by: Rob Clark Reviewed-by: Rob Clark Signed-off-by: Alexander Graf --- include/efi_loader.h | 8 ++++---- lib/efi_loader/efi_boottime.c | 32 ++++++++++++++++---------------- lib/efi_loader/efi_console.c | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 90db790001..e1179b7dcd 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -136,8 +136,8 @@ struct efi_object { * @nofify_function: Function to call when the event is triggered * @notify_context: Data to be passed to the notify function * @trigger_type: Type of timer, see efi_set_timer - * @queued: The notification functionis queued - * @signaled: The event occured + * @queued: The notification function is queued + * @signaled: The event occurred. The event is in the signaled state. */ struct efi_event { uint32_t type; @@ -147,8 +147,8 @@ struct efi_event { u64 trigger_next; u64 trigger_time; enum efi_timer_delay trigger_type; - int queued; - int signaled; + bool is_queued; + bool is_signaled; }; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index c8f39b5b10..cb688e16e9 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -164,14 +164,14 @@ static u64 efi_div10(u64 a) 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; } static efi_status_t efi_unsupported(const char *funcname) @@ -316,8 +316,8 @@ 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; } @@ -350,7 +350,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) @@ -366,7 +366,7 @@ 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(); @@ -403,7 +403,7 @@ 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; @@ -440,14 +440,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. */ @@ -459,7 +459,7 @@ 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; @@ -474,9 +474,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; @@ -493,8 +493,8 @@ 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); } } @@ -512,9 +512,9 @@ 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); } diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index fd5398d61d..1bdf36b4ae 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -460,7 +460,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); From e67e7249c8000d72b4c4b985fdeb3e103d65aa9e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 4 Oct 2017 15:31:26 +0200 Subject: [PATCH 06/37] efi_selftest: make tests easier to read Rename counter to more illustrative names. Update notification function description. Simplify notification function. Add comment for arbitrary non-zero value. Document @return. Use constants for return values of setup, execute, teardown. Reported-by: Simon Glass Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_selftest.h | 3 + lib/efi_selftest/efi_selftest.c | 15 ++-- lib/efi_selftest/efi_selftest_events.c | 80 +++++++++-------- .../efi_selftest_exitbootservices.c | 46 +++++----- lib/efi_selftest/efi_selftest_tpl.c | 85 ++++++++++--------- 5 files changed, 129 insertions(+), 100 deletions(-) diff --git a/include/efi_selftest.h b/include/efi_selftest.h index 76304a2b2a..beb662d4e1 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -14,6 +14,9 @@ #include #include +#define EFI_ST_SUCCESS 0 +#define EFI_ST_FAILURE 1 + /* * Prints an error message. * diff --git a/lib/efi_selftest/efi_selftest.c b/lib/efi_selftest/efi_selftest.c index ff00254c21..45d8d3d384 100644 --- a/lib/efi_selftest/efi_selftest.c +++ b/lib/efi_selftest/efi_selftest.c @@ -66,16 +66,17 @@ void efi_st_exit_boot_services(void) * * @test the test to be executed * @failures counter that will be incremented if a failure occurs + * @return EFI_ST_SUCCESS for success */ static int setup(struct efi_unit_test *test, unsigned int *failures) { int ret; if (!test->setup) - return 0; + return EFI_ST_SUCCESS; efi_st_printf("\nSetting up '%s'\n", test->name); ret = test->setup(handle, systable); - if (ret) { + if (ret != EFI_ST_SUCCESS) { efi_st_error("Setting up '%s' failed\n", test->name); ++*failures; } else { @@ -89,16 +90,17 @@ static int setup(struct efi_unit_test *test, unsigned int *failures) * * @test the test to be executed * @failures counter that will be incremented if a failure occurs + * @return EFI_ST_SUCCESS for success */ static int execute(struct efi_unit_test *test, unsigned int *failures) { int ret; if (!test->execute) - return 0; + return EFI_ST_SUCCESS; efi_st_printf("\nExecuting '%s'\n", test->name); ret = test->execute(); - if (ret) { + if (ret != EFI_ST_SUCCESS) { efi_st_error("Executing '%s' failed\n", test->name); ++*failures; } else { @@ -112,16 +114,17 @@ static int execute(struct efi_unit_test *test, unsigned int *failures) * * @test the test to be torn down * @failures counter that will be incremented if a failure occurs + * @return EFI_ST_SUCCESS for success */ static int teardown(struct efi_unit_test *test, unsigned int *failures) { int ret; if (!test->teardown) - return 0; + return EFI_ST_SUCCESS; efi_st_printf("\nTearing down '%s'\n", test->name); ret = test->teardown(); - if (ret) { + if (ret != EFI_ST_SUCCESS) { efi_st_error("Tearing down '%s' failed\n", test->name); ++*failures; } else { diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c index c4f66952b9..532f165d43 100644 --- a/lib/efi_selftest/efi_selftest_events.c +++ b/lib/efi_selftest/efi_selftest_events.c @@ -14,20 +14,22 @@ static struct efi_event *event_notify; static struct efi_event *event_wait; -static unsigned int counter; +static unsigned int timer_ticks; static struct efi_boot_services *boottime; /* - * Notification function, increments a counter. + * Notification function, increments the notfication count if parameter + * context is provided. * * @event notified event - * @context pointer to the counter + * @context pointer to the notification count */ static void EFIAPI notify(struct efi_event *event, void *context) { - if (!context) - return; - ++*(unsigned int *)context; + unsigned int *count = context; + + if (count) + ++*count; } /* @@ -38,6 +40,7 @@ static void EFIAPI notify(struct efi_event *event, void *context) * * @handle: handle of the loaded image * @systable: system table + * @return: EFI_ST_SUCCESS for success */ static int setup(const efi_handle_t handle, const struct efi_system_table *systable) @@ -47,25 +50,27 @@ static int setup(const efi_handle_t handle, boottime = systable->boottime; ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, - TPL_CALLBACK, notify, (void *)&counter, + TPL_CALLBACK, notify, (void *)&timer_ticks, &event_notify); if (ret != EFI_SUCCESS) { efi_st_error("could not create event\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT, TPL_CALLBACK, notify, NULL, &event_wait); if (ret != EFI_SUCCESS) { efi_st_error("could not create event\n"); - return 1; + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } /* * Tear down unit test. * * Close the events created in setup. + * + * @return: EFI_ST_SUCCESS for success */ static int teardown(void) { @@ -76,7 +81,7 @@ static int teardown(void) event_notify = NULL; if (ret != EFI_SUCCESS) { efi_st_error("could not close event\n"); - return 1; + return EFI_ST_FAILURE; } } if (event_wait) { @@ -84,10 +89,10 @@ static int teardown(void) event_wait = NULL; if (ret != EFI_SUCCESS) { efi_st_error("could not close event\n"); - return 1; + return EFI_ST_FAILURE; } } - return 0; + return EFI_ST_SUCCESS; } /* @@ -98,6 +103,8 @@ static int teardown(void) * * Run a 100 ms single shot timer and check that it is called once * while waiting for 100 ms periodic timer for two periods. + * + * @return: EFI_ST_SUCCESS for success */ static int execute(void) { @@ -105,85 +112,86 @@ static int execute(void) efi_status_t ret; /* Set 10 ms timer */ - counter = 0; + timer_ticks = 0; ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 100 ms timer */ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } + /* Set some arbitrary non-zero value to make change detectable. */ index = 5; ret = boottime->wait_for_event(1, &event_wait, &index); if (ret != EFI_SUCCESS) { efi_st_error("Could not wait for event\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->check_event(event_wait); if (ret != EFI_NOT_READY) { efi_st_error("Signaled state was not cleared.\n"); efi_st_printf("ret = %u\n", (unsigned int)ret); - return 1; + return EFI_ST_FAILURE; } if (index != 0) { efi_st_error("WaitForEvent returned wrong index\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Counter periodic: %u\n", counter); - if (counter < 8 || counter > 12) { + efi_st_printf("Notification count periodic: %u\n", timer_ticks); + if (timer_ticks < 8 || timer_ticks > 12) { efi_st_error("Incorrect timing of events\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0); if (index != 0) { efi_st_error("Could not cancel timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 10 ms timer */ - counter = 0; + timer_ticks = 0; ret = boottime->set_timer(event_notify, EFI_TIMER_RELATIVE, 100000); if (index != 0) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 100 ms timer */ ret = boottime->set_timer(event_wait, EFI_TIMER_PERIODIC, 1000000); if (index != 0) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->wait_for_event(1, &event_wait, &index); if (ret != EFI_SUCCESS) { efi_st_error("Could not wait for event\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Counter single shot: %u\n", counter); - if (counter != 1) { + efi_st_printf("Notification count single shot: %u\n", timer_ticks); + if (timer_ticks != 1) { efi_st_error("Single shot timer failed\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->wait_for_event(1, &event_wait, &index); if (ret != EFI_SUCCESS) { efi_st_error("Could not wait for event\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Stopped counter: %u\n", counter); - if (counter != 1) { + efi_st_printf("Notification count stopped timer: %u\n", timer_ticks); + if (timer_ticks != 1) { efi_st_error("Stopped timer fired\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0); if (index != 0) { efi_st_error("Could not cancel timer\n"); - return 1; + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } EFI_UNIT_TEST(events) = { diff --git a/lib/efi_selftest/efi_selftest_exitbootservices.c b/lib/efi_selftest/efi_selftest_exitbootservices.c index 60271e6180..cddd11d52b 100644 --- a/lib/efi_selftest/efi_selftest_exitbootservices.c +++ b/lib/efi_selftest/efi_selftest_exitbootservices.c @@ -1,5 +1,5 @@ /* - * efi_selftest_events + * efi_selftest_exitbootservices * * Copyright (c) 2017 Heinrich Schuchardt * @@ -13,19 +13,19 @@ static struct efi_boot_services *boottime; static struct efi_event *event_notify; -static unsigned int counter; +static unsigned int notification_count; /* - * Notification function, increments a counter. + * Notification function, increments the notification count. * * @event notified event - * @context pointer to the counter + * @context pointer to the notification count */ static void EFIAPI notify(struct efi_event *event, void *context) { - if (!context) - return; - ++*(unsigned int *)context; + unsigned int *count = context; + + ++*count; } /* @@ -35,6 +35,7 @@ static void EFIAPI notify(struct efi_event *event, void *context) * * @handle: handle of the loaded image * @systable: system table + * @return: EFI_ST_SUCCESS for success */ static int setup(const efi_handle_t handle, const struct efi_system_table *systable) @@ -43,21 +44,24 @@ static int setup(const efi_handle_t handle, boottime = systable->boottime; - counter = 0; + notification_count = 0; ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, - TPL_CALLBACK, notify, (void *)&counter, + TPL_CALLBACK, notify, + (void *)¬ification_count, &event_notify); if (ret != EFI_SUCCESS) { efi_st_error("could not create event\n"); - return 1; + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } /* * Tear down unit test. * * Close the event created in setup. + * + * @return: EFI_ST_SUCCESS for success */ static int teardown(void) { @@ -68,10 +72,10 @@ static int teardown(void) event_notify = NULL; if (ret != EFI_SUCCESS) { efi_st_error("could not close event\n"); - return 1; + return EFI_ST_FAILURE; } } - return 0; + return EFI_ST_SUCCESS; } /* @@ -82,19 +86,21 @@ static int teardown(void) * * Call ExitBootServices again and check that the notification function is * not called again. + * + * @return: EFI_ST_SUCCESS for success */ static int execute(void) { - if (counter != 1) { - efi_st_error("ExitBootServices was not notified"); - return 1; + if (notification_count != 1) { + efi_st_error("ExitBootServices was not notified\n"); + return EFI_ST_FAILURE; } efi_st_exit_boot_services(); - if (counter != 1) { - efi_st_error("ExitBootServices was notified twice"); - return 1; + if (notification_count != 1) { + efi_st_error("ExitBootServices was notified twice\n"); + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } EFI_UNIT_TEST(exitbootservices) = { diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c index 90ace0f51e..5d13f3b52d 100644 --- a/lib/efi_selftest/efi_selftest_tpl.c +++ b/lib/efi_selftest/efi_selftest_tpl.c @@ -13,20 +13,20 @@ static struct efi_event *event_notify; static struct efi_event *event_wait; -static unsigned int counter; +static unsigned int notification_count; static struct efi_boot_services *boottime; /* - * Notification function, increments a counter. + * Notification function, increments the notification count. * * @event notified event - * @context pointer to the counter + * @context pointer to the notification count */ static void EFIAPI notify(struct efi_event *event, void *context) { - if (!context) - return; - ++*(unsigned int *)context; + unsigned int *count = context; + + ++*count; } /* @@ -37,6 +37,7 @@ static void EFIAPI notify(struct efi_event *event, void *context) * * @handle: handle of the loaded image * @systable: system table + * @return: EFI_ST_SUCCESS for success */ static int setup(const efi_handle_t handle, const struct efi_system_table *systable) @@ -46,25 +47,28 @@ static int setup(const efi_handle_t handle, boottime = systable->boottime; ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, - TPL_CALLBACK, notify, (void *)&counter, + TPL_CALLBACK, notify, + (void *)¬ification_count, &event_notify); if (ret != EFI_SUCCESS) { efi_st_error("could not create event\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT, TPL_HIGH_LEVEL, notify, NULL, &event_wait); if (ret != EFI_SUCCESS) { efi_st_error("could not create event\n"); - return 1; + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } /* * Tear down unit test. * * Close the events created in setup. + * + * @return: EFI_ST_SUCCESS for success */ static int teardown(void) { @@ -75,7 +79,7 @@ static int teardown(void) event_notify = NULL; if (ret != EFI_SUCCESS) { efi_st_error("could not close event\n"); - return 1; + return EFI_ST_FAILURE; } } if (event_wait) { @@ -83,11 +87,11 @@ static int teardown(void) event_wait = NULL; if (ret != EFI_SUCCESS) { efi_st_error("could not close event\n"); - return 1; + return EFI_ST_FAILURE; } } boottime->restore_tpl(TPL_APPLICATION); - return 0; + return EFI_ST_SUCCESS; } /* @@ -101,6 +105,8 @@ static int teardown(void) * * Lower the TPL level and check that the queued notification * function is called. + * + * @return: EFI_ST_SUCCESS for success */ static int execute(void) { @@ -109,100 +115,103 @@ static int execute(void) UINTN old_tpl; /* Set 10 ms timer */ - counter = 0; + notification_count = 0; ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 100 ms timer */ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } index = 5; ret = boottime->wait_for_event(1, &event_wait, &index); if (ret != EFI_SUCCESS) { efi_st_error("Could not wait for event\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->check_event(event_wait); if (ret != EFI_NOT_READY) { efi_st_error("Signaled state was not cleared.\n"); efi_st_printf("ret = %u\n", (unsigned int)ret); - return 1; + return EFI_ST_FAILURE; } if (index != 0) { efi_st_error("WaitForEvent returned wrong index\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter); - if (counter < 8 || counter > 12) { + efi_st_printf("Notification count with TPL level TPL_APPLICATION: %u\n", + notification_count); + if (notification_count < 8 || notification_count > 12) { efi_st_error("Incorrect timing of events\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0); if (index != 0) { efi_st_error("Could not cancel timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Raise TPL level */ old_tpl = boottime->raise_tpl(TPL_CALLBACK); if (old_tpl != TPL_APPLICATION) { efi_st_error("Initial TPL level was not TPL_APPLICATION"); - return 1; + return EFI_ST_FAILURE; } /* Set 10 ms timer */ - counter = 0; + notification_count = 0; ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 100000); if (index != 0) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 100 ms timer */ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } do { ret = boottime->check_event(event_wait); } while (ret == EFI_NOT_READY); if (ret != EFI_SUCCESS) { efi_st_error("Could not check event\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Counter with TPL level TPL_CALLBACK: %u\n", counter); - if (counter != 0) { + efi_st_printf("Notification count with TPL level TPL_CALLBACK: %u\n", + notification_count); + if (notification_count != 0) { efi_st_error("Suppressed timer fired\n"); - return 1; + return EFI_ST_FAILURE; } /* Set 1 ms timer */ ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 1000); if (ret != EFI_SUCCESS) { efi_st_error("Could not set timer\n"); - return 1; + return EFI_ST_FAILURE; } /* Restore the old TPL level */ boottime->restore_tpl(TPL_APPLICATION); ret = boottime->wait_for_event(1, &event_wait, &index); if (ret != EFI_SUCCESS) { efi_st_error("Could not wait for event\n"); - return 1; + return EFI_ST_FAILURE; } - efi_st_printf("Counter with TPL level TPL_APPLICATION: %u\n", counter); - if (counter < 1) { + efi_st_printf("Notification count with TPL level TPL_APPLICATION: %u\n", + notification_count); + if (notification_count < 1) { efi_st_error("Queued timer event did not fire\n"); - return 1; + return EFI_ST_FAILURE; } ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0); if (index != 0) { efi_st_error("Could not cancel timer\n"); - return 1; + return EFI_ST_FAILURE; } - return 0; + return EFI_ST_SUCCESS; } EFI_UNIT_TEST(tpl) = { From 332468f7fca8d6089367083f4386478793e3b544 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 21 Sep 2017 18:30:11 +0200 Subject: [PATCH 07/37] efi_loader: provide function comments for boot services Provide comments describing the boot service functions. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 640 +++++++++++++++++++++++++++++++++- 1 file changed, 638 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index cb688e16e9..df75dd9032 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -161,6 +161,19 @@ static u64 efi_div10(u64 a) return ret; } +/* + * 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. + * + * @event event to signal + */ void efi_signal_event(struct efi_event *event) { if (event->notify_function) { @@ -174,12 +187,28 @@ void efi_signal_event(struct efi_event *event) 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 +225,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 +247,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 +271,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 +292,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 +323,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 +346,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; @@ -289,6 +388,21 @@ static efi_status_t efi_create_handle(void **handle) */ 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, @@ -324,6 +438,20 @@ efi_status_t efi_create_event(uint32_t type, UINTN notify_tpl, 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) ( @@ -339,8 +467,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) { @@ -372,6 +503,17 @@ void efi_timer_check(void) 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) { @@ -409,6 +551,18 @@ efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type, 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) @@ -417,6 +571,18 @@ 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) @@ -466,6 +632,18 @@ out: 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 + */ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) { int i; @@ -484,6 +662,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; @@ -501,6 +689,18 @@ static efi_status_t EFIAPI efi_close_event(struct efi_event *event) 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; @@ -521,6 +721,20 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) 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, void *protocol_interface) @@ -579,6 +793,20 @@ 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, void *protocol_interface) @@ -591,6 +819,20 @@ 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, void *new_interface) @@ -600,6 +842,18 @@ 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) { @@ -641,6 +895,18 @@ 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) { @@ -650,6 +916,19 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle, protocol_interface)); } +/* + * 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(efi_guid_t *protocol, struct efi_event *event, void **registration) @@ -658,6 +937,17 @@ static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, 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, struct efi_object *efiobj) @@ -681,6 +971,19 @@ 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, @@ -719,6 +1022,20 @@ 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, @@ -731,6 +1048,18 @@ static efi_status_t EFIAPI efi_locate_handle_ext( buffer_size, buffer)); } +/* + * 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(efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device) @@ -759,6 +1088,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; @@ -789,6 +1128,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) { @@ -796,8 +1146,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 enty 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, @@ -832,6 +1189,12 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob 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 + */ efi_status_t efi_load_image_from_path(struct efi_device_path *file_path, void **buffer) { @@ -873,6 +1236,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, @@ -926,6 +1305,17 @@ 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 + */ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, unsigned long *exit_data_size, s16 **exit_data) @@ -954,6 +1344,18 @@ 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 + */ 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) @@ -978,6 +1380,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; @@ -992,6 +1400,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; @@ -1004,6 +1422,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) @@ -1016,6 +1437,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) { @@ -1051,6 +1483,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; @@ -1059,6 +1501,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); @@ -1066,6 +1518,18 @@ 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 + */ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, uint64_t watchdog_code, unsigned long data_size, @@ -1076,6 +1540,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, @@ -1087,6 +1564,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) @@ -1096,6 +1585,19 @@ 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, void *agent_handle, @@ -1106,6 +1608,19 @@ 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, struct efi_open_protocol_info_entry **entry_buffer, @@ -1116,6 +1631,17 @@ 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 + */ static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, efi_guid_t ***protocol_buffer, unsigned long *protocol_buffer_count) @@ -1169,6 +1695,20 @@ 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, @@ -1202,6 +1742,17 @@ out: return EFI_EXIT(r); } +/* + * 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 + */ static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, void *registration, void **protocol_interface) @@ -1237,6 +1788,18 @@ 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, ...) { @@ -1281,6 +1844,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, ...) { @@ -1288,6 +1863,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) @@ -1297,6 +1884,17 @@ static efi_status_t EFIAPI efi_calculate_crc32(void *data, return EFI_EXIT(EFI_SUCCESS); } +/* + * 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, void *source, unsigned long length) { @@ -1304,12 +1902,38 @@ static void EFIAPI efi_copy_mem(void *destination, void *source, memcpy(destination, source, length); } +/* + * 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, unsigned long size, uint8_t value) { EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value); memset(buffer, value, size); } +/* + * 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 **protocol_interface, void *agent_handle, @@ -1382,6 +2006,18 @@ 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, void **protocol_interface) From 7d963323a25f87f701aab6ced99a912230f19776 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:14:14 +0200 Subject: [PATCH 08/37] efi_loader: replace efi_div10 by do_div We should use the existing 64bit division instead of reinventing the wheel. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 36 ++--------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index df75dd9032..66ce92f654 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -128,39 +129,6 @@ 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 - * - * Decimals of one tenth: 0x1 / 0xA = 0x0.19999... - */ -#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; -} - /* * Queue an EFI event. * @@ -523,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]) From f7c78176d6925726d654cd84eb866c7b39da1b13 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:51 +0200 Subject: [PATCH 09/37] efi_loader: call EFI_EXIT in efi_copy_mem, efi_set_mem EFI_ENTRY and EFI_EXIT calls must match. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 66ce92f654..b8b98f2c4a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1868,6 +1868,7 @@ static void EFIAPI efi_copy_mem(void *destination, void *source, { EFI_ENTRY("%p, %p, %ld", destination, source, length); memcpy(destination, source, length); + EFI_EXIT(EFI_SUCCESS); } /* @@ -1885,6 +1886,7 @@ static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value) { EFI_ENTRY("%p, %ld, 0x%x", buffer, size, value); memset(buffer, value, size); + EFI_EXIT(EFI_SUCCESS); } /* From fc05a9590689b70fffdb9914e9867ea53fd579fa Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:52 +0200 Subject: [PATCH 10/37] efi_loader: parameters of CopyMem and SetMem The UEFI spec defines the length parameters of CopyMem and SetMem as UINTN. We should size_t here. The source buffer of CopyMem should be marked as const. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 7 +++---- lib/efi_loader/efi_boottime.c | 10 +++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index c3b9032a48..0b1a383e61 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -156,10 +156,9 @@ struct efi_boot_services { void *handle, ...); efi_status_t (EFIAPI *calculate_crc32)(void *data, unsigned long data_size, uint32_t *crc32); - void (EFIAPI *copy_mem)(void *destination, void *source, - unsigned long length); - void (EFIAPI *set_mem)(void *buffer, unsigned long size, - uint8_t value); + void (EFIAPI *copy_mem)(void *destination, const void *source, + size_t length); + void (EFIAPI *set_mem)(void *buffer, size_t size, uint8_t value); void *create_event_ex; }; diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b8b98f2c4a..c48ff2cd2a 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1863,10 +1863,10 @@ static efi_status_t EFIAPI efi_calculate_crc32(void *data, * @source source of the copy operation * @length number of bytes to copy */ -static void EFIAPI efi_copy_mem(void *destination, void *source, - unsigned long length) +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); } @@ -1882,9 +1882,9 @@ static void EFIAPI efi_copy_mem(void *destination, void *source, * @size size of buffer in bytes * @value byte to copy to the buffer */ -static void EFIAPI efi_set_mem(void *buffer, unsigned long size, uint8_t value) +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); } From 5a9682d0ddd9328eb581b18a8b263c3834943942 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:53 +0200 Subject: [PATCH 11/37] efi_loader: pass GUIDs as const efi_guid_t * We need to call some boottime services internally. Our GUIDs are stored as const efi_guid_t *. The boottime services never change GUIDs. So we can define the parameters as const efi_guid_t *. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 27 +++++++++++------------ lib/efi_loader/efi_boottime.c | 40 ++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 0b1a383e61..aa4306aac9 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -77,24 +77,25 @@ struct efi_boot_services { efi_status_t (EFIAPI *check_event)(struct efi_event *event); #define EFI_NATIVE_INTERFACE 0x00000000 efi_status_t (EFIAPI *install_protocol_interface)( - void **handle, efi_guid_t *protocol, + void **handle, const efi_guid_t *protocol, int protocol_interface_type, void *protocol_interface); efi_status_t (EFIAPI *reinstall_protocol_interface)( - void *handle, efi_guid_t *protocol, + void *handle, const efi_guid_t *protocol, void *old_interface, void *new_interface); efi_status_t (EFIAPI *uninstall_protocol_interface)(void *handle, - efi_guid_t *protocol, void *protocol_interface); - efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, efi_guid_t *, - void **); + const efi_guid_t *protocol, void *protocol_interface); + efi_status_t (EFIAPI *handle_protocol)(efi_handle_t, + const efi_guid_t *protocol, + void **protocol_interface); void *reserved; efi_status_t (EFIAPI *register_protocol_notify)( - efi_guid_t *protocol, struct efi_event *event, + const efi_guid_t *protocol, struct efi_event *event, void **registration); efi_status_t (EFIAPI *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); - efi_status_t (EFIAPI *locate_device_path)(efi_guid_t *protocol, + efi_status_t (EFIAPI *locate_device_path)(const efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device); efi_status_t (EFIAPI *install_configuration_table)( @@ -131,14 +132,14 @@ struct efi_boot_services { #define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010 #define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020 efi_status_t (EFIAPI *open_protocol)(efi_handle_t handle, - efi_guid_t *protocol, void **interface, + const efi_guid_t *protocol, void **interface, efi_handle_t agent_handle, efi_handle_t controller_handle, u32 attributes); efi_status_t (EFIAPI *close_protocol)(void *handle, - efi_guid_t *protocol, void *agent_handle, + const efi_guid_t *protocol, void *agent_handle, void *controller_handle); efi_status_t(EFIAPI *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); efi_status_t (EFIAPI *protocols_per_handle)(efi_handle_t handle, @@ -146,9 +147,9 @@ struct efi_boot_services { unsigned long *protocols_buffer_count); efi_status_t (EFIAPI *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 (EFIAPI *locate_protocol)(efi_guid_t *protocol, + efi_status_t (EFIAPI *locate_protocol)(const efi_guid_t *protocol, void *registration, void **protocol_interface); efi_status_t (EFIAPI *install_multiple_protocol_interfaces)( void **handle, ...); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index c48ff2cd2a..e5adc17fab 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -704,7 +704,7 @@ static efi_status_t EFIAPI efi_check_event(struct efi_event *event) * @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; @@ -776,7 +776,7 @@ out: * @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, @@ -802,7 +802,7 @@ static efi_status_t EFIAPI efi_install_protocol_interface_ext(void **handle, * @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, @@ -823,7 +823,7 @@ 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, - efi_guid_t *protocol, void *protocol_interface) + const efi_guid_t *protocol, void *protocol_interface) { struct list_head *lhandle; int i; @@ -876,7 +876,7 @@ out: * @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); @@ -897,9 +897,10 @@ static efi_status_t EFIAPI efi_uninstall_protocol_interface_ext(void *handle, * @registration key for retrieving the registration information * @return status code */ -static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, - struct efi_event *event, - void **registration) +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); @@ -917,7 +918,7 @@ static efi_status_t EFIAPI efi_register_protocol_notify(efi_guid_t *protocol, * @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; @@ -954,7 +955,7 @@ static int efi_search(enum efi_locate_search_type search_type, */ 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; @@ -1006,7 +1007,7 @@ static efi_status_t efi_locate_handle( */ 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, @@ -1028,7 +1029,8 @@ static efi_status_t EFIAPI efi_locate_handle_ext( * @device handle of the device * @return status code */ -static efi_status_t EFIAPI efi_locate_device_path(efi_guid_t *protocol, +static efi_status_t EFIAPI efi_locate_device_path( + const efi_guid_t *protocol, struct efi_device_path **device_path, efi_handle_t *device) { @@ -1567,7 +1569,7 @@ static efi_status_t EFIAPI efi_disconnect_controller(void *controller_handle, * @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) { @@ -1590,7 +1592,7 @@ static efi_status_t EFIAPI efi_close_protocol(void *handle, * @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) { @@ -1679,7 +1681,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle(void *handle, */ 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; @@ -1721,7 +1723,7 @@ out: * @registration registration key passed to the notification function * @protocol_interface interface implementing the protocol */ -static efi_status_t EFIAPI efi_locate_protocol(efi_guid_t *protocol, +static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol, void *registration, void **protocol_interface) { @@ -1774,7 +1776,7 @@ static efi_status_t EFIAPI efi_install_multiple_protocol_interfaces( 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; @@ -1905,7 +1907,7 @@ static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value) * @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) { @@ -1989,7 +1991,7 @@ out: * @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, From ca379e1bf16e59841f15ed34088eb1362bf2bd35 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:54 +0200 Subject: [PATCH 12/37] efi_loader: wrong type in wait_for_event The UEFI spec defines parameter index of WaitForEvent as UINTN*. So we should use size_t here. I deliberately do not use UINTN because I hold a following patch that will eliminate UINTN because uppercase types to not match the U-Boot coding style. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 2 +- lib/efi_loader/efi_boottime.c | 2 +- lib/efi_selftest/efi_selftest_events.c | 2 +- lib/efi_selftest/efi_selftest_tpl.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index aa4306aac9..c44dc9d0cb 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -71,7 +71,7 @@ struct efi_boot_services { enum efi_timer_delay type, uint64_t trigger_time); efi_status_t (EFIAPI *wait_for_event)(unsigned long number_of_events, - struct efi_event **event, unsigned long *index); + struct efi_event **event, size_t *index); efi_status_t (EFIAPI *signal_event)(struct efi_event *event); efi_status_t (EFIAPI *close_event)(struct efi_event *event); efi_status_t (EFIAPI *check_event)(struct efi_event *event); diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index e5adc17fab..976d5822f7 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -553,7 +553,7 @@ static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event, */ 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; diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c index 532f165d43..b2cdc150da 100644 --- a/lib/efi_selftest/efi_selftest_events.c +++ b/lib/efi_selftest/efi_selftest_events.c @@ -108,7 +108,7 @@ static int teardown(void) */ static int execute(void) { - unsigned long index; + size_t index; efi_status_t ret; /* Set 10 ms timer */ diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c index 5d13f3b52d..0b78ee7595 100644 --- a/lib/efi_selftest/efi_selftest_tpl.c +++ b/lib/efi_selftest/efi_selftest_tpl.c @@ -110,7 +110,7 @@ static int teardown(void) */ static int execute(void) { - unsigned long index; + size_t index; efi_status_t ret; UINTN old_tpl; From 84a12ce64e08257d9e8eb7b9e7c2b232a2a35ebf Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:55 +0200 Subject: [PATCH 13/37] efi_loader: incorrect definition of EFI_SIMPLE_NETWORK_PROTOCOL WaitForPacket is an event and not a function pointer. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/efi_api.h b/include/efi_api.h index c44dc9d0cb..308baeec49 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -633,7 +633,7 @@ struct efi_simple_network ulong *header_size, ulong *buffer_size, void *buffer, struct efi_mac_address *src_addr, struct efi_mac_address *dest_addr, u16 *protocol); - void (EFIAPI *waitforpacket)(void); + struct efi_event *wait_for_packet; struct efi_simple_network_mode *mode; }; From 2e0864a47e223d1581acfb49e4ec2f68615da3dc Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:56 +0200 Subject: [PATCH 14/37] efi_loader: correct bits of receive_filters bit mask Remove extraneous commas. Add comment. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 308baeec49..8c227ce703 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -593,11 +593,12 @@ struct efi_simple_network_mode { u8 media_present; }; -#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01, -#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02, -#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04, -#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08, -#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10, +/* receive_filters bit mask */ +#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 +#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 +#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 struct efi_simple_network { From bdecf974f168af49f5deb2d325e7ad8f3ad4a52e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:57 +0200 Subject: [PATCH 15/37] efi_loader: fill simple network protocol revision Provide the simple network protocol revision. This revision number could be used to identify backwards compatible enhancements of the protocol. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 3 +++ lib/efi_loader/efi_net.c | 1 + 2 files changed, 4 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index 8c227ce703..2f31464cb3 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -600,6 +600,9 @@ struct efi_simple_network_mode { #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 +/* revision of the simple network protocol */ +#define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000 + struct efi_simple_network { u64 revision; diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 91f1e4a69e..fb23bcf633 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -228,6 +228,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; From 5d4a5ea964e523a29ec077b6f95537956c366c1f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:58 +0200 Subject: [PATCH 16/37] efi_loader: efi_net: hwaddr_size = 6 The length of a MAC address is 6. We have to set this length in the EFI_SIMPLE_NETWORK_MODE structure of the EFI_SIMPLE_NETWORK_PROTOCOL. Without this patch iPXE fails to initialize the network with error message SNP MAC(001e0633bcbf,0x0) has invalid hardware address length 0 Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index fb23bcf633..9def34cf93 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -245,6 +245,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; From 61da678c394583a4731178858e9b9312f05c758b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:35:59 +0200 Subject: [PATCH 17/37] efi_net: return EFI_UNSUPPORTED where appropriate U-Boot does not implement all functions of the simple network protocol. The unimplemented functions return either of EFI_SUCCESS and EFI_INVALID_PARAMETER. The UEFI spec foresees to return EFI_UNSUPPORTED in these cases. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 9def34cf93..569bf367a8 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -78,9 +78,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 +87,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 +96,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 +116,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, From a0549ef6073e346eb0cbbae1395d4db446b7b05e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:00 +0200 Subject: [PATCH 18/37] efi_loader: use events for efi_net_receive A timer event is defined. The timer handler cares for receiving new packets. efi_timer_check is called both in efi_net_transmit and efi_net_receive to enable events during network communication. Calling efi_timer_check in efi_net_get_status is implemented in a separate patch. [agraf] This patch is needed to make efi_net_get_status() actually report incoming packets. Signed-off-by: Heinrich Schuchardt [agraf: fix spelling in comment] Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 53 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 569bf367a8..37047891c9 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -19,6 +19,11 @@ 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; struct efi_net_obj { /* Generic EFI object parent class data */ @@ -143,6 +148,8 @@ static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this, EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size, 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 */ return EFI_EXIT(EFI_INVALID_PARAMETER); @@ -174,9 +181,7 @@ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, 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); @@ -204,10 +209,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 */ @@ -253,5 +280,25 @@ int efi_net_register(void) /* Hook net up to the device list */ list_add_tail(&netobj->parent.link, &efi_obj_list); + /* + * 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; } From e5c21603fce2e6143f7312211eaed47c16510ea3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:01 +0200 Subject: [PATCH 19/37] efi_loader: implement WaitForPacket event The WaitForPacket event informs that a network package has been received by the SimpleNetworkProtocol. Signed-off-by: Heinrich Schuchardt [agraf: Move is_signaled = true line into efi_net_push()] Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 37047891c9..cd46d2db53 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -24,6 +24,10 @@ static void *new_tx_packet; * 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 */ @@ -171,6 +175,7 @@ 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; } static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, @@ -280,6 +285,17 @@ 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. * From 891b3d9051690d0ba39da4eda1d15773ebfee2b6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:02 +0200 Subject: [PATCH 20/37] efi_loader: fix efi_net_get_status The returned interrupt status was wrong. As out transmit buffer is empty we need to always set EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT. When we have received a packet we need to set EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT. Furthermore we should call efi_timer_check() to handle events. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 6 ++++++ lib/efi_loader/efi_net.c | 11 ++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 2f31464cb3..1f349db246 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -600,6 +600,12 @@ struct efi_simple_network_mode { #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 #define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 +/* interrupt status bit mask */ +#define EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01 +#define EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02 +#define EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04 +#define EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08 + /* revision of the simple network protocol */ #define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000 diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index cd46d2db53..b16463ba1a 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -133,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; From 8db174d651e3f92ddb9660f1f8b8755b902c3c11 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:03 +0200 Subject: [PATCH 21/37] efi_loader: size fields in SimpleNetworkProtocol The size fields in the Simple Network Protocol are all UINTN in the UEFI spec. So use size_t. Provide a function description of the receive function. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_api.h | 4 ++-- lib/efi_loader/efi_net.c | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 1f349db246..a9a6494afe 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -636,11 +636,11 @@ struct efi_simple_network efi_status_t (EFIAPI *get_status)(struct efi_simple_network *this, u32 *int_status, void **txbuf); efi_status_t (EFIAPI *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_status_t (EFIAPI *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 efi_event *wait_for_packet; diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index b16463ba1a..4546b43603 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -150,12 +150,13 @@ 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(); @@ -183,8 +184,23 @@ static void efi_net_push(void *pkt, int len) 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) { From 336d9dfc0a3b2245945a9fca9312c4266d186866 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:04 +0200 Subject: [PATCH 22/37] efi_loader: fill return values in SimpleNetworkProtocol In the receive function all return values should be filled. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_net.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 4546b43603..432d9a99a2 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -204,6 +204,10 @@ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, 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); @@ -211,13 +215,32 @@ static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this, 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; From 9820c2f30c028f40d22fdc892799fd7088f23bc7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:05 +0200 Subject: [PATCH 23/37] efi_selftest: correct definition of efi_st_error Enclose definition in parantheses to allow using efi_st_error like a void function. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_selftest.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/efi_selftest.h b/include/efi_selftest.h index beb662d4e1..2f0992f06e 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -23,8 +23,8 @@ * @... format string followed by fields to print */ #define efi_st_error(...) \ - efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__); \ - efi_st_printf(__VA_ARGS__) \ + (efi_st_printf("%s(%u):\nERROR: ", __FILE__, __LINE__), \ + efi_st_printf(__VA_ARGS__)) \ /* * A test may be setup and executed at boottime, From 1b6332597f23ea71b94a9ce65e15a0d3f5ea23ed Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:06 +0200 Subject: [PATCH 24/37] efi_selftest: allow printing MAC addresses Add %pm as format string to print a MAC address. This is helpful when analyzing network problems. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_console.c | 41 ++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/efi_selftest/efi_selftest_console.c b/lib/efi_selftest/efi_selftest_console.c index 7b5b724a61..840e2290c6 100644 --- a/lib/efi_selftest/efi_selftest_console.c +++ b/lib/efi_selftest/efi_selftest_console.c @@ -12,6 +12,37 @@ struct efi_simple_text_output_protocol *con_out; struct efi_simple_input_interface *con_in; +/* + * Print a MAC address to an u16 string + * + * @pointer: mac address + * @buf: pointer to buffer address + * on return position of terminating zero word + */ +static void mac(void *pointer, u16 **buf) +{ + int i, j; + u16 c; + u8 *p = (u8 *)pointer; + u8 byte; + u16 *pos = *buf; + + for (i = 0; i < ARP_HLEN; ++i) { + if (i) + *pos++ = ':'; + byte = p[i]; + for (j = 4; j >= 0; j -= 4) { + c = (byte >> j) & 0x0f; + c += '0'; + if (c > '9') + c += 'a' - '9' - 1; + *pos++ = c; + } + } + *pos = 0; + *buf = pos; +} + /* * Print a pointer to an u16 string * @@ -146,7 +177,15 @@ void efi_st_printf(const char *fmt, ...) int2dec(va_arg(args, s32), &pos); break; case 'p': - pointer(va_arg(args, void*), &pos); + ++c; + switch (*c) { + case 'm': + mac(va_arg(args, void*), &pos); + break; + default: + --c; + pointer(va_arg(args, void*), &pos); + } break; case 's': s = va_arg(args, const char *); From 5ca23ed5bc63832baa24a6107537fdd229c458ae Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 5 Oct 2017 16:36:07 +0200 Subject: [PATCH 25/37] efi_loader: supply EFI network test This patch provides an EFI application to check the correct function of the Simple Network Protocol implementation. It sends a DHCP request and analyzes the DHCP offer. Different error conditions including a 10s timeout are checked. A successful execution will look like this: => bootefi nettest Scanning disk ide.blk#0... Found 1 disks WARNING: Invalid device tree, expect boot to fail Network test DHCP Discover DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) as broadcast message. OK. The test was completed successfully. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- include/efi_selftest.h | 11 + lib/efi_selftest/Makefile | 8 +- lib/efi_selftest/efi_selftest_snp.c | 424 +++++++++++++++++++++++++++ lib/efi_selftest/efi_selftest_util.c | 25 ++ 4 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 lib/efi_selftest/efi_selftest_snp.c create mode 100644 lib/efi_selftest/efi_selftest_util.c diff --git a/include/efi_selftest.h b/include/efi_selftest.h index 2f0992f06e..7ec42a0406 100644 --- a/include/efi_selftest.h +++ b/include/efi_selftest.h @@ -60,6 +60,17 @@ void efi_st_exit_boot_services(void); void efi_st_printf(const char *fmt, ...) __attribute__ ((format (__printf__, 1, 2))); +/* + * Compare memory. + * We cannot use lib/string.c due to different CFLAGS values. + * + * @buf1: first buffer + * @buf2: second buffer + * @length: number of bytes to compare + * @return: 0 if both buffers contain the same bytes + */ +int efi_st_memcmp(const void *buf1, const void *buf2, size_t length); + /* * Reads an Unicode character from the input device. * diff --git a/lib/efi_selftest/Makefile b/lib/efi_selftest/Makefile index 30f1960933..e446046e02 100644 --- a/lib/efi_selftest/Makefile +++ b/lib/efi_selftest/Makefile @@ -15,12 +15,18 @@ CFLAGS_efi_selftest_events.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_events.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_exitbootservices.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_exitbootservices.o := $(CFLAGS_NON_EFI) +CFLAGS_efi_selftest_snp.o := $(CFLAGS_EFI) +CFLAGS_REMOVE_efi_selftest_snp.o := $(CFLAGS_NON_EFI) CFLAGS_efi_selftest_tpl.o := $(CFLAGS_EFI) CFLAGS_REMOVE_efi_selftest_tpl.o := $(CFLAGS_NON_EFI) +CFLAGS_efi_selftest_util.o := $(CFLAGS_EFI) +CFLAGS_REMOVE_efi_selftest_util.o := $(CFLAGS_NON_EFI) obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += \ efi_selftest.o \ efi_selftest_console.o \ efi_selftest_events.o \ efi_selftest_exitbootservices.o \ -efi_selftest_tpl.o +efi_selftest_snp.o \ +efi_selftest_tpl.o \ +efi_selftest_util.o diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c new file mode 100644 index 0000000000..638be0147d --- /dev/null +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -0,0 +1,424 @@ +/* + * efi_selftest_snp + * + * Copyright (c) 2017 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This unit test covers the Simple Network Protocol as well as + * the CopyMem and SetMem boottime services. + * + * A DHCP discover message is sent. The test is successful if a + * DHCP reply is received. + * + * TODO: Once ConnectController and DisconnectController are implemented + * we should connect our code as controller. + */ + +#include + +/* + * MAC address for broadcasts + */ +static const u8 BROADCAST_MAC[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct dhcp_hdr { + u8 op; +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + u8 htype; +# define HWT_ETHER 1 + u8 hlen; +# define HWL_ETHER 6 + u8 hops; + u32 xid; + u16 secs; + u16 flags; +#define DHCP_FLAGS_UNICAST 0x0000 +#define DHCP_FLAGS_BROADCAST 0x0080 + u32 ciaddr; + u32 yiaddr; + u32 siaddr; + u32 giaddr; + u8 chaddr[16]; + u8 sname[64]; + u8 file[128]; +}; + +/* + * Message type option. + */ +#define DHCP_MESSAGE_TYPE 0x35 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +struct dhcp { + struct ethernet_hdr eth_hdr; + struct ip_udp_hdr ip_udp; + struct dhcp_hdr dhcp_hdr; + u8 opt[128]; +} __packed; + +static struct efi_boot_services *boottime; +static struct efi_simple_network *net; +static struct efi_event *timer; +static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID; +/* IP packet ID */ +static unsigned int net_ip_id; + +/* + * Compute the checksum of the IP header. We cover even values of length only. + * We cannot use net/checksum.c due to different CFLAGS values. + * + * @buf: IP header + * @len: length of header in bytes + * @return: checksum + */ +static unsigned int efi_ip_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len; i += 2) + sum += *pos++; + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + sum = ~sum & 0xffff; + + return sum; +} + +/* + * Transmit a DHCPDISCOVER message. + */ +static efi_status_t send_dhcp_discover(void) +{ + efi_status_t ret; + struct dhcp p = {}; + + /* + * Fill ethernet header + */ + boottime->copy_mem(p.eth_hdr.et_dest, (void *)BROADCAST_MAC, ARP_HLEN); + boottime->copy_mem(p.eth_hdr.et_src, &net->mode->current_address, + ARP_HLEN); + p.eth_hdr.et_protlen = htons(PROT_IP); + /* + * Fill IP header + */ + p.ip_udp.ip_hl_v = 0x45; + p.ip_udp.ip_len = htons(sizeof(struct dhcp) - + sizeof(struct ethernet_hdr)); + p.ip_udp.ip_id = htons(++net_ip_id); + p.ip_udp.ip_off = htons(IP_FLAGS_DFRAG); + p.ip_udp.ip_ttl = 0xff; /* time to live */ + p.ip_udp.ip_p = IPPROTO_UDP; + boottime->set_mem(&p.ip_udp.ip_dst, 4, 0xff); + p.ip_udp.ip_sum = efi_ip_checksum(&p.ip_udp, IP_HDR_SIZE); + + /* + * Fill UDP header + */ + p.ip_udp.udp_src = htons(68); + p.ip_udp.udp_dst = htons(67); + p.ip_udp.udp_len = htons(sizeof(struct dhcp) - + sizeof(struct ethernet_hdr) - + sizeof(struct ip_hdr)); + /* + * Fill DHCP header + */ + p.dhcp_hdr.op = BOOTREQUEST; + p.dhcp_hdr.htype = HWT_ETHER; + p.dhcp_hdr.hlen = HWL_ETHER; + p.dhcp_hdr.flags = htons(DHCP_FLAGS_UNICAST); + boottime->copy_mem(&p.dhcp_hdr.chaddr, + &net->mode->current_address, ARP_HLEN); + /* + * Fill options + */ + p.opt[0] = 0x63; /* DHCP magic cookie */ + p.opt[1] = 0x82; + p.opt[2] = 0x53; + p.opt[3] = 0x63; + p.opt[4] = DHCP_MESSAGE_TYPE; + p.opt[5] = 0x01; /* length */ + p.opt[6] = DHCPDISCOVER; + p.opt[7] = 0x39; /* maximum message size */ + p.opt[8] = 0x02; /* length */ + p.opt[9] = 0x02; /* 576 bytes */ + p.opt[10] = 0x40; + p.opt[11] = 0xff; /* end of options */ + + /* + * Transmit DHCPDISCOVER message. + */ + ret = net->transmit(net, 0, sizeof(struct dhcp), &p, NULL, NULL, 0); + if (ret != EFI_SUCCESS) + efi_st_error("Sending a DHCP request failed\n"); + else + efi_st_printf("DHCP Discover\n"); + return ret; +} + +/* + * Setup unit test. + * + * Create a 1 s periodic timer. + * Start the network driver. + * + * @handle: handle of the loaded image + * @systable: system table + * @return: EFI_ST_SUCCESS for success + */ +static int setup(const efi_handle_t handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + + boottime = systable->boottime; + + /* + * Create a timer event. + */ + ret = boottime->create_event(EVT_TIMER, TPL_CALLBACK, NULL, NULL, + &timer); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to create event\n"); + return EFI_ST_FAILURE; + } + /* + * Set timer period to 1s. + */ + ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to locate simple network protocol\n"); + return EFI_ST_FAILURE; + } + /* + * Find an interface implementing the SNP protocol. + */ + ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to locate simple network protocol\n"); + return EFI_ST_FAILURE; + } + /* + * Check hardware address size. + */ + if (!net->mode) { + efi_st_error("Mode not provided\n"); + return EFI_ST_FAILURE; + } + if (net->mode->hwaddr_size != ARP_HLEN) { + efi_st_error("HwAddressSize = %u, expected %u\n", + net->mode->hwaddr_size, ARP_HLEN); + return EFI_ST_FAILURE; + } + /* + * Check that WaitForPacket event exists. + */ + if (!net->wait_for_packet) { + efi_st_error("WaitForPacket event missing\n"); + return EFI_ST_FAILURE; + } + /* + * Initialize network adapter. + */ + ret = net->initialize(net, 0, 0); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to initialize network adapter\n"); + return EFI_ST_FAILURE; + } + /* + * Start network adapter. + */ + ret = net->start(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to start network adapter\n"); + return EFI_ST_FAILURE; + } + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * A DHCP discover message is sent. The test is successful if a + * DHCP reply is received within 10 seconds. + * + * @return: EFI_ST_SUCCESS for success + */ +static int execute(void) +{ + efi_status_t ret; + struct efi_event *events[2]; + size_t index; + union { + struct dhcp p; + u8 b[PKTSIZE]; + } buffer; + struct efi_mac_address srcaddr; + struct efi_mac_address destaddr; + size_t buffer_size; + u8 *addr; + /* + * The timeout is to occur after 10 s. + */ + unsigned int timeout = 10; + + /* + * Send DHCP discover message + */ + ret = send_dhcp_discover(); + if (ret != EFI_SUCCESS) + return EFI_ST_FAILURE; + + /* + * If we would call WaitForEvent only with the WaitForPacket event, + * our code would block until a packet is received which might never + * occur. By calling WaitFor event with both a timer event and the + * WaitForPacket event we can escape this blocking situation. + * + * If the timer event occurs before we have received a DHCP reply + * a further DHCP discover message is sent. + */ + events[0] = timer; + events[1] = net->wait_for_packet; + for (;;) { + /* + * Wait for packet to be received or timer event. + */ + boottime->wait_for_event(2, events, &index); + if (index == 0) { + /* + * The timer event occurred. Check for timeout. + */ + --timeout; + if (!timeout) { + efi_st_error("Timeout occurred\n"); + return EFI_ST_FAILURE; + } + /* + * Send further DHCP discover message + */ + ret = send_dhcp_discover(); + if (ret != EFI_SUCCESS) + return EFI_ST_FAILURE; + continue; + } + /* + * Receive packet + */ + buffer_size = sizeof(buffer); + net->receive(net, NULL, &buffer_size, &buffer, + &srcaddr, &destaddr, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to receive packet"); + return EFI_ST_FAILURE; + } + /* + * Check the packet is meant for this system. + * Unfortunately QEMU ignores the broadcast flag. + * So we have to check for broadcasts too. + */ + if (efi_st_memcmp(&destaddr, &net->mode->current_address, + ARP_HLEN) && + efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + continue; + /* + * Check this is a DHCP reply + */ + if (buffer.p.eth_hdr.et_protlen != ntohs(PROT_IP) || + buffer.p.ip_udp.ip_hl_v != 0x45 || + buffer.p.ip_udp.ip_p != IPPROTO_UDP || + buffer.p.ip_udp.udp_src != ntohs(67) || + buffer.p.ip_udp.udp_dst != ntohs(68) || + buffer.p.dhcp_hdr.op != BOOTREPLY) + continue; + /* + * We successfully received a DHCP reply. + */ + break; + } + + /* + * Write a log message. + */ + addr = (u8 *)&buffer.p.ip_udp.ip_src; + efi_st_printf("DHCP reply received from %u.%u.%u.%u (%pm) ", + addr[0], addr[1], addr[2], addr[3], &srcaddr); + if (!efi_st_memcmp(&destaddr, BROADCAST_MAC, ARP_HLEN)) + efi_st_printf("as broadcast message.\n"); + else + efi_st_printf("as unicast message.\n"); + + return EFI_ST_SUCCESS; +} + +/* + * Tear down unit test. + * + * Close the timer event created in setup. + * Shut down the network adapter. + * + * @return: EFI_ST_SUCCESS for success + */ +static int teardown(void) +{ + efi_status_t ret; + int exit_status = EFI_ST_SUCCESS; + + if (timer) { + /* + * Stop timer. + */ + ret = boottime->set_timer(timer, EFI_TIMER_STOP, 0); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to stop timer"); + exit_status = EFI_ST_FAILURE; + } + /* + * Close timer event. + */ + ret = boottime->close_event(timer); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to close event"); + exit_status = EFI_ST_FAILURE; + } + } + if (net) { + /* + * Stop network adapter. + */ + ret = net->stop(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to stop network adapter\n"); + exit_status = EFI_ST_FAILURE; + } + /* + * Shut down network adapter. + */ + ret = net->shutdown(net); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to shut down network adapter\n"); + exit_status = EFI_ST_FAILURE; + } + } + + return exit_status; +} + +EFI_UNIT_TEST(snp) = { + .name = "simple network protocol", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, + .teardown = teardown, +}; diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c new file mode 100644 index 0000000000..c9c295e2fb --- /dev/null +++ b/lib/efi_selftest/efi_selftest_util.c @@ -0,0 +1,25 @@ +/* + * efi_selftest_util + * + * Copyright (c) 2017 Heinrich Schuchardt + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Utility functions + */ + +#include + +int efi_st_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 EFI_ST_SUCCESS; +} From c155dfeb1e17181998e66830b677baa8983daf6b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 6 Oct 2017 20:39:13 +0200 Subject: [PATCH 26/37] efi_selftest: efi_st_memcmp return difference of bytes If the memory regions are different efi_st_memcmp currently returns the difference of the addresses. Insted the difference of the first differing byte pair should be returned. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_selftest/efi_selftest_util.c b/lib/efi_selftest/efi_selftest_util.c index c9c295e2fb..5cffe383d8 100644 --- a/lib/efi_selftest/efi_selftest_util.c +++ b/lib/efi_selftest/efi_selftest_util.c @@ -17,7 +17,7 @@ int efi_st_memcmp(const void *buf1, const void *buf2, size_t length) for (; length; --length) { if (*pos1 != *pos2) - return pos1 - pos2; + return *pos1 - *pos2; ++pos1; ++pos2; } From 7f8ec5b63e5a8774bfcadafbc0d9583686b00455 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Oct 2017 06:57:25 +0200 Subject: [PATCH 27/37] efi_selftest: avoid dereferencing NULL in tpl test The task priority levels test uses two events one passes the notification counter as context. The other passes NULL. Both use the same notification function. So we need to check for NULL here. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_tpl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c index 0b78ee7595..b8c0e70262 100644 --- a/lib/efi_selftest/efi_selftest_tpl.c +++ b/lib/efi_selftest/efi_selftest_tpl.c @@ -26,7 +26,8 @@ static void EFIAPI notify(struct efi_event *event, void *context) { unsigned int *count = context; - ++*count; + if (count) + ++*count; } /* From 1a2c8d2f609237664fcda944c251ce693e4366f5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Oct 2017 06:57:26 +0200 Subject: [PATCH 28/37] efi_loader: avoid NULL dereference in efi_dp_match When calling bootefi hello twice a kernel dump occurs. Neither bootefi hello nor bootefi selftest have an image device patch. So do not try to dereference the NULL value. Fixes: 95c5553ea26 efi_loader: refactor boot device and loaded_image handling Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 976d5822f7..54cf16476c 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1154,7 +1154,8 @@ 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); } From 77511b3b4be449f1547cd97ec6dde780ef589d84 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sun, 8 Oct 2017 11:33:08 -0400 Subject: [PATCH 29/37] efi_loader: Fix disk dp's for pre-DM/legacy devices This fixes an issue with OpenBSD's bootloader, and I think should also fix a similar issue with grub2 on legacy devices. In the legacy case we were creating disk objects for the partitions, but not also the parent device. Reported-by: Jonathan Gray Signed-off-by: Rob Clark Signed-off-by: Alexander Graf --- lib/efi_loader/efi_disk.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index eb9ce772d1..47b487aa30 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -340,6 +340,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 = 1; desc = blk_get_devnum_by_type(if_type, i); if (!desc) @@ -349,6 +351,15 @@ int efi_disk_register(void) snprintf(devname, sizeof(devname), "%s%d", if_typename, i); + + /* add devices for each partition: */ + while (!part_get_info(desc, part, &info)) { + efi_disk_add_dev(devname, if_typename, desc, + i, 0, part); + part++; + } + + /* ... and add block device: */ efi_disk_add_dev(devname, if_typename, desc, i, 0, 0); disks++; From 10a08c4ed70e57ac570974b04844ceec13ad311a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Oct 2017 06:57:27 +0200 Subject: [PATCH 30/37] efi_loader: comments for functions add missing @return For some functions the @return description is missing. Fix typo. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_loader/efi_boottime.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 54cf16476c..f627340de4 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -611,6 +611,7 @@ out: * notification function for execution. * * @event event to signal + * @return status code */ static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event) { @@ -1120,7 +1121,7 @@ static efi_status_t EFIAPI efi_install_configuration_table_ext(efi_guid_t *guid, * Initialize a loaded_image_info + loaded_image_info object with correct * protocols, boot-device, etc. * - * @info loaded image info to be passed to the enty point of the + * @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 @@ -1165,6 +1166,7 @@ void efi_setup_loaded_image(struct efi_loaded_image *info, struct efi_object *ob * * @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) @@ -1286,6 +1288,7 @@ static efi_status_t EFIAPI efi_load_image(bool boot_policy, * @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, @@ -1326,6 +1329,7 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, * @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, @@ -1500,6 +1504,7 @@ static efi_status_t EFIAPI efi_stall(unsigned long microseconds) * @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, @@ -1612,6 +1617,7 @@ static efi_status_t EFIAPI efi_open_protocol_information(efi_handle_t handle, * @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, @@ -1723,6 +1729,7 @@ out: * @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, From fdd04563cedb6abbc013821c123f5d7ef3078c31 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 8 Oct 2017 06:57:28 +0200 Subject: [PATCH 31/37] efi_selftest: error handling in SNP test Avoid NULL pointer dereference after setup failed due to a missing network. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_snp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/efi_selftest/efi_selftest_snp.c b/lib/efi_selftest/efi_selftest_snp.c index 638be0147d..bdd6ce20da 100644 --- a/lib/efi_selftest/efi_selftest_snp.c +++ b/lib/efi_selftest/efi_selftest_snp.c @@ -198,7 +198,7 @@ static int setup(const efi_handle_t handle, */ ret = boottime->set_timer(timer, EFI_TIMER_PERIODIC, 10000000); if (ret != EFI_SUCCESS) { - efi_st_error("Failed to locate simple network protocol\n"); + efi_st_error("Failed to set timer\n"); return EFI_ST_FAILURE; } /* @@ -206,6 +206,7 @@ static int setup(const efi_handle_t handle, */ ret = boottime->locate_protocol(&efi_net_guid, NULL, (void **)&net); if (ret != EFI_SUCCESS) { + net = NULL; efi_st_error("Failed to locate simple network protocol\n"); return EFI_ST_FAILURE; } @@ -273,6 +274,12 @@ static int execute(void) */ unsigned int timeout = 10; + /* Setup may have failed */ + if (!net || !timer) { + efi_st_error("Cannot execute test after setup failure\n"); + return EFI_ST_FAILURE; + } + /* * Send DHCP discover message */ From 16a73b249d138fedeb188710533902ed7aac1ddc Mon Sep 17 00:00:00 2001 From: Jonathan Gray Date: Tue, 10 Oct 2017 13:55:26 +1100 Subject: [PATCH 32/37] efi_loader: search all possible disk partitions When searching for partitions don't stop if a partition is not present for a given partition number as there may be valid partitions after. Search for up to MAX_SEARCH_PARTITIONS matching the other callers of part_get_info(). This allows OpenBSD to boot via the efi_loader on rpi_3 again after changes made after U-Boot 2017.09. With MBR partitioning OpenBSD will by default use the fourth partition for the 0xA6 (OpenBSD) partition. Signed-off-by: Jonathan Gray Signed-off-by: Alexander Graf --- lib/efi_loader/efi_disk.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 47b487aa30..6b192701a8 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: */ @@ -341,7 +343,7 @@ int efi_disk_register(void) struct blk_desc *desc; char devname[32] = { 0 }; /* dp->str is u16[32] long */ disk_partition_t info; - int part = 1; + int part; desc = blk_get_devnum_by_type(if_type, i); if (!desc) @@ -353,7 +355,9 @@ int efi_disk_register(void) if_typename, i); /* 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(devname, if_typename, desc, i, 0, part); part++; From 984f251feeb932cfe551cbd62c5f7f5e073b6cc3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Mon, 9 Oct 2017 21:09:05 +0200 Subject: [PATCH 33/37] efi_loader: MAX_UTF8_PER_UTF16 = 3 The constant MAX_UTF8_PER_UTF16 is used to calculate required memory when converting from UTF-16 to UTF-8. If this constant is too big we waste memory. A code point encoded by one UTF-16 symbol is converted to a maximum of three UTF-8 symbols, e.g. 0xffff could be encoded as 0xef 0xbf 0xbf. The first byte carries four bits, the second and third byte carry six bits each. A code point encoded by two UTF-16 symbols is converted to four UTF-8 symbols. So in this case we need a maximum of two UTF-8 symbols per UTF-16 symbol. As the overall maximum is three UTF-8 symobls per UTF-16 symbol we need MAX_UTF8_PER_UTF16 = 3. Fixes: 78178bb0c9d lib: add some utf16 handling helpers Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- include/charset.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/charset.h b/include/charset.h index 39279f746a..37a3278499 100644 --- a/include/charset.h +++ b/include/charset.h @@ -9,7 +9,7 @@ #ifndef __CHARSET_H_ #define __CHARSET_H_ -#define MAX_UTF8_PER_UTF16 4 +#define MAX_UTF8_PER_UTF16 3 /** * utf16_strlen() - Get the length of an utf16 string @@ -52,7 +52,7 @@ uint16_t *utf16_strdup(const uint16_t *s); * Converts 'size' characters of the utf16 string 'src' to utf8 * written to the 'dest' buffer. * - * NOTE that a single utf16 character can generate up to 4 utf8 + * NOTE that a single utf16 character can generate up to 3 utf8 * characters. See MAX_UTF8_PER_UTF16. * * @dest the destination buffer to write the utf8 characters From bcbc4a80462c42c5d5ccac0287b8a7d49df9e179 Mon Sep 17 00:00:00 2001 From: Jonathan Gray Date: Tue, 10 Oct 2017 21:32:29 +1100 Subject: [PATCH 34/37] efi_loader: don't increment part twice per loop Correct a mistake in the part number handling of commit 16a73b249d138fedeb188710533902ed7aac1ddc and only increment part once per loop. Signed-off-by: Jonathan Gray Tested-by: Peter Robinson Signed-off-by: Alexander Graf --- lib/efi_loader/efi_disk.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/efi_loader/efi_disk.c b/lib/efi_loader/efi_disk.c index 6b192701a8..e61dbc8058 100644 --- a/lib/efi_loader/efi_disk.c +++ b/lib/efi_loader/efi_disk.c @@ -360,7 +360,6 @@ int efi_disk_register(void) continue; efi_disk_add_dev(devname, if_typename, desc, i, 0, part); - part++; } /* ... and add block device: */ From bf19273e81eb5e23c9bc14c3881f92a120565561 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 10 Oct 2017 08:23:06 -0400 Subject: [PATCH 35/37] efi_loader: Add mem-mapped for fallback When we don't have a real device/image path, such as 'bootefi hello', construct a mem-mapped device-path. This fixes 'bootefi hello' after devicepath refactoring. Fixes: 95c5553ea2 ("efi_loader: refactor boot device and loaded_image handling") Signed-off-by: Rob Clark Acked-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- cmd/bootefi.c | 23 +++++++++++++++++++++++ include/efi_api.h | 8 ++++++++ include/efi_loader.h | 3 +++ lib/efi_loader/efi_device_path.c | 24 ++++++++++++++++++++++++ lib/efi_loader/efi_device_path_to_text.c | 9 +++++++++ 5 files changed, 67 insertions(+) diff --git a/cmd/bootefi.c b/cmd/bootefi.c index b7087e3da8..478bc116e2 100644 --- a/cmd/bootefi.c +++ b/cmd/bootefi.c @@ -127,6 +127,7 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt, { struct efi_loaded_image loaded_image_info = {}; struct efi_object loaded_image_info_obj = {}; + struct efi_device_path *memdp = NULL; ulong ret; ulong (*entry)(void *image_handle, struct efi_system_table *st) @@ -135,6 +136,20 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt, const efi_guid_t fdt_guid = EFI_FDT_GUID; bootm_headers_t img = { 0 }; + /* + * Special case for efi payload not loaded from disk, such as + * 'bootefi hello' or for example payload loaded directly into + * memory via jtag/etc: + */ + if (!device_path && !image_path) { + printf("WARNING: using memory device/image path, this may confuse some payloads!\n"); + /* actual addresses filled in after efi_load_pe() */ + memdp = efi_dp_from_mem(0, 0, 0); + device_path = image_path = memdp; + } else { + assert(device_path && image_path); + } + /* Initialize and populate EFI object list */ if (!efi_obj_list_initalized) efi_init_obj_list(); @@ -181,6 +196,14 @@ static unsigned long do_bootefi_exec(void *efi, void *fdt, goto exit; } + if (memdp) { + struct efi_device_path_memory *mdp = (void *)memdp; + mdp->memory_type = loaded_image_info.image_code_type; + mdp->start_address = (uintptr_t)loaded_image_info.image_base; + mdp->end_address = mdp->start_address + + loaded_image_info.image_size; + } + /* we don't support much: */ env_set("efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported", "{ro,boot}(blob)0000000000000000"); diff --git a/include/efi_api.h b/include/efi_api.h index a9a6494afe..94c15b279a 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -297,8 +297,16 @@ struct efi_mac_addr { } __packed; #define DEVICE_PATH_TYPE_HARDWARE_DEVICE 0x01 +# define DEVICE_PATH_SUB_TYPE_MEMORY 0x03 # define DEVICE_PATH_SUB_TYPE_VENDOR 0x04 +struct efi_device_path_memory { + struct efi_device_path dp; + u32 memory_type; + u64 start_address; + u64 end_address; +} __packed; + struct efi_device_path_vendor { struct efi_device_path dp; efi_guid_t guid; diff --git a/include/efi_loader.h b/include/efi_loader.h index e1179b7dcd..1b92edbd77 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -259,6 +259,9 @@ struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part); struct efi_device_path *efi_dp_from_file(struct blk_desc *desc, int part, const char *path); struct efi_device_path *efi_dp_from_eth(void); +struct efi_device_path *efi_dp_from_mem(uint32_t mem_type, + uint64_t start_address, + uint64_t end_address); void efi_dp_split_file_path(struct efi_device_path *full_path, struct efi_device_path **device_path, struct efi_device_path **file_path); 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; From 2d5dc2a52d412964031c9920d354ad5bdc91c654 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 10 Oct 2017 08:23:01 -0400 Subject: [PATCH 36/37] efi_loader: console support for color attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shell.efi uses this, and supporting color attributes makes things look nicer. Map the EFI fg/bg color attributes to ANSI escape sequences. Not all colors have a perfect match, but spec just says "Devices supporting a different number of text colors are required to emulate the above colors to the best of the device’s capabilities". Signed-off-by: Rob Clark Tested-by: Heinrich Schuchardt Reviewed-by: Alexander Graf [agraf: s/unsigned/unsigned int/] Signed-off-by: Alexander Graf --- include/efi_api.h | 33 +++++++++++++++++++++++++++++++++ lib/efi_loader/efi_console.c | 27 +++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 94c15b279a..fcd7483ab2 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -433,6 +433,39 @@ struct simple_text_output_mode { EFI_GUID(0x387477c2, 0x69c7, 0x11d2, \ 0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_BLACK 0x00 +#define EFI_BLUE 0x01 +#define EFI_GREEN 0x02 +#define EFI_CYAN 0x03 +#define EFI_RED 0x04 +#define EFI_MAGENTA 0x05 +#define EFI_BROWN 0x06 +#define EFI_LIGHTGRAY 0x07 +#define EFI_BRIGHT 0x08 +#define EFI_DARKGRAY 0x08 +#define EFI_LIGHTBLUE 0x09 +#define EFI_LIGHTGREEN 0x0a +#define EFI_LIGHTCYAN 0x0b +#define EFI_LIGHTRED 0x0c +#define EFI_LIGHTMAGENTA 0x0d +#define EFI_YELLOW 0x0e +#define EFI_WHITE 0x0f +#define EFI_BACKGROUND_BLACK 0x00 +#define EFI_BACKGROUND_BLUE 0x10 +#define EFI_BACKGROUND_GREEN 0x20 +#define EFI_BACKGROUND_CYAN 0x30 +#define EFI_BACKGROUND_RED 0x40 +#define EFI_BACKGROUND_MAGENTA 0x50 +#define EFI_BACKGROUND_BROWN 0x60 +#define EFI_BACKGROUND_LIGHTGRAY 0x70 + +/* extract foreground color from EFI attribute */ +#define EFI_ATTR_FG(attr) ((attr) & 0x07) +/* treat high bit of FG as bright/bold (similar to edk2) */ +#define EFI_ATTR_BOLD(attr) (((attr) >> 3) & 0x01) +/* extract background color from EFI attribute */ +#define EFI_ATTR_BG(attr) (((attr) >> 4) & 0x7) + struct efi_simple_text_output_protocol { void *reset; efi_status_t (EFIAPI *output_string)( diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 1bdf36b4ae..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( From abe994633b2ad56c5eea87c9253873f41dab477d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 13 Oct 2017 01:00:05 +0200 Subject: [PATCH 37/37] efi_selftest: correctly check return values When cancelling the timer we should check the return value provided by the set_timer service. Signed-off-by: Heinrich Schuchardt Signed-off-by: Alexander Graf --- lib/efi_selftest/efi_selftest_events.c | 2 +- lib/efi_selftest/efi_selftest_tpl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/efi_selftest/efi_selftest_events.c b/lib/efi_selftest/efi_selftest_events.c index b2cdc150da..081f31257f 100644 --- a/lib/efi_selftest/efi_selftest_events.c +++ b/lib/efi_selftest/efi_selftest_events.c @@ -186,7 +186,7 @@ static int execute(void) return EFI_ST_FAILURE; } ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0); - if (index != 0) { + if (ret != EFI_SUCCESS) { efi_st_error("Could not cancel timer\n"); return EFI_ST_FAILURE; } diff --git a/lib/efi_selftest/efi_selftest_tpl.c b/lib/efi_selftest/efi_selftest_tpl.c index b8c0e70262..ddb67ed268 100644 --- a/lib/efi_selftest/efi_selftest_tpl.c +++ b/lib/efi_selftest/efi_selftest_tpl.c @@ -207,7 +207,7 @@ static int execute(void) return EFI_ST_FAILURE; } ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0); - if (index != 0) { + if (ret != EFI_SUCCESS) { efi_st_error("Could not cancel timer\n"); return EFI_ST_FAILURE; }