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 FILE_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 ) {
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  */
115  controller = NULL;
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 ) {
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;
201  attributes = EFI_OPEN_PROTOCOL_GET_PROTOCOL;
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  */
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 ) {
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  */
260  controller = handle;
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  */
283 
284  /* Sanity checks */
285  assert ( handle != NULL );
286  assert ( protocol != NULL );
287 
288  /* Close protocol */
289  controller = handle;
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 ) {
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 ) {
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 }
EFI_BOOT_SERVICES * BootServices
A pointer to the EFI Boot Services Table.
Definition: UefiSpec.h:2099
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:175
#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
Definition: UefiSpec.h:1357
int efi_open_unsafe_untyped(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
Open protocol for unsafe persistent use.
Definition: efi_open.c:181
128 bit buffer containing a unique identifier value.
Definition: Base.h:216
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:241
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
#define EFI_OPEN_PROTOCOL_BY_DRIVER
Definition: UefiSpec.h:1358
#define EFI_OPEN_PROTOCOL_EXCLUSIVE
Definition: UefiSpec.h:1359
EFI_CLOSE_PROTOCOL CloseProtocol
Definition: UefiSpec.h:2001
int efi_open_untyped(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
Open (or test) protocol for ephemeral use.
Definition: efi_open.c:97
#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL
Definition: UefiSpec.h:1356
void efi_close_unsafe(EFI_HANDLE handle, EFI_GUID *protocol)
Close protocol opened for unsafe persistent use.
Definition: efi_open.c:219
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
An object interface.
Definition: interface.h:125
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
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL
Definition: UefiSpec.h:1355
EFI Boot Services Table.
Definition: UefiSpec.h:1931
EFI_HANDLE efi_image_handle
Image handle passed to entry point.
Definition: efi_init.c:36
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:302
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
Definition: UefiBaseType.h:32
EFI_SYSTEM_TABLE * efi_systab
EFI_OPEN_PROTOCOL OpenProtocol
Definition: UefiSpec.h:2000
uint16_t protocol
Protocol ID.
Definition: stp.h:19
FILE_SECBOOT(PERMITTED)
uint16_t handle
Handle.
Definition: smbios.h:17
#define NULL
NULL pointer (VOID *)
Definition: Base.h:322
Definition: efi.h:62
if(natsemi->flags &NATSEMI_64BIT) return 1