iPXE
efi_autoexec.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2021 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 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <ipxe/image.h>
30 #include <ipxe/init.h>
31 #include <ipxe/in.h>
32 #include <ipxe/efi/efi.h>
33 #include <ipxe/efi/efi_autoexec.h>
36 #include <ipxe/efi/Guid/FileInfo.h>
37 
38 /** @file
39  *
40  * EFI autoexec script
41  *
42  */
43 
44 /** Autoexec script filename */
45 static wchar_t efi_autoexec_wname[] = L"autoexec.ipxe";
46 
47 /** Autoexec script image name */
48 static char efi_autoexec_name[] = "autoexec.ipxe";
49 
50 /** Autoexec script (if any) */
51 static void *efi_autoexec;
52 
53 /** Autoexec script length */
54 static size_t efi_autoexec_len;
55 
56 /**
57  * Load autoexec script from filesystem
58  *
59  * @v device Device handle
60  * @ret rc Return status code
61  */
64  union {
65  void *interface;
67  } u;
68  struct {
70  CHAR16 name[ sizeof ( efi_autoexec_wname ) /
71  sizeof ( efi_autoexec_wname[0] ) ];
72  } info;
74  EFI_FILE_PROTOCOL *file;
75  UINTN size;
76  VOID *data;
77  EFI_STATUS efirc;
78  int rc;
79 
80  /* Open simple file system protocol */
81  if ( ( efirc = bs->OpenProtocol ( device,
83  &u.interface, efi_image_handle,
84  device,
86  rc = -EEFI ( efirc );
87  DBGC ( device, "EFI %s has no filesystem instance: %s\n",
88  efi_handle_name ( device ), strerror ( rc ) );
89  goto err_filesystem;
90  }
91 
92  /* Open root directory */
93  if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
94  rc = -EEFI ( efirc );
95  DBGC ( device, "EFI %s could not open volume: %s\n",
96  efi_handle_name ( device ), strerror ( rc ) );
97  goto err_volume;
98  }
99 
100  /* Open autoexec script */
101  if ( ( efirc = root->Open ( root, &file, efi_autoexec_wname,
102  EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
103  rc = -EEFI ( efirc );
104  DBGC ( device, "EFI %s has no %ls: %s\n",
106  strerror ( rc ) );
107  goto err_open;
108  }
109 
110  /* Get file information */
111  size = sizeof ( info );
112  if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
113  &info ) ) != 0 ) {
114  rc = -EEFI ( efirc );
115  DBGC ( device, "EFI %s could not get %ls info: %s\n",
117  strerror ( rc ) );
118  goto err_getinfo;
119  }
120  size = info.info.FileSize;
121 
122  /* Ignore zero-length files */
123  if ( ! size ) {
124  rc = -EINVAL;
125  DBGC ( device, "EFI %s has zero-length %ls\n",
127  goto err_empty;
128  }
129 
130  /* Allocate temporary copy */
131  if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
132  &data ) ) != 0 ) {
133  rc = -EEFI ( efirc );
134  DBGC ( device, "EFI %s could not allocate %ls: %s\n",
136  strerror ( rc ) );
137  goto err_alloc;
138  }
139 
140  /* Read file */
141  if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) {
142  rc = -EEFI ( efirc );
143  DBGC ( device, "EFI %s could not read %ls: %s\n",
145  strerror ( rc ) );
146  goto err_read;
147  }
148 
149  /* Record autoexec script */
150  efi_autoexec = data;
152  data = NULL;
153  DBGC ( device, "EFI %s found %ls\n",
155 
156  /* Success */
157  rc = 0;
158 
159  err_read:
160  if ( data )
161  bs->FreePool ( data );
162  err_alloc:
163  err_empty:
164  err_getinfo:
165  file->Close ( file );
166  err_open:
167  root->Close ( root );
168  err_volume:
171  err_filesystem:
172  return rc;
173 }
174 
175 /**
176  * Load autoexec script from TFTP server
177  *
178  * @v device Device handle
179  * @ret rc Return status code
180  */
183  union {
184  void *interface;
186  } u;
188  EFI_PXE_BASE_CODE_PACKET *packet;
189  union {
190  struct in_addr in;
192  } server;
193  size_t filename_max;
194  char *filename;
195  char *sep;
196  UINT64 size;
197  VOID *data;
198  EFI_STATUS efirc;
199  int rc;
200 
201  /* Open PXE base code protocol */
202  if ( ( efirc = bs->OpenProtocol ( device,
204  &u.interface, efi_image_handle,
205  device,
207  rc = -EEFI ( efirc );
208  DBGC ( device, "EFI %s has no PXE base code instance: %s\n",
209  efi_handle_name ( device ), strerror ( rc ) );
210  goto err_pxe;
211  }
212 
213  /* Do not attempt to parse DHCPv6 packets */
214  mode = u.pxe->Mode;
215  if ( mode->UsingIpv6 ) {
216  rc = -ENOTSUP;
217  DBGC ( device, "EFI %s has IPv6 PXE base code\n",
218  efi_handle_name ( device ) );
219  goto err_ipv6;
220  }
221 
222  /* Identify relevant reply packet */
223  if ( mode->PxeReplyReceived &&
224  mode->PxeReply.Dhcpv4.BootpBootFile[0] ) {
225  /* Use boot filename if present in PXE reply */
226  DBGC ( device, "EFI %s using PXE reply filename\n",
227  efi_handle_name ( device ) );
228  packet = &mode->PxeReply;
229  } else if ( mode->DhcpAckReceived &&
230  mode->DhcpAck.Dhcpv4.BootpBootFile[0] ) {
231  /* Otherwise, use boot filename if present in DHCPACK */
232  DBGC ( device, "EFI %s using DHCPACK filename\n",
233  efi_handle_name ( device ) );
234  packet = &mode->DhcpAck;
235  } else if ( mode->ProxyOfferReceived &&
236  mode->ProxyOffer.Dhcpv4.BootpBootFile[0] ) {
237  /* Otherwise, use boot filename if present in ProxyDHCPOFFER */
238  DBGC ( device, "EFI %s using ProxyDHCPOFFER filename\n",
239  efi_handle_name ( device ) );
240  packet = &mode->ProxyOffer;
241  } else {
242  /* No boot filename available */
243  rc = -ENOENT;
244  DBGC ( device, "EFI %s has no PXE boot filename\n",
245  efi_handle_name ( device ) );
246  goto err_packet;
247  }
248 
249  /* Allocate filename */
250  filename_max = ( sizeof ( packet->Dhcpv4.BootpBootFile )
251  + ( sizeof ( efi_autoexec_name ) - 1 /* NUL */ )
252  + 1 /* NUL */ );
253  filename = zalloc ( filename_max );
254  if ( ! filename ) {
255  rc = -ENOMEM;
256  goto err_filename;
257  }
258 
259  /* Extract next-server address and boot filename */
260  memset ( &server, 0, sizeof ( server ) );
261  memcpy ( &server.in, packet->Dhcpv4.BootpSiAddr,
262  sizeof ( server.in ) );
263  memcpy ( filename, packet->Dhcpv4.BootpBootFile,
264  sizeof ( packet->Dhcpv4.BootpBootFile ) );
265 
266  /* Update filename to autoexec script name */
267  sep = strrchr ( filename, '/' );
268  if ( ! sep )
269  sep = strrchr ( filename, '\\' );
270  if ( ! sep )
271  sep = ( filename - 1 );
272  strcpy ( ( sep + 1 ), efi_autoexec_name );
273 
274  /* Get file size */
275  if ( ( efirc = u.pxe->Mtftp ( u.pxe,
277  NULL, FALSE, &size, NULL, &server.ip,
278  ( ( UINT8 * ) filename ), NULL,
279  FALSE ) ) != 0 ) {
280  rc = -EEFI ( efirc );
281  DBGC ( device, "EFI %s could not get size of %s:%s: %s\n",
282  efi_handle_name ( device ), inet_ntoa ( server.in ),
283  filename, strerror ( rc ) );
284  goto err_size;
285  }
286 
287  /* Ignore zero-length files */
288  if ( ! size ) {
289  rc = -EINVAL;
290  DBGC ( device, "EFI %s has zero-length %s:%s\n",
291  efi_handle_name ( device ), inet_ntoa ( server.in ),
292  filename );
293  goto err_empty;
294  }
295 
296  /* Allocate temporary copy */
297  if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
298  &data ) ) != 0 ) {
299  rc = -EEFI ( efirc );
300  DBGC ( device, "EFI %s could not allocate %s:%s: %s\n",
301  efi_handle_name ( device ), inet_ntoa ( server.in ),
302  filename, strerror ( rc ) );
303  goto err_alloc;
304  }
305 
306  /* Download file */
307  if ( ( efirc = u.pxe->Mtftp ( u.pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
308  data, FALSE, &size, NULL, &server.ip,
309  ( ( UINT8 * ) filename ), NULL,
310  FALSE ) ) != 0 ) {
311  rc = -EEFI ( efirc );
312  DBGC ( device, "EFI %s could not download %s:%s: %s\n",
313  efi_handle_name ( device ), inet_ntoa ( server.in ),
314  filename, strerror ( rc ) );
315  goto err_download;
316  }
317 
318  /* Record autoexec script */
319  efi_autoexec = data;
321  data = NULL;
322  DBGC ( device, "EFI %s found %s:%s\n", efi_handle_name ( device ),
323  inet_ntoa ( server.in ), filename );
324 
325  /* Success */
326  rc = 0;
327 
328  err_download:
329  if ( data )
330  bs->FreePool ( data );
331  err_alloc:
332  err_empty:
333  err_size:
334  free ( filename );
335  err_filename:
336  err_packet:
337  err_ipv6:
340  err_pxe:
341  return rc;
342 }
343 
344 /**
345  * Load autoexec script
346  *
347  * @v device Device handle
348  * @ret rc Return status code
349  */
351  int rc;
352 
353  /* Sanity check */
354  assert ( efi_autoexec == NULL );
355  assert ( efi_autoexec_len == 0 );
356 
357  /* Try loading from file system, if supported */
358  if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 )
359  return 0;
360 
361  /* Try loading via TFTP, if supported */
362  if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 )
363  return 0;
364 
365  return -ENOENT;
366 }
367 
368 /**
369  * Register autoexec script
370  *
371  */
372 static void efi_autoexec_startup ( void ) {
375  struct image *image;
376 
377  /* Do nothing if we have no autoexec script */
378  if ( ! efi_autoexec )
379  return;
380 
381  /* Create autoexec image */
385  if ( ! image ) {
386  DBGC ( device, "EFI %s could not create %s\n",
388  return;
389  }
390  DBGC ( device, "EFI %s registered %s\n",
392 
393  /* Free temporary copy */
394  bs->FreePool ( efi_autoexec );
395  efi_autoexec = NULL;
396 }
397 
398 /** Autoexec script startup function */
399 struct startup_fn efi_autoexec_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
400  .name = "efi_autoexec",
401  .startup = efi_autoexec_startup,
402 };
EFI_PXE_BASE_CODE_MODE.
Definition: PxeBaseCode.h:272
static size_t efi_autoexec_len
Autoexec script length.
Definition: efi_autoexec.c:54
EFI_FILE_CLOSE Close
EFI_BOOT_SERVICES * BootServices
A pointer to the EFI Boot Services Table.
Definition: UefiSpec.h:2000
#define EINVAL
Invalid argument.
Definition: errno.h:428
#define STARTUP_NORMAL
Normal startup.
Definition: init.h:63
EFI_LOADED_IMAGE_PROTOCOL * efi_loaded_image
Loaded image protocol for this image.
Definition: efi_init.c:36
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Definition: ath9k_hw.c:1984
#define EEFI(efirc)
Convert an EFI status code to an iPXE status code.
Definition: efi.h:162
u32 info
Definition: ar9003_mac.h:67
__be32 in[4]
Definition: CIB_PRM.h:35
char * strrchr(const char *src, int character)
Find rightmost character within a string.
Definition: string.c:289
struct stp_switch root
Root switch.
Definition: stp.h:26
Error codes.
#define DBGC(...)
Definition: compiler.h:505
EFI_FILE_GET_INFO GetInfo
#define ENOENT
No such file or directory.
Definition: errno.h:514
struct image * image_memory(const char *name, userptr_t data, size_t len)
Create registered image from block of memory.
Definition: image.c:537
static char efi_autoexec_name[]
Autoexec script image name.
Definition: efi_autoexec.c:48
EFI_PXE_BASE_CODE_DHCPV4_PACKET Dhcpv4
Definition: PxeBaseCode.h:256
static int efi_autoexec_tftp(EFI_HANDLE device)
Load autoexec script from TFTP server.
Definition: efi_autoexec.c:181
unsigned short CHAR16
Definition: ProcessorBind.h:59
EFI_GUID efi_pxe_base_code_protocol_guid
PXE base code protocol GUID.
Definition: efi_guid.c:224
struct startup_fn efi_autoexec_startup_fn __startup_fn(STARTUP_NORMAL)
Autoexec script startup function.
EFI PXE Base Code Protocol definitions, which is used to access PXE-compatible devices for network ac...
An executable image.
Definition: image.h:24
EFI_PXE_BASE_CODE_PACKET PxeReply
Definition: PxeBaseCode.h:298
EFI_CLOSE_PROTOCOL CloseProtocol
Definition: UefiSpec.h:1906
const char * name
Definition: init.h:42
unsigned char UINT8
Definition: ProcessorBind.h:62
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
A startup/shutdown function.
Definition: init.h:41
#define ENOMEM
Not enough space.
Definition: errno.h:534
A hardware device.
Definition: device.h:73
void * memcpy(void *dest, const void *src, size_t len) __nonnull
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
An object interface.
Definition: interface.h:124
SimpleFileSystem protocol as defined in the UEFI 2.0 specification.
Executable images.
16-byte buffer aligned on a 4-byte boundary.
Definition: UefiBaseType.h:110
EFI_GUID efi_simple_file_system_protocol_guid
Simple file system protocol GUID.
Definition: efi_guid.c:232
char * strcpy(char *dest, const char *src)
Copy string.
Definition: string.c:326
#define EFI_OPEN_PROTOCOL_GET_PROTOCOL
Definition: UefiSpec.h:1271
const char * efi_handle_name(EFI_HANDLE handle)
Get name of an EFI handle.
Definition: efi_debug.c:722
uint32_t fs
Definition: librm.h:252
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:624
EFI Boot Services Table.
Definition: UefiSpec.h:1836
IP address structure.
Definition: in.h:39
EFI_HANDLE efi_image_handle
Image handle passed to entry point.
Definition: efi_init.c:33
char * inet_ntoa(struct in_addr in)
Convert IPv4 address to dotted-quad notation.
Definition: ipv4.c:658
IP4_t ip
Destination IP address.
Definition: pxe_api.h:58
UINT64 UINTN
Unsigned value of native width.
Definition: ProcessorBind.h:71
#define VOID
Undeclared type.
Definition: Base.h:319
unsigned long long UINT64
Definition: ProcessorBind.h:54
EFI_FREE_POOL FreePool
Definition: UefiSpec.h:1855
Provides a GUID and a data structure that can be used with EFI_FILE_PROTOCOL.SetInfo() and EFI_FILE_P...
static void efi_autoexec_startup(void)
Register autoexec script.
Definition: efi_autoexec.c:372
EFI API.
Packet structure.
Definition: PxeBaseCode.h:254
EFI_PXE_BASE_CODE_PACKET DhcpAck
Definition: PxeBaseCode.h:295
EFI_GUID efi_file_info_id
File information GUID.
Definition: efi_guid.c:303
#define EFI_FILE_MODE_READ
EFI autoexec script.
RETURN_STATUS EFI_STATUS
Function return status for EFI API.
Definition: UefiBaseType.h:35
static int efi_autoexec_filesystem(EFI_HANDLE device)
Load autoexec script from filesystem.
Definition: efi_autoexec.c:62
static void * efi_autoexec
Autoexec script (if any)
Definition: efi_autoexec.c:51
userptr_t virt_to_user(volatile const void *addr)
Convert virtual address to user pointer.
union @17 u
uint8_t size
Entry size (in 32-bit words)
Definition: ena.h:16
#define FALSE
Definition: tlan.h:45
EFI_SYSTEM_TABLE * efi_systab
EFI_OPEN_PROTOCOL OpenProtocol
Definition: UefiSpec.h:1905
struct arbelprm_port_state_change_st data
Message.
Definition: arbel.h:12
The data portions of a loaded Boot Serves Driver, and the default data allocation type used by a Boot...
The EFI_PXE_BASE_CODE_PROTOCOL is used to control PXE-compatible devices.
Definition: PxeBaseCode.h:909
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
EFI_PXE_BASE_CODE_PACKET ProxyOffer
Definition: PxeBaseCode.h:296
String functions.
static wchar_t efi_autoexec_wname[]
Autoexec script filename.
Definition: efi_autoexec.c:45
The EFI_FILE_PROTOCOL provides file IO access to supported file systems.
Definition: efi.h:50
EFI_HANDLE DeviceHandle
The device handle that the EFI Image was loaded from.
Definition: LoadedImage.h:61
EFI_ALLOCATE_POOL AllocatePool
Definition: UefiSpec.h:1854
void * memset(void *dest, int character, size_t len) __nonnull
int efi_autoexec_load(EFI_HANDLE device)
Load autoexec script.
Definition: efi_autoexec.c:350