| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * efi_selftest_tpl |
| * |
| * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| * |
| * This unit test uses timer events to check the handling of |
| * task priority levels. |
| */ |
| |
| #include <efi_selftest.h> |
| |
| static struct efi_event *event_notify; |
| static struct efi_event *event_wait; |
| static unsigned int notification_count; |
| static struct efi_boot_services *boottime; |
| |
| /* |
| * Notification function, increments the notification count. |
| * |
| * @event notified event |
| * @context pointer to the notification count |
| */ |
| static void EFIAPI notify(struct efi_event *event, void *context) |
| { |
| unsigned int *count = context; |
| |
| if (count) |
| ++*count; |
| } |
| |
| /* |
| * Setup unit test. |
| * |
| * Create two timer events. |
| * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT. |
| * |
| * @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; |
| |
| ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, |
| TPL_CALLBACK, notify, |
| (void *)¬ification_count, |
| &event_notify); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("could not create event\n"); |
| 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 EFI_ST_FAILURE; |
| } |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Tear down unit test. |
| * |
| * Close the events created in setup. |
| * |
| * @return: EFI_ST_SUCCESS for success |
| */ |
| static int teardown(void) |
| { |
| efi_status_t ret; |
| |
| if (event_notify) { |
| ret = boottime->close_event(event_notify); |
| event_notify = NULL; |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("could not close event\n"); |
| return EFI_ST_FAILURE; |
| } |
| } |
| if (event_wait) { |
| ret = boottime->close_event(event_wait); |
| event_wait = NULL; |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("could not close event\n"); |
| return EFI_ST_FAILURE; |
| } |
| } |
| boottime->restore_tpl(TPL_APPLICATION); |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Execute unit test. |
| * |
| * Run a 10 ms periodic timer and check that it is called 10 times |
| * while waiting for 100 ms single shot timer. |
| * |
| * Raise the TPL level to the level of the 10 ms timer and observe |
| * that the notification function is not called again. |
| * |
| * Lower the TPL level and check that the queued notification |
| * function is called. |
| * |
| * @return: EFI_ST_SUCCESS for success |
| */ |
| static int execute(void) |
| { |
| efi_uintn_t index; |
| efi_status_t ret; |
| efi_uintn_t old_tpl; |
| |
| /* Set 10 ms timer */ |
| 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 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 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 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 EFI_ST_FAILURE; |
| } |
| if (index != 0) { |
| efi_st_error("WaitForEvent returned wrong index\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (notification_count < 8 || notification_count > 12) { |
| efi_st_printf( |
| "Notification count with TPL level TPL_APPLICATION: %u\n", |
| notification_count); |
| efi_st_error("Incorrect timing of events\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->set_timer(event_notify, EFI_TIMER_STOP, 0); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Could not cancel timer\n"); |
| 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 EFI_ST_FAILURE; |
| } |
| /* Set 10 ms timer */ |
| 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 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 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 EFI_ST_FAILURE; |
| } |
| if (notification_count != 0) { |
| efi_st_printf( |
| "Notification count with TPL level TPL_CALLBACK: %u\n", |
| notification_count); |
| efi_st_error("Suppressed timer fired\n"); |
| 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 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 EFI_ST_FAILURE; |
| } |
| if (notification_count < 1) { |
| efi_st_printf( |
| "Notification count with TPL level TPL_APPLICATION: %u\n", |
| notification_count); |
| efi_st_error("Queued timer event did not fire\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->set_timer(event_wait, EFI_TIMER_STOP, 0); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Could not cancel timer\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| EFI_UNIT_TEST(tpl) = { |
| .name = "task priority levels", |
| .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
| .setup = setup, |
| .execute = execute, |
| .teardown = teardown, |
| }; |