iPXE
efi_open.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27/** @file
28 *
29 * EFI protocol opening and closing
30 *
31 * The UEFI model for opening and closing protocols is broken by
32 * design and cannot be repaired.
33 *
34 * Calling OpenProtocol() to obtain a protocol interface pointer does
35 * not, in general, provide any guarantees about the lifetime of that
36 * pointer. It is theoretically possible that the pointer has already
37 * become invalid by the time that OpenProtocol() returns the pointer
38 * to its caller. (This can happen when a USB device is physically
39 * removed, for example.)
40 *
41 * Various UEFI design flaws make it occasionally necessary to hold on
42 * to a protocol interface pointer despite the total lack of
43 * guarantees that the pointer will remain valid.
44 *
45 * The UEFI driver model overloads the semantics of OpenProtocol() to
46 * accommodate the use cases of recording a driver attachment (which
47 * is modelled as opening a protocol with EFI_OPEN_PROTOCOL_BY_DRIVER
48 * attributes) and recording the existence of a related child
49 * controller (which is modelled as opening a protocol with
50 * EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER attributes).
51 *
52 * The parameters defined for CloseProtocol() are not sufficient to
53 * allow the implementation to precisely identify the matching call to
54 * OpenProtocol(). While the UEFI model appears to allow for matched
55 * open and close pairs, this is merely an illusion. Calling
56 * CloseProtocol() will delete *all* matching records in the protocol
57 * open information tables.
58 *
59 * Since the parameters defined for CloseProtocol() do not include the
60 * attributes passed to OpenProtocol(), this means that a matched
61 * open/close pair using EFI_OPEN_PROTOCOL_GET_PROTOCOL can
62 * inadvertently end up deleting the record that defines a driver
63 * attachment or the existence of a child controller. This in turn
64 * can cause some very unexpected side effects, such as allowing other
65 * UEFI drivers to start controlling hardware to which iPXE believes
66 * it has exclusive access. This rarely ends well.
67 *
68 * To prevent this kind of inadvertent deletion, we establish a
69 * convention for four different types of protocol opening:
70 *
71 * - ephemeral opens: always opened with ControllerHandle = NULL
72 *
73 * - unsafe opens: always opened with ControllerHandle = AgentHandle
74 *
75 * - by-driver opens: always opened with ControllerHandle = Handle
76 *
77 * - by-child opens: always opened with ControllerHandle != Handle
78 *
79 * This convention ensures that the four types of open never overlap
80 * within the set of parameters defined for CloseProtocol(), and so a
81 * close of one type cannot inadvertently delete the record
82 * corresponding to a different type.
83 */
84
85#include <assert.h>
86#include <errno.h>
87#include <ipxe/efi/efi.h>
88
89/**
90 * Open (or test) protocol for ephemeral use
91 *
92 * @v handle EFI handle
93 * @v protocol Protocol GUID
94 * @v interface Protocol interface pointer to fill in (or NULL to test)
95 * @ret rc Return status code
96 */
98 void **interface ) {
99 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
102 unsigned int attributes;
103 EFI_STATUS efirc;
104 int rc;
105
106 /* Sanity checks */
107 assert ( handle != NULL );
108 assert ( protocol != NULL );
109
110 /* Open protocol
111 *
112 * We set ControllerHandle to NULL to avoid collisions with
113 * other open types.
114 */
116 attributes = ( interface ? EFI_OPEN_PROTOCOL_GET_PROTOCOL :
118 if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent,
119 controller, attributes ) ) != 0 ) {
120 rc = -EEFI ( efirc );
121 if ( interface )
122 *interface = NULL;
123 return rc;
124 }
125
126 /* Close protocol immediately
127 *
128 * While it may seem prima facie unsafe to use a protocol
129 * after closing it, UEFI doesn't actually give us any safety
130 * even while the protocol is nominally open. Opening a
131 * protocol with EFI_OPEN_PROTOCOL_GET_PROTOCOL attributes
132 * does not in any way ensure that the interface pointer
133 * remains valid: there are no locks or notifications
134 * associated with these "opens".
135 *
136 * The only way to obtain a (partially) guaranteed persistent
137 * interface pointer is to open the protocol with the
138 * EFI_OPEN_PROTOCOL_BY_DRIVER attributes. This is not
139 * possible in the general case, since UEFI permits only a
140 * single image at a time to have the protocol opened with
141 * these attributes.
142 *
143 * We can therefore obtain at best an ephemeral interface
144 * pointer: one that is guaranteed to remain valid only for as
145 * long as we do not relinquish the thread of control.
146 *
147 * (Since UEFI permits calls to UninstallProtocolInterface()
148 * at levels up to and including TPL_NOTIFY, this means that
149 * we technically cannot rely on the pointer remaining valid
150 * unless the caller is itself running at TPL_NOTIFY. This is
151 * clearly impractical, and large portions of the EDK2
152 * codebase presume that using EFI_OPEN_PROTOCOL_GET_PROTOCOL
153 * is safe at lower TPLs.)
154 *
155 * Closing is not strictly necessary for protocols opened
156 * ephemerally (i.e. using EFI_OPEN_PROTOCOL_GET_PROTOCOL or
157 * EFI_OPEN_PROTOCOL_TEST_PROTOCOL), but avoids polluting the
158 * protocol open information tables with stale data.
159 *
160 * Closing immediately also simplifies the callers' code
161 * paths, since they do not need to worry about closing the
162 * protocol.
163 *
164 * The overall effect is equivalent to using HandleProtocol(),
165 * but without the associated pollution of the protocol open
166 * information tables, and with improved traceability.
167 */
168 bs->CloseProtocol ( handle, protocol, agent, controller );
169
170 return 0;
171}
172
173/**
174 * Open protocol for unsafe persistent use
175 *
176 * @v handle EFI handle
177 * @v protocol Protocol GUID
178 * @v interface Protocol interface pointer to fill in
179 * @ret rc Return status code
180 */
182 void **interface ) {
183 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
186 unsigned int attributes;
187 EFI_STATUS efirc;
188 int rc;
189
190 /* Sanity checks */
191 assert ( handle != NULL );
192 assert ( protocol != NULL );
193 assert ( interface != NULL );
194
195 /* Open protocol
196 *
197 * We set ControllerHandle equal to AgentHandle to avoid
198 * collisions with other open types.
199 */
200 controller = agent;
202 if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent,
203 controller, attributes ) ) != 0 ) {
204 rc = -EEFI ( efirc );
205 *interface = NULL;
206 return rc;
207 }
208
209 return 0;
210}
211
212/**
213 * Close protocol opened for unsafe persistent use
214 *
215 * @v handle EFI handle
216 * @v protocol Protocol GUID
217 * @v child Child controller handle
218 */
220 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
223
224 /* Sanity checks */
225 assert ( handle != NULL );
226 assert ( protocol != NULL );
227
228 /* Close protocol */
229 controller = agent;
230 bs->CloseProtocol ( handle, protocol, agent, controller );
231}
232
233/**
234 * Open protocol for persistent use by a driver
235 *
236 * @v handle EFI handle
237 * @v protocol Protocol GUID
238 * @v interface Protocol interface pointer to fill in
239 * @ret rc Return status code
240 */
242 void **interface ) {
243 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
246 unsigned int attributes;
247 EFI_STATUS efirc;
248 int rc;
249
250 /* Sanity checks */
251 assert ( handle != NULL );
252 assert ( protocol != NULL );
253 assert ( interface != NULL );
254
255 /* Open protocol
256 *
257 * We set ControllerHandle equal to Handle to avoid collisions
258 * with other open types.
259 */
261 attributes = ( EFI_OPEN_PROTOCOL_BY_DRIVER |
263 if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent,
264 controller, attributes ) ) != 0 ) {
265 rc = -EEFI ( efirc );
266 *interface = NULL;
267 return rc;
268 }
269
270 return 0;
271}
272
273/**
274 * Close protocol opened for persistent use by a driver
275 *
276 * @v handle EFI handle
277 * @v protocol Protocol GUID
278 */
280 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
283
284 /* Sanity checks */
285 assert ( handle != NULL );
286 assert ( protocol != NULL );
287
288 /* Close protocol */
290 bs->CloseProtocol ( handle, protocol, agent, controller );
291}
292
293/**
294 * Open protocol for persistent use by a child controller
295 *
296 * @v handle EFI handle
297 * @v protocol Protocol GUID
298 * @v child Child controller handle
299 * @v interface Protocol interface pointer to fill in
300 * @ret rc Return status code
301 */
303 EFI_HANDLE child, void **interface ) {
304 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
307 unsigned int attributes;
308 EFI_STATUS efirc;
309 int rc;
310
311 /* Sanity checks */
312 assert ( handle != NULL );
313 assert ( protocol != NULL );
314 assert ( child != NULL );
315 assert ( interface != NULL );
316
317 /* Open protocol
318 *
319 * We set ControllerHandle to a non-NULL value distinct from
320 * both Handle and AgentHandle to avoid collisions with other
321 * open types.
322 */
323 controller = child;
324 assert ( controller != handle );
325 assert ( controller != agent );
327 if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent,
328 controller, attributes ) ) != 0 ) {
329 rc = -EEFI ( efirc );
330 *interface = NULL;
331 return rc;
332 }
333
334 return 0;
335}
336
337/**
338 * Close protocol opened for persistent use by a child controller
339 *
340 * @v handle EFI handle
341 * @v protocol Protocol GUID
342 * @v child Child controller handle
343 */
345 EFI_HANDLE child ) {
346 EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
349
350 /* Sanity checks */
351 assert ( handle != NULL );
352 assert ( protocol != NULL );
353 assert ( child != NULL );
354
355 /* Close protocol */
356 controller = child;
357 assert ( controller != handle );
358 assert ( controller != agent );
359 bs->CloseProtocol ( handle, protocol, agent, controller );
360}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
GUID EFI_GUID
128-bit buffer containing a unique identifier value.
#define EFI_OPEN_PROTOCOL_BY_DRIVER
Definition UefiSpec.h:1358
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL
Definition UefiSpec.h:1356
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL
Definition UefiSpec.h:1355
#define EFI_OPEN_PROTOCOL_EXCLUSIVE
Definition UefiSpec.h:1359
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
Definition UefiSpec.h:1357
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
Assertions.
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
EFI_HANDLE efi_image_handle
Image handle passed to entry point.
Definition efi_init.c:36
void efi_close_by_driver(EFI_HANDLE handle, EFI_GUID *protocol)
Close protocol opened for persistent use by a driver.
Definition efi_open.c:279
void efi_close_by_child(EFI_HANDLE handle, EFI_GUID *protocol, EFI_HANDLE child)
Close protocol opened for persistent use by a child controller.
Definition efi_open.c:344
int efi_open_by_child_untyped(EFI_HANDLE handle, EFI_GUID *protocol, EFI_HANDLE child, void **interface)
Open protocol for persistent use by a child controller.
Definition efi_open.c:302
void efi_close_unsafe(EFI_HANDLE handle, EFI_GUID *protocol)
Close protocol opened for unsafe persistent use.
Definition efi_open.c:219
int efi_open_by_driver_untyped(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
Open protocol for persistent use by a driver.
Definition efi_open.c:241
int efi_open_untyped(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
Open (or test) protocol for ephemeral use.
Definition efi_open.c:97
int efi_open_unsafe_untyped(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
Open protocol for unsafe persistent use.
Definition efi_open.c:181
Error codes.
uint8_t controller
CD-ROM controller number.
Definition int13.h:7
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
EFI API.
#define EFI_HANDLE
Definition efi.h:53
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition efi.h:175
EFI_SYSTEM_TABLE * efi_systab
uint16_t handle
Handle.
Definition smbios.h:5
if(natsemi->flags &NATSEMI_64BIT) return 1
uint16_t protocol
Protocol ID.
Definition stp.h:7
EFI Boot Services Table.
Definition UefiSpec.h:1931
EFI_CLOSE_PROTOCOL CloseProtocol
Definition UefiSpec.h:2001
EFI_OPEN_PROTOCOL OpenProtocol
Definition UefiSpec.h:2000
An object interface.
Definition interface.h:125