1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * efi_selftest_register_notify
4  *
5  * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
6  *
7  * This unit test checks the following protocol services:
8  * InstallProtocolInterface, UninstallProtocolInterface,
9  * RegisterProtocolNotify, CreateEvent, CloseEvent.
10  */
11 
12 #include <efi_selftest.h>
13 
14 /*
15  * The test currently does not actually call the interface function.
16  * So this is just a dummy structure.
17  */
18 struct interface {
19 	void (EFIAPI * inc)(void);
20 };
21 
22 struct context {
23 	void *registration_key;
24 	efi_uintn_t notify_count;
25 	efi_uintn_t handle_count;
26 	efi_handle_t *handles;
27 	efi_status_t ret;
28 };
29 
30 static struct efi_boot_services *boottime;
31 static efi_guid_t guid1 =
32 	EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a,
33 		 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf);
34 static efi_guid_t guid2 =
35 	EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77,
36 		 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f);
37 static struct context context;
38 static struct efi_event *event;
39 
40 /*
41  * Notification function, increments the notification count if parameter
42  * context is provided.
43  *
44  * @event	notified event
45  * @context	pointer to the notification count
46  */
notify(struct efi_event * event,void * context)47 static void EFIAPI notify(struct efi_event *event, void *context)
48 {
49 	struct context *cp = context;
50 	efi_uintn_t handle_count;
51 	efi_handle_t *handles;
52 
53 	cp->notify_count++;
54 
55 	for (;;) {
56 		cp->ret = boottime->locate_handle_buffer(BY_REGISTER_NOTIFY,
57 							 NULL,
58 							 cp->registration_key,
59 							 &handle_count,
60 							 &handles);
61 		if (cp->ret != EFI_SUCCESS)
62 			break;
63 		cp->handle_count += handle_count;
64 		cp->handles = handles;
65 	}
66 }
67 
68 /*
69  * Setup unit test.
70  *
71  * @handle:	handle of the loaded image
72  * @systable:	system table
73  */
setup(const efi_handle_t img_handle,const struct efi_system_table * systable)74 static int setup(const efi_handle_t img_handle,
75 		 const struct efi_system_table *systable)
76 {
77 	efi_status_t ret;
78 
79 	boottime = systable->boottime;
80 
81 	ret = boottime->create_event(EVT_NOTIFY_SIGNAL,
82 				     TPL_CALLBACK, notify, &context,
83 				     &event);
84 	if (ret != EFI_SUCCESS) {
85 		efi_st_error("could not create event\n");
86 		return EFI_ST_FAILURE;
87 	}
88 
89 	ret = boottime->register_protocol_notify(&guid1, event,
90 						 &context.registration_key);
91 	if (ret != EFI_SUCCESS) {
92 		efi_st_error("could not register event\n");
93 		return EFI_ST_FAILURE;
94 	}
95 
96 	return EFI_ST_SUCCESS;
97 }
98 
99 /*
100  * Tear down unit test.
101  *
102  */
teardown(void)103 static int teardown(void)
104 {
105 	efi_status_t ret;
106 
107 	if (event) {
108 		ret = boottime->close_event(event);
109 		event = NULL;
110 		if (ret != EFI_SUCCESS) {
111 			efi_st_error("could not close event\n");
112 			return EFI_ST_FAILURE;
113 		}
114 	}
115 
116 	return EFI_ST_SUCCESS;
117 }
118 
119 /*
120  * Execute unit test.
121  *
122  */
execute(void)123 static int execute(void)
124 {
125 	efi_status_t ret;
126 	efi_handle_t handle1 = NULL, handle2 = NULL;
127 	struct interface *interface;
128 	struct interface interface1, interface2;
129 
130 	ret = boottime->install_protocol_interface(&handle1, &guid1,
131 						   EFI_NATIVE_INTERFACE,
132 						   &interface1);
133 	if (ret != EFI_SUCCESS) {
134 		efi_st_error("could not install interface\n");
135 		return EFI_ST_FAILURE;
136 	}
137 	if (!context.notify_count) {
138 		efi_st_error("install was not notified\n");
139 		return EFI_ST_FAILURE;
140 	}
141 	if (context.notify_count > 1) {
142 		efi_st_error("install was notified too often\n");
143 		return EFI_ST_FAILURE;
144 	}
145 	if (context.handle_count != 1) {
146 		efi_st_error("LocateHandle failed\n");
147 		return EFI_ST_FAILURE;
148 	}
149 	interface = NULL;
150 	ret = boottime->open_protocol(handle1, &guid1, (void**)&interface,
151 				      NULL, NULL,
152 				      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
153 	if (ret != EFI_SUCCESS) {
154 		efi_st_error("Cannot find installed protocol on handle\n");
155 		return EFI_ST_FAILURE;
156 	}
157 	if (interface != &interface1) {
158 		efi_st_error("Wrong interface after install\n");
159 		return EFI_ST_FAILURE;
160 	}
161 	ret = boottime->free_pool(context.handles);
162 	if (ret != EFI_SUCCESS) {
163 		efi_st_error("FreePool failed\n");
164 		return EFI_ST_FAILURE;
165 	}
166 	context.notify_count = 0;
167 	ret = boottime->install_protocol_interface(&handle1, &guid2,
168 						   EFI_NATIVE_INTERFACE,
169 						   &interface1);
170 	if (ret != EFI_SUCCESS) {
171 		efi_st_error("could not install interface\n");
172 		return EFI_ST_FAILURE;
173 	}
174 	if (context.notify_count) {
175 		efi_st_error("wrong protocol was notified\n");
176 		return EFI_ST_FAILURE;
177 	}
178 	context.notify_count = 0;
179 	ret = boottime->reinstall_protocol_interface(handle1, &guid1,
180 						     &interface1, &interface2);
181 	if (ret != EFI_SUCCESS) {
182 		efi_st_error("could not reinstall interface\n");
183 		return EFI_ST_FAILURE;
184 	}
185 	if (!context.notify_count) {
186 		efi_st_error("reinstall was not notified\n");
187 		return EFI_ST_FAILURE;
188 	}
189 	if (context.notify_count > 1) {
190 		efi_st_error("reinstall was notified too often\n");
191 		return EFI_ST_FAILURE;
192 	}
193 	if (context.handle_count != 2) {
194 		efi_st_error("LocateHandle failed\n");
195 		return EFI_ST_FAILURE;
196 	}
197 	ret = boottime->free_pool(context.handles);
198 	if (ret != EFI_SUCCESS) {
199 		efi_st_error("FreePool failed\n");
200 		return EFI_ST_FAILURE;
201 	}
202 	interface = NULL;
203 	ret = boottime->open_protocol(handle1, &guid1, (void**)&interface,
204 				      NULL, NULL,
205 				      EFI_OPEN_PROTOCOL_GET_PROTOCOL);
206 	if (ret != EFI_SUCCESS) {
207 		efi_st_error("Cannot find reinstalled protocol on handle\n");
208 		return EFI_ST_FAILURE;
209 	}
210 	if (interface != &interface2) {
211 		efi_st_error("Wrong interface after reinstall\n");
212 		return EFI_ST_FAILURE;
213 	}
214 	context.notify_count = 0;
215 	ret = boottime->install_protocol_interface(&handle2, &guid1,
216 						   EFI_NATIVE_INTERFACE,
217 						   &interface1);
218 	if (ret != EFI_SUCCESS) {
219 		efi_st_error("could not install interface\n");
220 		return EFI_ST_FAILURE;
221 	}
222 	if (!context.notify_count) {
223 		efi_st_error("install was not notified\n");
224 		return EFI_ST_FAILURE;
225 	}
226 	if (context.notify_count > 1) {
227 		efi_st_error("install was notified too often\n");
228 		return EFI_ST_FAILURE;
229 	}
230 	if (context.handle_count != 3) {
231 		efi_st_error("LocateHandle failed\n");
232 		return EFI_ST_FAILURE;
233 	}
234 	if (context.ret != EFI_NOT_FOUND) {
235 		efi_st_error("LocateHandle did not return EFI_NOT_FOUND\n");
236 		return EFI_ST_FAILURE;
237 	}
238 	ret = boottime->free_pool(context.handles);
239 	if (ret != EFI_SUCCESS) {
240 		efi_st_error("FreePool failed\n");
241 		return EFI_ST_FAILURE;
242 	}
243 
244 	ret = boottime->uninstall_multiple_protocol_interfaces
245 			(handle1, &guid1, &interface2,
246 			 &guid2, &interface1, NULL);
247 	if (ret != EFI_SUCCESS) {
248 		efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
249 		return EFI_ST_FAILURE;
250 	}
251 	ret = boottime->uninstall_multiple_protocol_interfaces
252 			(handle2, &guid1, &interface1, NULL);
253 	if (ret != EFI_SUCCESS) {
254 		efi_st_error("UninstallMultipleProtocolInterfaces failed\n");
255 		return EFI_ST_FAILURE;
256 	}
257 
258 	return EFI_ST_SUCCESS;
259 }
260 
261 EFI_UNIT_TEST(regprotnot) = {
262 	.name = "register protocol notify",
263 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
264 	.setup = setup,
265 	.execute = execute,
266 	.teardown = teardown,
267 };
268