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