diff options
Diffstat (limited to 'src/interface/efi')
-rw-r--r-- | src/interface/efi/efi_autoexec.c | 489 | ||||
-rw-r--r-- | src/interface/efi/efiprefix.c | 11 |
2 files changed, 122 insertions, 378 deletions
diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index fb12cef08..d9ad3b990 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/efiprefix.c b/src/interface/efi/efiprefix.c index f6395b65b..10d8f0bf6 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -81,25 +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 ); - - /* Drop temporary reference to URI */ - uri_put ( uri ); } /** EFI application initialisation function */ @@ -114,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(); |