1 /*
2  * efi_selftest_events
3  *
4  * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  *
8  * This unit test checks that the notification function of an
9  * EVT_SIGNAL_EXIT_BOOT_SERVICES event is called exactly once.
10  */
11 
12 #include <efi_selftest.h>
13 
14 static struct efi_boot_services *boottime;
15 static struct efi_event *event_notify;
16 static unsigned int counter;
17 
18 /*
19  * Notification function, increments a counter.
20  *
21  * @event	notified event
22  * @context	pointer to the counter
23  */
24 static void EFIAPI notify(struct efi_event *event, void *context)
25 {
26 	if (!context)
27 		return;
28 	++*(unsigned int *)context;
29 }
30 
31 /*
32  * Setup unit test.
33  *
34  * Create an EVT_SIGNAL_EXIT_BOOT_SERVICES event.
35  *
36  * @handle:	handle of the loaded image
37  * @systable:	system table
38  */
39 static int setup(const efi_handle_t handle,
40 		 const struct efi_system_table *systable)
41 {
42 	efi_status_t ret;
43 
44 	boottime = systable->boottime;
45 
46 	counter = 0;
47 	ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
48 				     TPL_CALLBACK, notify, (void *)&counter,
49 				     &event_notify);
50 	if (ret != EFI_SUCCESS) {
51 		efi_st_error("could not create event\n");
52 		return 1;
53 	}
54 	return 0;
55 }
56 
57 /*
58  * Tear down unit test.
59  *
60  * Close the event created in setup.
61  */
62 static int teardown(void)
63 {
64 	efi_status_t ret;
65 
66 	if (event_notify) {
67 		ret = boottime->close_event(event_notify);
68 		event_notify = NULL;
69 		if (ret != EFI_SUCCESS) {
70 			efi_st_error("could not close event\n");
71 			return 1;
72 		}
73 	}
74 	return 0;
75 }
76 
77 /*
78  * Execute unit test.
79  *
80  * Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
81  * event has been called.
82  *
83  * Call ExitBootServices again and check that the notification function is
84  * not called again.
85  */
86 static int execute(void)
87 {
88 	if (counter != 1) {
89 		efi_st_error("ExitBootServices was not notified");
90 		return 1;
91 	}
92 	efi_st_exit_boot_services();
93 	if (counter != 1) {
94 		efi_st_error("ExitBootServices was notified twice");
95 		return 1;
96 	}
97 	return 0;
98 }
99 
100 EFI_UNIT_TEST(exitbootservices) = {
101 	.name = "ExitBootServices",
102 	.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
103 	.setup = setup,
104 	.execute = execute,
105 	.teardown = teardown,
106 };
107