1 /*
2  * efi_selftest_exitbootservices
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 notification_count;
17 
18 /*
19  * Notification function, increments the notification count.
20  *
21  * @event	notified event
22  * @context	pointer to the notification count
23  */
24 static void EFIAPI notify(struct efi_event *event, void *context)
25 {
26 	unsigned int *count = context;
27 
28 	++*count;
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  * @return:	EFI_ST_SUCCESS for success
39  */
40 static int setup(const efi_handle_t handle,
41 		 const struct efi_system_table *systable)
42 {
43 	efi_status_t ret;
44 
45 	boottime = systable->boottime;
46 
47 	notification_count = 0;
48 	ret = boottime->create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES,
49 				     TPL_CALLBACK, notify,
50 				     (void *)&notification_count,
51 				     &event_notify);
52 	if (ret != EFI_SUCCESS) {
53 		efi_st_error("could not create event\n");
54 		return EFI_ST_FAILURE;
55 	}
56 	return EFI_ST_SUCCESS;
57 }
58 
59 /*
60  * Tear down unit test.
61  *
62  * Close the event created in setup.
63  *
64  * @return:	EFI_ST_SUCCESS for success
65  */
66 static int teardown(void)
67 {
68 	efi_status_t ret;
69 
70 	if (event_notify) {
71 		ret = boottime->close_event(event_notify);
72 		event_notify = NULL;
73 		if (ret != EFI_SUCCESS) {
74 			efi_st_error("could not close event\n");
75 			return EFI_ST_FAILURE;
76 		}
77 	}
78 	return EFI_ST_SUCCESS;
79 }
80 
81 /*
82  * Execute unit test.
83  *
84  * Check that the notification function of the EVT_SIGNAL_EXIT_BOOT_SERVICES
85  * event has been called.
86  *
87  * Call ExitBootServices again and check that the notification function is
88  * not called again.
89  *
90  * @return:	EFI_ST_SUCCESS for success
91  */
92 static int execute(void)
93 {
94 	if (notification_count != 1) {
95 		efi_st_error("ExitBootServices was not notified\n");
96 		return EFI_ST_FAILURE;
97 	}
98 	efi_st_exit_boot_services();
99 	if (notification_count != 1) {
100 		efi_st_error("ExitBootServices was notified twice\n");
101 		return EFI_ST_FAILURE;
102 	}
103 	return EFI_ST_SUCCESS;
104 }
105 
106 EFI_UNIT_TEST(exitbootservices) = {
107 	.name = "ExitBootServices",
108 	.phase = EFI_SETUP_BEFORE_BOOTTIME_EXIT,
109 	.setup = setup,
110 	.execute = execute,
111 	.teardown = teardown,
112 };
113