summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/efi/efi_autoexec.c489
-rw-r--r--src/interface/efi/efi_driver.c117
-rw-r--r--src/interface/efi/efi_local.c294
-rw-r--r--src/interface/efi/efi_mp.c112
-rw-r--r--src/interface/efi/efi_path.c305
-rw-r--r--src/interface/efi/efi_service.c138
-rw-r--r--src/interface/efi/efiprefix.c16
7 files changed, 920 insertions, 551 deletions
diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c
index fb12cef0..d9ad3b99 100644
--- a/src/interface/efi/efi_autoexec.c
+++ b/src/interface/efi/efi_autoexec.c
@@ -24,16 +24,16 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
-#include <stdlib.h>
#include <errno.h>
+#include <ipxe/timer.h>
#include <ipxe/image.h>
-#include <ipxe/init.h>
-#include <ipxe/in.h>
+#include <ipxe/netdevice.h>
#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_utils.h>
#include <ipxe/efi/efi_autoexec.h>
-#include <ipxe/efi/Protocol/PxeBaseCode.h>
-#include <ipxe/efi/Protocol/SimpleFileSystem.h>
-#include <ipxe/efi/Guid/FileInfo.h>
+#include <ipxe/efi/mnpnet.h>
+#include <usr/imgmgmt.h>
+#include <usr/sync.h>
/** @file
*
@@ -41,413 +41,160 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
-/** Autoexec script filename */
-static wchar_t efi_autoexec_wname[] = L"autoexec.ipxe";
+/** Timeout for autoexec script downloads */
+#define EFI_AUTOEXEC_TIMEOUT ( 2 * TICKS_PER_SEC )
/** Autoexec script image name */
-static char efi_autoexec_name[] = "autoexec.ipxe";
-
-/** Autoexec script (if any) */
-static void *efi_autoexec;
-
-/** Autoexec script length */
-static size_t efi_autoexec_len;
+#define EFI_AUTOEXEC_NAME "autoexec.ipxe"
+
+/** An EFI autoexec script loader */
+struct efi_autoexec_loader {
+ /** Required protocol GUID */
+ EFI_GUID *protocol;
+ /**
+ * Load autoexec script
+ *
+ * @v handle Handle on which protocol was found
+ * @v image Image to fill in
+ * @ret rc Return status code
+ */
+ int ( * load ) ( EFI_HANDLE handle, struct image **image );
+};
/**
- * Load autoexec script from path within filesystem
+ * Load autoexec script from filesystem
*
- * @v device Device handle
- * @v path Relative path to image, or NULL to load from root
+ * @v handle Simple filesystem protocol handle
+ * @v image Image to fill in
* @ret rc Return status code
*/
-static int efi_autoexec_filesystem ( EFI_HANDLE device,
- EFI_DEVICE_PATH_PROTOCOL *path ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- union {
- void *interface;
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
- } u;
- struct {
- EFI_FILE_INFO info;
- CHAR16 name[ sizeof ( efi_autoexec_wname ) /
- sizeof ( efi_autoexec_wname[0] ) ];
- } info;
- FILEPATH_DEVICE_PATH *filepath;
- EFI_FILE_PROTOCOL *root;
- EFI_FILE_PROTOCOL *file;
- UINTN size;
- VOID *data;
- unsigned int dirlen;
- size_t len;
- CHAR16 *wname;
- EFI_STATUS efirc;
+static int efi_autoexec_filesystem ( EFI_HANDLE handle, struct image **image ) {
+ EFI_HANDLE device = efi_loaded_image->DeviceHandle;
int rc;
- /* Identify directory */
- if ( path ) {
-
- /* Check relative device path is a file path */
- if ( ! ( ( path->Type == MEDIA_DEVICE_PATH ) &&
- ( path->SubType == MEDIA_FILEPATH_DP ) ) ) {
- DBGC ( device, "EFI %s image path ",
- efi_handle_name ( device ) );
- DBGC ( device, " \"%s\" is not a file path\n",
- efi_devpath_text ( path ) );
- rc = -ENOTTY;
- goto err_not_filepath;
- }
- filepath = container_of ( path, FILEPATH_DEVICE_PATH, Header );
-
- /* Find length of containing directory */
- dirlen = ( ( ( ( path->Length[1] << 8 ) | path->Length[0] )
- - offsetof ( typeof ( *filepath ), PathName ) )
- / sizeof ( filepath->PathName[0] ) );
- for ( ; dirlen ; dirlen-- ) {
- if ( filepath->PathName[ dirlen - 1 ] == L'\\' )
- break;
- }
-
- } else {
-
- /* Use root directory */
- filepath = NULL;
- dirlen = 0;
- }
-
- /* Allocate filename */
- len = ( ( dirlen * sizeof ( wname[0] ) ) + sizeof ( efi_autoexec_wname ) );
- wname = malloc ( len );
- if ( ! wname ) {
- rc = -ENOMEM;
- goto err_wname;
- }
- memcpy ( wname, filepath->PathName, ( dirlen * sizeof ( wname[0] ) ) );
- memcpy ( &wname[dirlen], efi_autoexec_wname, sizeof ( efi_autoexec_wname ) );
-
- /* Open simple file system protocol */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_simple_file_system_protocol_guid,
- &u.interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s has no filesystem instance: %s\n",
- efi_handle_name ( device ), strerror ( rc ) );
- goto err_filesystem;
- }
-
- /* Open root directory */
- if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not open volume: %s\n",
- efi_handle_name ( device ), strerror ( rc ) );
- goto err_volume;
- }
-
- /* Open autoexec script */
- if ( ( efirc = root->Open ( root, &file, wname,
- EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s has no %ls: %s\n",
- efi_handle_name ( device ), wname, strerror ( rc ) );
- goto err_open;
- }
-
- /* Get file information */
- size = sizeof ( info );
- if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
- &info ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not get %ls info: %s\n",
- efi_handle_name ( device ), wname, strerror ( rc ) );
- goto err_getinfo;
- }
- size = info.info.FileSize;
-
- /* Ignore zero-length files */
- if ( ! size ) {
- rc = -EINVAL;
- DBGC ( device, "EFI %s has zero-length %ls\n",
- efi_handle_name ( device ), wname );
- goto err_empty;
- }
-
- /* Allocate temporary copy */
- if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
- &data ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not allocate %ls: %s\n",
- efi_handle_name ( device ), wname, strerror ( rc ) );
- goto err_alloc;
- }
-
- /* Read file */
- if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not read %ls: %s\n",
- efi_handle_name ( device ), wname, strerror ( rc ) );
- goto err_read;
+ /* Check that we were loaded from a filesystem */
+ if ( handle != device ) {
+ DBGC ( device, "EFI %s is not the file system handle\n",
+ efi_handle_name ( device ) );
+ return -ENOTTY;
}
- /* Record autoexec script */
- efi_autoexec = data;
- efi_autoexec_len = size;
- data = NULL;
- DBGC ( device, "EFI %s found %ls\n", efi_handle_name ( device ), wname );
+ /* Try loading from loaded image directory, if supported */
+ if ( ( rc = imgacquire ( "file:" EFI_AUTOEXEC_NAME,
+ EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 )
+ return 0;
- /* Success */
- rc = 0;
+ /* Try loading from root directory, if supported */
+ if ( ( rc = imgacquire ( "file:/" EFI_AUTOEXEC_NAME,
+ EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 )
+ return 0;
- err_read:
- if ( data )
- bs->FreePool ( data );
- err_alloc:
- err_empty:
- err_getinfo:
- file->Close ( file );
- err_open:
- root->Close ( root );
- err_volume:
- bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid,
- efi_image_handle, device );
- err_filesystem:
- free ( wname );
- err_wname:
- err_not_filepath:
return rc;
}
/**
- * Load autoexec script from TFTP server
+ * Load autoexec script via temporary network device
*
- * @v device Device handle
+ * @v handle Managed network protocol service binding handle
+ * @v image Image to fill in
* @ret rc Return status code
*/
-static int efi_autoexec_tftp ( EFI_HANDLE device ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- union {
- void *interface;
- EFI_PXE_BASE_CODE_PROTOCOL *pxe;
- } u;
- EFI_PXE_BASE_CODE_MODE *mode;
- EFI_PXE_BASE_CODE_PACKET *packet;
- union {
- struct in_addr in;
- EFI_IP_ADDRESS ip;
- } server;
- size_t filename_max;
- char *filename;
- char *sep;
- UINT64 size;
- VOID *data;
- EFI_STATUS efirc;
+static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) {
+ EFI_HANDLE device = efi_loaded_image->DeviceHandle;
+ struct net_device *netdev;
int rc;
- /* Open PXE base code protocol */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_pxe_base_code_protocol_guid,
- &u.interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s has no PXE base code instance: %s\n",
+ /* Create temporary network device */
+ if ( ( rc = mnptemp_create ( handle, &netdev ) ) != 0 ) {
+ DBGC ( device, "EFI %s could not create net device: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
- goto err_pxe;
- }
-
- /* Do not attempt to parse DHCPv6 packets */
- mode = u.pxe->Mode;
- if ( mode->UsingIpv6 ) {
- rc = -ENOTSUP;
- DBGC ( device, "EFI %s has IPv6 PXE base code\n",
- efi_handle_name ( device ) );
- goto err_ipv6;
+ goto err_create;
}
- /* Identify relevant reply packet */
- if ( mode->PxeReplyReceived &&
- mode->PxeReply.Dhcpv4.BootpBootFile[0] ) {
- /* Use boot filename if present in PXE reply */
- DBGC ( device, "EFI %s using PXE reply filename\n",
- efi_handle_name ( device ) );
- packet = &mode->PxeReply;
- } else if ( mode->DhcpAckReceived &&
- mode->DhcpAck.Dhcpv4.BootpBootFile[0] ) {
- /* Otherwise, use boot filename if present in DHCPACK */
- DBGC ( device, "EFI %s using DHCPACK filename\n",
- efi_handle_name ( device ) );
- packet = &mode->DhcpAck;
- } else if ( mode->ProxyOfferReceived &&
- mode->ProxyOffer.Dhcpv4.BootpBootFile[0] ) {
- /* Otherwise, use boot filename if present in ProxyDHCPOFFER */
- DBGC ( device, "EFI %s using ProxyDHCPOFFER filename\n",
- efi_handle_name ( device ) );
- packet = &mode->ProxyOffer;
- } else {
- /* No boot filename available */
- rc = -ENOENT;
- DBGC ( device, "EFI %s has no PXE boot filename\n",
- efi_handle_name ( device ) );
- goto err_packet;
- }
-
- /* Allocate filename */
- filename_max = ( sizeof ( packet->Dhcpv4.BootpBootFile )
- + ( sizeof ( efi_autoexec_name ) - 1 /* NUL */ )
- + 1 /* NUL */ );
- filename = zalloc ( filename_max );
- if ( ! filename ) {
- rc = -ENOMEM;
- goto err_filename;
- }
-
- /* Extract next-server address and boot filename */
- memset ( &server, 0, sizeof ( server ) );
- memcpy ( &server.in, packet->Dhcpv4.BootpSiAddr,
- sizeof ( server.in ) );
- memcpy ( filename, packet->Dhcpv4.BootpBootFile,
- sizeof ( packet->Dhcpv4.BootpBootFile ) );
-
- /* Update filename to autoexec script name */
- sep = strrchr ( filename, '/' );
- if ( ! sep )
- sep = strrchr ( filename, '\\' );
- if ( ! sep )
- sep = ( filename - 1 );
- strcpy ( ( sep + 1 ), efi_autoexec_name );
-
- /* Get file size */
- if ( ( efirc = u.pxe->Mtftp ( u.pxe,
- EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
- NULL, FALSE, &size, NULL, &server.ip,
- ( ( UINT8 * ) filename ), NULL,
- FALSE ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not get size of %s:%s: %s\n",
- efi_handle_name ( device ), inet_ntoa ( server.in ),
- filename, strerror ( rc ) );
- goto err_size;
- }
-
- /* Ignore zero-length files */
- if ( ! size ) {
- rc = -EINVAL;
- DBGC ( device, "EFI %s has zero-length %s:%s\n",
- efi_handle_name ( device ), inet_ntoa ( server.in ),
- filename );
- goto err_empty;
- }
-
- /* Allocate temporary copy */
- if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
- &data ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not allocate %s:%s: %s\n",
- efi_handle_name ( device ), inet_ntoa ( server.in ),
- filename, strerror ( rc ) );
- goto err_alloc;
+ /* Open network device */
+ if ( ( rc = netdev_open ( netdev ) ) != 0 ) {
+ DBGC ( device, "EFI %s could not open net device: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_open;
}
- /* Download file */
- if ( ( efirc = u.pxe->Mtftp ( u.pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
- data, FALSE, &size, NULL, &server.ip,
- ( ( UINT8 * ) filename ), NULL,
- FALSE ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( device, "EFI %s could not download %s:%s: %s\n",
- efi_handle_name ( device ), inet_ntoa ( server.in ),
- filename, strerror ( rc ) );
- goto err_download;
+ /* Attempt download */
+ rc = imgacquire ( EFI_AUTOEXEC_NAME, EFI_AUTOEXEC_TIMEOUT, image );
+ if ( rc != 0 ) {
+ DBGC ( device, "EFI %s could not download %s: %s\n",
+ efi_handle_name ( device ), EFI_AUTOEXEC_NAME,
+ strerror ( rc ) );
}
- /* Record autoexec script */
- efi_autoexec = data;
- efi_autoexec_len = size;
- data = NULL;
- DBGC ( device, "EFI %s found %s:%s\n", efi_handle_name ( device ),
- inet_ntoa ( server.in ), filename );
+ /* Ensure network exchanges have completed */
+ sync ( EFI_AUTOEXEC_TIMEOUT );
- /* Success */
- rc = 0;
-
- err_download:
- if ( data )
- bs->FreePool ( data );
- err_alloc:
- err_empty:
- err_size:
- free ( filename );
- err_filename:
- err_packet:
- err_ipv6:
- bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
- efi_image_handle, device );
- err_pxe:
+ err_open:
+ mnptemp_destroy ( netdev );
+ err_create:
return rc;
}
+/** Autoexec script loaders */
+static struct efi_autoexec_loader efi_autoexec_loaders[] = {
+ {
+ .protocol = &efi_simple_file_system_protocol_guid,
+ .load = efi_autoexec_filesystem,
+ },
+ {
+ .protocol = &efi_managed_network_service_binding_protocol_guid,
+ .load = efi_autoexec_network,
+ },
+};
+
/**
* Load autoexec script
*
- * @v device Device handle
- * @v path Image path within device handle
* @ret rc Return status code
*/
-int efi_autoexec_load ( EFI_HANDLE device,
- EFI_DEVICE_PATH_PROTOCOL *path ) {
- int rc;
-
- /* Sanity check */
- assert ( efi_autoexec == NULL );
- assert ( efi_autoexec_len == 0 );
-
- /* Try loading from file system loaded image directory, if supported */
- if ( ( rc = efi_autoexec_filesystem ( device, path ) ) == 0 )
- return 0;
-
- /* Try loading from file system root directory, if supported */
- if ( ( rc = efi_autoexec_filesystem ( device, NULL ) ) == 0 )
- return 0;
-
- /* Try loading via TFTP, if supported */
- if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 )
- return 0;
-
- return -ENOENT;
-}
-
-/**
- * Register autoexec script
- *
- */
-static void efi_autoexec_startup ( void ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+int efi_autoexec_load ( void ) {
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
+ EFI_HANDLE handle;
+ struct efi_autoexec_loader *loader;
struct image *image;
+ unsigned int i;
+ int rc;
- /* Do nothing if we have no autoexec script */
- if ( ! efi_autoexec )
- return;
+ /* Use first applicable loader */
+ for ( i = 0 ; i < ( sizeof ( efi_autoexec_loaders ) /
+ sizeof ( efi_autoexec_loaders[0] ) ) ; i ++ ) {
+
+ /* Locate required protocol for this loader */
+ loader = &efi_autoexec_loaders[i];
+ if ( ( rc = efi_locate_device ( device, loader->protocol,
+ &handle, 0 ) ) != 0 ) {
+ DBGC ( device, "EFI %s found no %s: %s\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( loader->protocol ),
+ strerror ( rc ) );
+ continue;
+ }
+ DBGC ( device, "EFI %s found %s on ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( loader->protocol ) );
+ DBGC ( device, "%s\n", efi_handle_name ( handle ) );
+
+ /* Try loading */
+ if ( ( rc = loader->load ( handle, &image ) ) != 0 )
+ return rc;
+
+ /* Discard zero-length images */
+ if ( ! image->len ) {
+ DBGC ( device, "EFI %s discarding zero-length %s\n",
+ efi_handle_name ( device ), image->name );
+ unregister_image ( image );
+ return -ENOENT;
+ }
- /* Create autoexec image */
- image = image_memory ( efi_autoexec_name,
- virt_to_user ( efi_autoexec ),
- efi_autoexec_len );
- if ( ! image ) {
- DBGC ( device, "EFI %s could not create %s\n",
- efi_handle_name ( device ), efi_autoexec_name );
- return;
+ DBGC ( device, "EFI %s loaded %s (%zd bytes)\n",
+ efi_handle_name ( device ), image->name, image->len );
+ return 0;
}
- DBGC ( device, "EFI %s registered %s\n",
- efi_handle_name ( device ), efi_autoexec_name );
- /* Free temporary copy */
- bs->FreePool ( efi_autoexec );
- efi_autoexec = NULL;
+ return -ENOENT;
}
-
-/** Autoexec script startup function */
-struct startup_fn efi_autoexec_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
- .name = "efi_autoexec",
- .startup = efi_autoexec_startup,
-};
diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c
index 8e537d53..fd9be5f5 100644
--- a/src/interface/efi/efi_driver.c
+++ b/src/interface/efi/efi_driver.c
@@ -62,18 +62,85 @@ static LIST_HEAD ( efi_devices );
static int efi_driver_disconnecting;
/**
- * Find EFI device
+ * Allocate new EFI device
*
* @v device EFI device handle
+ * @ret efidev EFI device, or NULL on error
+ */
+struct efi_device * efidev_alloc ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_device *efidev = NULL;
+ union {
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ void *interface;
+ } path;
+ EFI_DEVICE_PATH_PROTOCOL *path_end;
+ size_t path_len;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Open device path */
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_device_path_protocol_guid,
+ &path.interface, efi_image_handle,
+ device,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIDRV %s could not open device path: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_open_path;
+ }
+ path_len = ( efi_path_len ( path.path ) + sizeof ( *path_end ) );
+
+ /* Allocate and initialise structure */
+ efidev = zalloc ( sizeof ( *efidev ) + path_len );
+ if ( ! efidev )
+ goto err_alloc;
+ efidev->device = device;
+ efidev->dev.desc.bus_type = BUS_TYPE_EFI;
+ efidev->path = ( ( ( void * ) efidev ) + sizeof ( *efidev ) );
+ memcpy ( efidev->path, path.path, path_len );
+ INIT_LIST_HEAD ( &efidev->dev.children );
+ list_add ( &efidev->dev.siblings, &efi_devices );
+
+ err_alloc:
+ bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
+ efi_image_handle, device );
+ err_open_path:
+ return efidev;
+}
+
+/**
+ * Free EFI device
+ *
+ * @v efidev EFI device
+ */
+void efidev_free ( struct efi_device *efidev ) {
+
+ assert ( list_empty ( &efidev->dev.children ) );
+ list_del ( &efidev->dev.siblings );
+ free ( efidev );
+}
+
+/**
+ * Find EFI device
+ *
+ * @v device EFI device handle (or child handle)
* @ret efidev EFI device, or NULL if not found
*/
static struct efi_device * efidev_find ( EFI_HANDLE device ) {
struct efi_device *efidev;
+ /* Avoid false positive matches against NULL children */
+ if ( ! device )
+ return NULL;
+
/* Look for an existing EFI device */
list_for_each_entry ( efidev, &efi_devices, dev.siblings ) {
- if ( efidev->device == device )
+ if ( ( device == efidev->device ) ||
+ ( device == efidev->child ) ) {
return efidev;
+ }
}
return NULL;
@@ -153,16 +220,9 @@ efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
static EFI_STATUS EFIAPI
efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_driver *efidrv;
struct efi_device *efidev;
struct efi_saved_tpl tpl;
- union {
- EFI_DEVICE_PATH_PROTOCOL *path;
- void *interface;
- } path;
- EFI_DEVICE_PATH_PROTOCOL *path_end;
- size_t path_len;
EFI_STATUS efirc;
int rc;
@@ -191,36 +251,12 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
goto err_disconnecting;
}
- /* Open device path */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_device_path_protocol_guid,
- &path.interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
- DBGC ( device, "EFIDRV %s could not open device path: %s\n",
- efi_handle_name ( device ), strerror ( rc ) );
- goto err_open_path;
- }
- path_len = ( efi_path_len ( path.path ) + sizeof ( *path_end ) );
-
- /* Allocate and initialise structure */
- efidev = zalloc ( sizeof ( *efidev ) + path_len );
+ /* Add new device */
+ efidev = efidev_alloc ( device );
if ( ! efidev ) {
efirc = EFI_OUT_OF_RESOURCES;
goto err_alloc;
}
- efidev->device = device;
- efidev->dev.desc.bus_type = BUS_TYPE_EFI;
- efidev->path = ( ( ( void * ) efidev ) + sizeof ( *efidev ) );
- memcpy ( efidev->path, path.path, path_len );
- INIT_LIST_HEAD ( &efidev->dev.children );
- list_add ( &efidev->dev.siblings, &efi_devices );
-
- /* Close device path */
- bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
- efi_image_handle, device );
- path.path = NULL;
/* Try to start this device */
for_each_table_entry ( efidrv, EFI_DRIVERS ) {
@@ -245,14 +281,8 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
}
efirc = EFI_UNSUPPORTED;
- list_del ( &efidev->dev.siblings );
- free ( efidev );
+ efidev_free ( efidev );
err_alloc:
- if ( path.path ) {
- bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
- efi_image_handle, device );
- }
- err_open_path:
err_disconnecting:
efi_restore_tpl ( &tpl );
err_already_started:
@@ -300,8 +330,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
efidrv = efidev->driver;
assert ( efidrv != NULL );
efidrv->stop ( efidev );
- list_del ( &efidev->dev.siblings );
- free ( efidev );
+ efidev_free ( efidev );
efi_restore_tpl ( &tpl );
return 0;
diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c
index d3ac3d54..b2881424 100644
--- a/src/interface/efi/efi_local.c
+++ b/src/interface/efi/efi_local.c
@@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/uri.h>
#include <ipxe/iobuf.h>
#include <ipxe/process.h>
+#include <ipxe/errortab.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_strings.h>
#include <ipxe/efi/efi_path.h>
@@ -48,6 +49,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+/* Disambiguate the various error causes */
+#define EINFO_EEFI_OPEN \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, "Could not open" )
+#define EINFO_EEFI_OPEN_NOT_FOUND \
+ __einfo_platformify ( EINFO_EEFI_OPEN, EFI_NOT_FOUND, \
+ "Not found" )
+#define EEFI_OPEN_NOT_FOUND \
+ __einfo_error ( EINFO_EEFI_OPEN_NOT_FOUND )
+#define EEFI_OPEN( efirc ) EPLATFORM ( EINFO_EEFI_OPEN, efirc, \
+ EEFI_OPEN_NOT_FOUND )
+
/** Download blocksize */
#define EFI_LOCAL_BLKSIZE 4096
@@ -60,6 +72,13 @@ struct efi_local {
/** Download process */
struct process process;
+ /** Download URI */
+ struct uri *uri;
+ /** Volume name, or NULL to use loaded image's device */
+ const char *volume;
+ /** File path */
+ const char *path;
+
/** EFI root directory */
EFI_FILE_PROTOCOL *root;
/** EFI file */
@@ -68,6 +87,24 @@ struct efi_local {
size_t len;
};
+/** Human-readable error messages */
+struct errortab efi_local_errors[] __errortab = {
+ __einfo_errortab ( EINFO_EEFI_OPEN_NOT_FOUND ),
+};
+
+/**
+ * Free local file
+ *
+ * @v refcnt Reference count
+ */
+static void efi_local_free ( struct refcnt *refcnt ) {
+ struct efi_local *local =
+ container_of ( refcnt, struct efi_local, refcnt );
+
+ uri_put ( local->uri );
+ free ( local );
+}
+
/**
* Close local file
*
@@ -96,91 +133,6 @@ static void efi_local_close ( struct efi_local *local, int rc ) {
}
/**
- * Local file process
- *
- * @v local Local file
- */
-static void efi_local_step ( struct efi_local *local ) {
- EFI_FILE_PROTOCOL *file = local->file;
- struct io_buffer *iobuf = NULL;
- size_t remaining;
- size_t frag_len;
- UINTN size;
- EFI_STATUS efirc;
- int rc;
-
- /* Wait until data transfer interface is ready */
- if ( ! xfer_window ( &local->xfer ) )
- return;
-
- /* Presize receive buffer */
- remaining = local->len;
- xfer_seek ( &local->xfer, remaining );
- xfer_seek ( &local->xfer, 0 );
-
- /* Get file contents */
- while ( remaining ) {
-
- /* Calculate length for this fragment */
- frag_len = remaining;
- if ( frag_len > EFI_LOCAL_BLKSIZE )
- frag_len = EFI_LOCAL_BLKSIZE;
-
- /* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &local->xfer, frag_len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err;
- }
-
- /* Read block */
- size = frag_len;
- if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){
- rc = -EEFI ( efirc );
- DBGC ( local, "LOCAL %p could not read from file: %s\n",
- local, strerror ( rc ) );
- goto err;
- }
- assert ( size <= frag_len );
- iob_put ( iobuf, size );
-
- /* Deliver data */
- if ( ( rc = xfer_deliver_iob ( &local->xfer,
- iob_disown ( iobuf ) ) ) != 0 ) {
- DBGC ( local, "LOCAL %p could not deliver data: %s\n",
- local, strerror ( rc ) );
- goto err;
- }
-
- /* Move to next block */
- remaining -= frag_len;
- }
-
- /* Close download */
- efi_local_close ( local, 0 );
-
- return;
-
- err:
- free_iob ( iobuf );
- efi_local_close ( local, rc );
-}
-
-/** Data transfer interface operations */
-static struct interface_operation efi_local_operations[] = {
- INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ),
- INTF_OP ( intf_close, struct efi_local *, efi_local_close ),
-};
-
-/** Data transfer interface descriptor */
-static struct interface_descriptor efi_local_xfer_desc =
- INTF_DESC ( struct efi_local, xfer, efi_local_operations );
-
-/** Process descriptor */
-static struct process_descriptor efi_local_process_desc =
- PROC_DESC_ONCE ( struct efi_local, process, efi_local_step );
-
-/**
* Check for matching volume name
*
* @v local Local file
@@ -354,15 +306,14 @@ static int efi_local_open_volume_index ( struct efi_local *local,
* Open root filesystem of specified volume
*
* @v local Local file
- * @v volume Volume name, or NULL to use loaded image's device
* @ret rc Return status code
*/
-static int efi_local_open_volume ( struct efi_local *local,
- const char *volume ) {
+static int efi_local_open_volume ( struct efi_local *local ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
int ( * check ) ( struct efi_local *local, EFI_HANDLE device,
EFI_FILE_PROTOCOL *root, const char *volume );
+ const char *volume = local->volume;
EFI_DEVICE_PATH_PROTOCOL *path;
EFI_FILE_PROTOCOL *root;
EFI_HANDLE *handles;
@@ -461,7 +412,7 @@ static int efi_local_open_resolved ( struct efi_local *local,
/* Open file */
if ( ( efirc = local->root->Open ( local->root, &file, name,
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
- rc = -EEFI ( efirc );
+ rc = -EEFI_OPEN ( efirc );
DBGC ( local, "LOCAL %p could not open \"%s\": %s\n",
local, resolved, strerror ( rc ) );
return rc;
@@ -475,11 +426,9 @@ static int efi_local_open_resolved ( struct efi_local *local,
* Open specified path
*
* @v local Local file
- * @v filename Path to file relative to our own image
* @ret rc Return status code
*/
-static int efi_local_open_path ( struct efi_local *local,
- const char *filename ) {
+static int efi_local_open_path ( struct efi_local *local ) {
EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image->FilePath;
EFI_DEVICE_PATH_PROTOCOL *next;
FILEPATH_DEVICE_PATH *fp;
@@ -510,7 +459,7 @@ static int efi_local_open_path ( struct efi_local *local,
}
/* Resolve path */
- resolved = resolve_path ( base, filename );
+ resolved = resolve_path ( base, local->path );
if ( ! resolved ) {
rc = -ENOMEM;
goto err_resolve;
@@ -580,6 +529,106 @@ static int efi_local_len ( struct efi_local *local ) {
}
/**
+ * Local file process
+ *
+ * @v local Local file
+ */
+static void efi_local_step ( struct efi_local *local ) {
+ struct io_buffer *iobuf = NULL;
+ size_t remaining;
+ size_t frag_len;
+ UINTN size;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Wait until data transfer interface is ready */
+ if ( ! xfer_window ( &local->xfer ) )
+ return;
+
+ /* Open specified volume root directory, if not yet open */
+ if ( ( ! local->root ) &&
+ ( ( rc = efi_local_open_volume ( local ) ) != 0 ) )
+ goto err;
+
+ /* Open specified file, if not yet open */
+ if ( ( ! local->file ) &&
+ ( ( rc = efi_local_open_path ( local ) ) != 0 ) )
+ goto err;
+
+ /* Get file length, if not yet known */
+ if ( ( ! local->len ) &&
+ ( ( rc = efi_local_len ( local ) ) != 0 ) )
+ goto err;
+
+ /* Presize receive buffer */
+ remaining = local->len;
+ xfer_seek ( &local->xfer, remaining );
+ xfer_seek ( &local->xfer, 0 );
+
+ /* Get file contents */
+ while ( remaining ) {
+
+ /* Calculate length for this fragment */
+ frag_len = remaining;
+ if ( frag_len > EFI_LOCAL_BLKSIZE )
+ frag_len = EFI_LOCAL_BLKSIZE;
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &local->xfer, frag_len );
+ if ( ! iobuf ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Read block */
+ size = frag_len;
+ if ( ( efirc = local->file->Read ( local->file, &size,
+ iobuf->data ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( local, "LOCAL %p could not read from file: %s\n",
+ local, strerror ( rc ) );
+ goto err;
+ }
+ assert ( size <= frag_len );
+ iob_put ( iobuf, size );
+
+ /* Deliver data */
+ if ( ( rc = xfer_deliver_iob ( &local->xfer,
+ iob_disown ( iobuf ) ) ) != 0 ) {
+ DBGC ( local, "LOCAL %p could not deliver data: %s\n",
+ local, strerror ( rc ) );
+ goto err;
+ }
+
+ /* Move to next block */
+ remaining -= frag_len;
+ }
+
+ /* Close download */
+ efi_local_close ( local, 0 );
+
+ return;
+
+ err:
+ free_iob ( iobuf );
+ efi_local_close ( local, rc );
+}
+
+/** Data transfer interface operations */
+static struct interface_operation efi_local_operations[] = {
+ INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ),
+ INTF_OP ( intf_close, struct efi_local *, efi_local_close ),
+};
+
+/** Data transfer interface descriptor */
+static struct interface_descriptor efi_local_xfer_desc =
+ INTF_DESC ( struct efi_local, xfer, efi_local_operations );
+
+/** Process descriptor */
+static struct process_descriptor efi_local_process_desc =
+ PROC_DESC_ONCE ( struct efi_local, process, efi_local_step );
+
+/**
* Open local file
*
* @v xfer Data transfer interface
@@ -588,33 +637,26 @@ static int efi_local_len ( struct efi_local *local ) {
*/
static int efi_local_open ( struct interface *xfer, struct uri *uri ) {
struct efi_local *local;
- const char *volume;
- const char *path;
- int rc, vol;
-
- /* Parse URI */
- volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL );
- path = ( uri->opaque ? uri->opaque : uri->path );
+ int vol;
+ int rc = 0;
/* Allocate and initialise structure */
local = zalloc ( sizeof ( *local ) );
- if ( ! local ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- ref_init ( &local->refcnt, NULL );
- intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt );
- process_init_stopped ( &local->process, &efi_local_process_desc,
- &local->refcnt );
-
- if ( volume && strcmp ( volume, "*" ) == 0 ) {
+ if ( ! local )
+ return -ENOMEM;
+ ref_init ( &local->refcnt, efi_local_free );
+ local->uri = uri_get ( uri );
+ local->volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL );
+ local->path = ( uri->opaque ? uri->opaque : uri->path );
+
+ if ( local->path && local->volume && strcmp ( local->volume, "*" ) == 0 ) {
/* Open on any volume */
vol = 0;
while ( ( rc = efi_local_open_volume_index ( local, vol++ ) ) != -ENOENT ) {
if ( rc != 0 )
continue;
/* Open specified path */
- if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) {
+ if ( ( rc = efi_local_open_path ( local ) ) != 0 ) {
local->root->Close ( local->root );
local->root = NULL;
continue;
@@ -622,21 +664,17 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) {
/* Success */
break;
}
- if ( rc != 0 )
- goto err_open_root;
- } else {
- /* Open specified volume */
- if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 )
- goto err_open_root;
-
- /* Open specified path */
- if ( ( rc = efi_local_open_path ( local, path ) ) != 0 )
- goto err_open_file;
+ if ( rc != 0 ) {
+ DBGC ( local, "LOCAL %p could not find %s on any partition\n",
+ local, local->path );
+ ref_put ( &local->refcnt );
+ return -ENOENT;
+ }
}
- /* Get length of file */
- if ( ( rc = efi_local_len ( local ) ) != 0 )
- goto err_len;
+ intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt );
+ process_init_stopped ( &local->process, &efi_local_process_desc,
+ &local->refcnt );
/* Start download process */
process_add ( &local->process );
@@ -645,14 +683,6 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) {
intf_plug_plug ( &local->xfer, xfer );
ref_put ( &local->refcnt );
return 0;
-
- err_len:
- err_open_file:
- err_open_root:
- efi_local_close ( local, 0 );
- ref_put ( &local->refcnt );
- err_alloc:
- return rc;
}
/** EFI local file URI opener */
diff --git a/src/interface/efi/efi_mp.c b/src/interface/efi/efi_mp.c
new file mode 100644
index 00000000..fdbbc9ae
--- /dev/null
+++ b/src/interface/efi/efi_mp.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * EFI multiprocessor API implementation
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/mp.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/MpService.h>
+
+/** EFI multiprocessor function call data */
+struct efi_mp_func_data {
+ /** Multiprocessor function */
+ mp_addr_t func;
+ /** Opaque data pointer */
+ mp_addr_t opaque;
+};
+
+/** Multiprocessor services protocol */
+static EFI_MP_SERVICES_PROTOCOL *efimp;
+EFI_REQUEST_PROTOCOL ( EFI_MP_SERVICES_PROTOCOL, &efimp );
+
+/**
+ * Call multiprocessor function on current CPU
+ *
+ * @v buffer Multiprocessor function call data
+ */
+static EFIAPI VOID efi_mp_call ( VOID *buffer ) {
+ struct efi_mp_func_data *data = buffer;
+
+ /* Call multiprocessor function */
+ mp_call ( data->func, data->opaque );
+}
+
+/**
+ * Execute a multiprocessor function on the boot processor
+ *
+ * @v func Multiprocessor function
+ * @v opaque Opaque data pointer
+ */
+static void efi_mp_exec_boot ( mp_func_t func, void *opaque ) {
+ struct efi_mp_func_data data;
+
+ /* Construct call data */
+ data.func = mp_address ( func );
+ data.opaque = mp_address ( opaque );
+
+ /* Call multiprocesor function */
+ efi_mp_call ( &data );
+}
+
+/**
+ * Start a multiprocessor function on all application processors
+ *
+ * @v func Multiprocessor function
+ * @v opaque Opaque data pointer
+ */
+static void efi_mp_start_all ( mp_func_t func, void *opaque ) {
+ struct efi_mp_func_data data;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing if MP services is not present */
+ if ( ! efimp ) {
+ DBGC ( func, "EFIMP has no multiprocessor services\n" );
+ return;
+ }
+
+ /* Construct call data */
+ data.func = mp_address ( func );
+ data.opaque = mp_address ( opaque );
+
+ /* Start up all application processors */
+ if ( ( efirc = efimp->StartupAllAPs ( efimp, efi_mp_call, FALSE, NULL,
+ 0, &data, NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( func, "EFIMP could not start APs: %s\n",
+ strerror ( rc ) );
+ return;
+ }
+}
+
+PROVIDE_MPAPI_INLINE ( efi, mp_address );
+PROVIDE_MPAPI ( efi, mp_exec_boot, efi_mp_exec_boot );
+PROVIDE_MPAPI ( efi, mp_start_all, efi_mp_start_all );
diff --git a/src/interface/efi/efi_path.c b/src/interface/efi/efi_path.c
index d1e22eea..ac3c0498 100644
--- a/src/interface/efi/efi_path.c
+++ b/src/interface/efi/efi_path.c
@@ -34,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/fcp.h>
#include <ipxe/ib_srp.h>
#include <ipxe/usb.h>
+#include <ipxe/settings.h>
+#include <ipxe/dhcp.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_path.h>
@@ -44,6 +46,40 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
+/** An EFI device path settings block */
+struct efi_path_settings {
+ /** Settings interface */
+ struct settings settings;
+ /** Device path */
+ EFI_DEVICE_PATH_PROTOCOL *path;
+};
+
+/** An EFI device path setting */
+struct efi_path_setting {
+ /** Setting */
+ const struct setting *setting;
+ /**
+ * Fetch setting
+ *
+ * @v pathset Path setting
+ * @v path Device path
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+ int ( * fetch ) ( struct efi_path_setting *pathset,
+ EFI_DEVICE_PATH_PROTOCOL *path,
+ void *data, size_t len );
+ /** Path type */
+ uint8_t type;
+ /** Path subtype */
+ uint8_t subtype;
+ /** Offset within device path */
+ uint8_t offset;
+ /** Length (if fixed) */
+ uint8_t len;
+};
+
/**
* Find next element in device path
*
@@ -112,6 +148,30 @@ size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) {
}
/**
+ * Get MAC address from device path
+ *
+ * @v path Device path
+ * @ret mac MAC address, or NULL if not found
+ */
+void * efi_path_mac ( EFI_DEVICE_PATH_PROTOCOL *path ) {
+ EFI_DEVICE_PATH_PROTOCOL *next;
+ MAC_ADDR_DEVICE_PATH *mac;
+
+ /* Search for MAC address path */
+ for ( ; ( next = efi_path_next ( path ) ) ; path = next ) {
+ if ( ( path->Type == MESSAGING_DEVICE_PATH ) &&
+ ( path->SubType == MSG_MAC_ADDR_DP ) ) {
+ mac = container_of ( path, MAC_ADDR_DEVICE_PATH,
+ Header );
+ return &mac->MacAddress;
+ }
+ }
+
+ /* No MAC address found */
+ return NULL;
+}
+
+/**
* Get VLAN tag from device path
*
* @v path Device path
@@ -176,6 +236,46 @@ int efi_path_guid ( EFI_DEVICE_PATH_PROTOCOL *path, union uuid *guid ) {
}
/**
+ * Parse URI from device path
+ *
+ * @v path Device path
+ * @ret uri URI, or NULL if not a URI
+ */
+struct uri * efi_path_uri ( EFI_DEVICE_PATH_PROTOCOL *path ) {
+ EFI_DEVICE_PATH_PROTOCOL *next;
+ URI_DEVICE_PATH *uripath;
+ char *uristring;
+ struct uri *uri;
+ size_t len;
+
+ /* Search for URI device path */
+ for ( ; ( next = efi_path_next ( path ) ) ; path = next ) {
+ if ( ( path->Type == MESSAGING_DEVICE_PATH ) &&
+ ( path->SubType == MSG_URI_DP ) ) {
+
+ /* Calculate path length */
+ uripath = container_of ( path, URI_DEVICE_PATH,
+ Header );
+ len = ( ( ( path->Length[1] << 8 ) | path->Length[0] )
+ - offsetof ( typeof ( *uripath ), Uri ) );
+
+ /* Parse URI */
+ uristring = zalloc ( len + 1 /* NUL */ );
+ if ( ! uristring )
+ return NULL;
+ memcpy ( uristring, uripath->Uri, len );
+ uri = parse_uri ( uristring );
+ free ( uristring );
+
+ return uri;
+ }
+ }
+
+ /* No URI path found */
+ return NULL;
+}
+
+/**
* Concatenate EFI device paths
*
* @v ... List of device paths (NULL terminated)
@@ -593,3 +693,208 @@ EFI_DEVICE_PATH_PROTOCOL * efi_describe ( struct interface *intf ) {
intf_put ( dest );
return path;
}
+
+/**
+ * Fetch an EFI device path fixed-size setting
+ *
+ * @v pathset Path setting
+ * @v path Device path
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int efi_path_fetch_fixed ( struct efi_path_setting *pathset,
+ EFI_DEVICE_PATH_PROTOCOL *path,
+ void *data, size_t len ) {
+
+ /* Copy data */
+ if ( len > pathset->len )
+ len = pathset->len;
+ memcpy ( data, ( ( ( void * ) path ) + pathset->offset ), len );
+
+ return pathset->len;
+}
+
+/**
+ * Fetch an EFI device path DNS setting
+ *
+ * @v pathset Path setting
+ * @v path Device path
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int efi_path_fetch_dns ( struct efi_path_setting *pathset,
+ EFI_DEVICE_PATH_PROTOCOL *path,
+ void *data, size_t len ) {
+ DNS_DEVICE_PATH *dns = container_of ( path, DNS_DEVICE_PATH, Header );
+ unsigned int count;
+ unsigned int i;
+ size_t frag_len;
+
+ /* Check applicability */
+ if ( ( !! dns->IsIPv6 ) !=
+ ( pathset->setting->type == &setting_type_ipv6 ) )
+ return -ENOENT;
+
+ /* Calculate number of addresses */
+ count = ( ( ( ( path->Length[1] << 8 ) | path->Length[0] ) -
+ pathset->offset ) / sizeof ( dns->DnsServerIp[0] ) );
+
+ /* Copy data */
+ for ( i = 0 ; i < count ; i++ ) {
+ frag_len = len;
+ if ( frag_len > pathset->len )
+ frag_len = pathset->len;
+ memcpy ( data, &dns->DnsServerIp[i], frag_len );
+ data += frag_len;
+ len -= frag_len;
+ }
+
+ return ( count * pathset->len );
+}
+
+/** EFI device path settings */
+static struct efi_path_setting efi_path_settings[] = {
+ { &ip_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv4_DP, offsetof ( IPv4_DEVICE_PATH, LocalIpAddress ),
+ sizeof ( struct in_addr ) },
+ { &netmask_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv4_DP, offsetof ( IPv4_DEVICE_PATH, SubnetMask ),
+ sizeof ( struct in_addr ) },
+ { &gateway_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv4_DP, offsetof ( IPv4_DEVICE_PATH, GatewayIpAddress ),
+ sizeof ( struct in_addr ) },
+ { &ip6_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv6_DP, offsetof ( IPv6_DEVICE_PATH, LocalIpAddress ),
+ sizeof ( struct in6_addr ) },
+ { &len6_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv6_DP, offsetof ( IPv6_DEVICE_PATH, PrefixLength ),
+ sizeof ( uint8_t ) },
+ { &gateway6_setting, efi_path_fetch_fixed, MESSAGING_DEVICE_PATH,
+ MSG_IPv6_DP, offsetof ( IPv6_DEVICE_PATH, GatewayIpAddress ),
+ sizeof ( struct in6_addr ) },
+ { &dns_setting, efi_path_fetch_dns, MESSAGING_DEVICE_PATH,
+ MSG_DNS_DP, offsetof ( DNS_DEVICE_PATH, DnsServerIp ),
+ sizeof ( struct in_addr ) },
+ { &dns6_setting, efi_path_fetch_dns, MESSAGING_DEVICE_PATH,
+ MSG_DNS_DP, offsetof ( DNS_DEVICE_PATH, DnsServerIp ),
+ sizeof ( struct in6_addr ) },
+};
+
+/**
+ * Fetch value of EFI device path setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v data Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int efi_path_fetch ( struct settings *settings, struct setting *setting,
+ void *data, size_t len ) {
+ struct efi_path_settings *pathsets =
+ container_of ( settings, struct efi_path_settings, settings );
+ EFI_DEVICE_PATH_PROTOCOL *path = pathsets->path;
+ EFI_DEVICE_PATH_PROTOCOL *next;
+ struct efi_path_setting *pathset;
+ unsigned int i;
+ int ret;
+
+ /* Find matching path setting, if any */
+ for ( i = 0 ; i < ( sizeof ( efi_path_settings ) /
+ sizeof ( efi_path_settings[0] ) ) ; i++ ) {
+
+ /* Check for a matching setting */
+ pathset = &efi_path_settings[i];
+ if ( setting_cmp ( setting, pathset->setting ) != 0 )
+ continue;
+
+ /* Find matching device path element, if any */
+ for ( ; ( next = efi_path_next ( path ) ) ; path = next ) {
+
+ /* Check for a matching path type */
+ if ( ( path->Type != pathset->type ) ||
+ ( path->SubType != pathset->subtype ) )
+ continue;
+
+ /* Fetch value */
+ if ( ( ret = pathset->fetch ( pathset, path,
+ data, len ) ) < 0 )
+ return ret;
+
+ /* Apply default type, if not already set */
+ if ( ! setting->type )
+ setting->type = pathset->setting->type;
+
+ return ret;
+ }
+ break;
+ }
+
+ return -ENOENT;
+}
+
+/** EFI device path settings operations */
+static struct settings_operations efi_path_settings_operations = {
+ .fetch = efi_path_fetch,
+};
+
+/**
+ * Create per-netdevice EFI path settings
+ *
+ * @v netdev Network device
+ * @v priv Private data
+ * @ret rc Return status code
+ */
+static int efi_path_net_probe ( struct net_device *netdev, void *priv ) {
+ struct efi_path_settings *pathsets = priv;
+ struct settings *settings = &pathsets->settings;
+ EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image_path;
+ unsigned int vlan;
+ void *mac;
+ int rc;
+
+ /* Check applicability */
+ pathsets->path = path;
+ mac = efi_path_mac ( path );
+ vlan = efi_path_vlan ( path );
+ if ( ( mac == NULL ) ||
+ ( memcmp ( mac, netdev->ll_addr,
+ netdev->ll_protocol->ll_addr_len ) != 0 ) ||
+ ( vlan != vlan_tag ( netdev ) ) ) {
+ DBGC ( settings, "EFI path %s does not apply to %s\n",
+ efi_devpath_text ( path ), netdev->name );
+ return 0;
+ }
+
+ /* Never override a real DHCP settings block */
+ if ( find_child_settings ( netdev_settings ( netdev ),
+ DHCP_SETTINGS_NAME ) ) {
+ DBGC ( settings, "EFI path %s not overriding %s DHCP "
+ "settings\n", efi_devpath_text ( path ), netdev->name );
+ return 0;
+ }
+
+ /* Initialise and register settings */
+ settings_init ( settings, &efi_path_settings_operations,
+ &netdev->refcnt, NULL );
+ if ( ( rc = register_settings ( settings, netdev_settings ( netdev ),
+ DHCP_SETTINGS_NAME ) ) != 0 ) {
+ DBGC ( settings, "EFI path %s could not register for %s: %s\n",
+ efi_devpath_text ( path ), netdev->name,
+ strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( settings, "EFI path %s registered for %s\n",
+ efi_devpath_text ( path ), netdev->name );
+
+ return 0;
+}
+
+/** EFI path settings per-netdevice driver */
+struct net_driver efi_path_net_driver __net_driver = {
+ .name = "EFI path",
+ .priv_len = sizeof ( struct efi_path_settings ),
+ .probe = efi_path_net_probe,
+};
diff --git a/src/interface/efi/efi_service.c b/src/interface/efi/efi_service.c
new file mode 100644
index 00000000..d4129c0d
--- /dev/null
+++ b/src/interface/efi/efi_service.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * EFI service binding
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_service.h>
+#include <ipxe/efi/Protocol/ServiceBinding.h>
+
+/**
+ * Add service to child handle
+ *
+ * @v service Service binding handle
+ * @v binding Service binding protocol GUID
+ * @v handle Handle on which to install child
+ * @ret rc Return status code
+ */
+int efi_service_add ( EFI_HANDLE service, EFI_GUID *binding,
+ EFI_HANDLE *handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_SERVICE_BINDING_PROTOCOL *sb;
+ void *interface;
+ } u;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Open service binding protocol */
+ if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface,
+ efi_image_handle, service,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( service, "EFISVC %s cannot open %s binding: %s\n",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ),
+ strerror ( rc ) );
+ goto err_open;
+ }
+
+ /* Create child handle */
+ if ( ( efirc = u.sb->CreateChild ( u.sb, handle ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( service, "EFISVC %s could not create %s child: %s\n",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ),
+ strerror ( rc ) );
+ goto err_create;
+ }
+
+ /* Success */
+ rc = 0;
+ DBGC ( service, "EFISVC %s created %s child ",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ) );
+ DBGC ( service, "%s\n", efi_handle_name ( *handle ) );
+
+ err_create:
+ bs->CloseProtocol ( service, binding, efi_image_handle, service );
+ err_open:
+ return rc;
+}
+
+/**
+ * Remove service from child handle
+ *
+ * @v service Service binding handle
+ * @v binding Service binding protocol GUID
+ * @v handle Child handle
+ * @ret rc Return status code
+ */
+int efi_service_del ( EFI_HANDLE service, EFI_GUID *binding,
+ EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_SERVICE_BINDING_PROTOCOL *sb;
+ void *interface;
+ } u;
+ EFI_STATUS efirc;
+ int rc;
+
+ DBGC ( service, "EFISVC %s removing %s child ",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ) );
+ DBGC ( service, "%s\n", efi_handle_name ( handle ) );
+
+ /* Open service binding protocol */
+ if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface,
+ efi_image_handle, service,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( service, "EFISVC %s cannot open %s binding: %s\n",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ),
+ strerror ( rc ) );
+ goto err_open;
+ }
+
+ /* Destroy child handle */
+ if ( ( efirc = u.sb->DestroyChild ( u.sb, handle ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( service, "EFISVC %s could not destroy %s child ",
+ efi_handle_name ( service ), efi_guid_ntoa ( binding ) );
+ DBGC ( service, "%s: %s\n",
+ efi_handle_name ( handle ), strerror ( rc ) );
+ goto err_destroy;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_destroy:
+ bs->CloseProtocol ( service, binding, efi_image_handle, service );
+ err_open:
+ return rc;
+}
diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c
index 26116068..10d8f0bf 100644
--- a/src/interface/efi/efiprefix.c
+++ b/src/interface/efi/efiprefix.c
@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdlib.h>
#include <errno.h>
#include <ipxe/device.h>
+#include <ipxe/uri.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
@@ -30,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi_autoexec.h>
#include <ipxe/efi/efi_cachedhcp.h>
#include <ipxe/efi/efi_watchdog.h>
+#include <ipxe/efi/efi_path.h>
#include <ipxe/efi/efi_veto.h>
/**
@@ -79,16 +81,19 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
static void efi_init_application ( void ) {
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *devpath = efi_loaded_image_path;
- EFI_DEVICE_PATH_PROTOCOL *filepath = efi_loaded_image->FilePath;
+ struct uri *uri;
+
+ /* Set current working URI from device path, if present */
+ uri = efi_path_uri ( devpath );
+ if ( uri )
+ churi ( uri );
+ uri_put ( uri );
/* Identify autoboot device, if any */
efi_set_autoboot_ll_addr ( device, devpath );
/* Store cached DHCP packet, if any */
efi_cachedhcp_record ( device, devpath );
-
- /* Load autoexec script, if any */
- efi_autoexec_load ( device, filepath );
}
/** EFI application initialisation function */
@@ -103,6 +108,9 @@ struct init_fn efi_init_application_fn __init_fn ( INIT_NORMAL ) = {
*/
static int efi_probe ( struct root_device *rootdev __unused ) {
+ /* Try loading autoexec script */
+ efi_autoexec_load();
+
/* Remove any vetoed drivers */
efi_veto();