mirror of
https://git.ipxe.org/ipxe.git
synced 2024-11-27 03:54:28 +08:00
[efi] Restructure handling of autoexec.ipxe script
We currently attempt to obtain the autoexec.ipxe script via early use of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or EFI_PXE_BASE_CODE_PROTOCOL interfaces to obtain an opaque block of memory, which is then registered as an image at an appropriate point during our startup sequence. The early use of these existent interfaces allows us to obtain the script even if our subsequent actions (e.g. disconnecting drivers in order to connect up our own) may cause the script to become inaccessible. This mirrors the approach used under BIOS, where the autoexec.ipxe script is provided by the prefix (e.g. as an initrd image when using the .lkrn build of iPXE) and so must be copied into a normally allocated image from wherever it happens to previously exist in memory. We do not currently have support for downloading an autoexec.ipxe script if we were ourselves downloaded via UEFI HTTP boot. There is an EFI_HTTP_PROTOCOL defined within the UEFI specification, but it is so poorly designed as to be unusable for the simple purpose of downloading an additional file from the same directory. It provides almost nothing more than a very slim wrapper around EFI_TCP4_PROTOCOL (or EFI_TCP6_PROTOCOL). It will not handle redirection, content encoding, retries, or even fundamentals such as the Content-Length header, leaving all of this up to the caller. The UEFI HTTP Boot driver will install an EFI_LOAD_FILE_PROTOCOL instance on the loaded image's device handle. This looks promising at first since it provides the LoadFile() API call which is specified to accept an arbitrary filename parameter. However, experimentation (and inspection of the code in EDK2) reveals a multitude of problems that prevent this from being usable. Calling LoadFile() will idiotically restart the entire DHCP process (and potentially pop up a UI requiring input from the user for e.g. a wireless network password). The filename provided to LoadFile() will be ignored. Any downloaded file will be rejected unless it happens to match one of the limited set of types expected by the UEFI HTTP Boot driver. The list of design failures and conceptual mismatches is fairly impressive. Choose to bypass every possible aspect of UEFI HTTP support, and instead use our own HTTP client and network stack to download the autoexec.ipxe script over a temporary MNP network device. Since this approach works for TFTP as well as HTTP, drop the direct use of EFI_PXE_BASE_CODE_PROTOCOL. For consistency and simplicity, also drop the direct use of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL and rely upon our existing support to access local files via "file:" URIs. This approach results in console output during the "iPXE initialising devices...ok" message that appears while startup is in progress. Remove the trailing "ok" so that this intermediate output appears at a sensible location on the screen. The welcome banner that will be printed immediately afterwards provides an indication that startup has completed successfully even absent the explicit "ok". Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
b940d54235
commit
165995b7e9
@ -32,9 +32,8 @@ __asmcall int main ( void ) {
|
||||
initialise();
|
||||
|
||||
/* Some devices take an unreasonably long time to initialise */
|
||||
printf ( "%s initialising devices...", product_short_name );
|
||||
printf ( "%s initialising devices...\n", product_short_name );
|
||||
startup();
|
||||
printf ( "ok\n" );
|
||||
|
||||
/* Attempt to boot */
|
||||
if ( ( rc = ipxe ( NULL ) ) != 0 )
|
||||
|
@ -9,9 +9,6 @@
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/efi/efi.h>
|
||||
|
||||
extern int efi_autoexec_load ( EFI_HANDLE device,
|
||||
EFI_DEVICE_PATH_PROTOCOL *path );
|
||||
extern int efi_autoexec_load ( void );
|
||||
|
||||
#endif /* _IPXE_EFI_AUTOEXEC_H */
|
||||
|
@ -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";
|
||||
#define EFI_AUTOEXEC_NAME "autoexec.ipxe"
|
||||
|
||||
/** Autoexec script (if any) */
|
||||
static void *efi_autoexec;
|
||||
|
||||
/** Autoexec script length */
|
||||
static size_t efi_autoexec_len;
|
||||
/** 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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 ) );
|
||||
/* Try loading from loaded image directory, if supported */
|
||||
if ( ( rc = imgacquire ( "file:" EFI_AUTOEXEC_NAME,
|
||||
EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 )
|
||||
return 0;
|
||||
|
||||
/* 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",
|
||||
/* Try loading from root directory, if supported */
|
||||
if ( ( rc = imgacquire ( "file:/" EFI_AUTOEXEC_NAME,
|
||||
EFI_AUTOEXEC_TIMEOUT, image ) ) == 0 )
|
||||
return 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load autoexec script via temporary network device
|
||||
*
|
||||
* @v handle Managed network protocol service binding handle
|
||||
* @v image Image to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) {
|
||||
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
|
||||
struct net_device *netdev;
|
||||
int rc;
|
||||
|
||||
/* 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_filesystem;
|
||||
goto err_create;
|
||||
}
|
||||
|
||||
/* 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",
|
||||
/* 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_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;
|
||||
/* 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 ) );
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* Ensure network exchanges have completed */
|
||||
sync ( EFI_AUTOEXEC_TIMEOUT );
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Record autoexec script */
|
||||
efi_autoexec = data;
|
||||
efi_autoexec_len = size;
|
||||
data = NULL;
|
||||
DBGC ( device, "EFI %s found %ls\n", efi_handle_name ( device ), wname );
|
||||
|
||||
/* Success */
|
||||
rc = 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:
|
||||
mnptemp_destroy ( netdev );
|
||||
err_create:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load autoexec script from TFTP server
|
||||
*
|
||||
* @v device Device handle
|
||||
* @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;
|
||||
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",
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 );
|
||||
|
||||
/* 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:
|
||||
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 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;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( efi_autoexec == NULL );
|
||||
assert ( efi_autoexec_len == 0 );
|
||||
/* Use first applicable loader */
|
||||
for ( i = 0 ; i < ( sizeof ( efi_autoexec_loaders ) /
|
||||
sizeof ( efi_autoexec_loaders[0] ) ) ; i ++ ) {
|
||||
|
||||
/* Try loading from file system loaded image directory, if supported */
|
||||
if ( ( rc = efi_autoexec_filesystem ( device, path ) ) == 0 )
|
||||
return 0;
|
||||
/* 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 from file system root directory, if supported */
|
||||
if ( ( rc = efi_autoexec_filesystem ( device, NULL ) ) == 0 )
|
||||
return 0;
|
||||
/* Try loading */
|
||||
if ( ( rc = loader->load ( handle, &image ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Try loading via TFTP, if supported */
|
||||
if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 )
|
||||
/* 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;
|
||||
}
|
||||
|
||||
DBGC ( device, "EFI %s loaded %s (%zd bytes)\n",
|
||||
efi_handle_name ( device ), image->name, image->len );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register autoexec script
|
||||
*
|
||||
*/
|
||||
static void efi_autoexec_startup ( void ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
|
||||
struct image *image;
|
||||
|
||||
/* Do nothing if we have no autoexec script */
|
||||
if ( ! efi_autoexec )
|
||||
return;
|
||||
|
||||
/* 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 registered %s\n",
|
||||
efi_handle_name ( device ), efi_autoexec_name );
|
||||
|
||||
/* Free temporary copy */
|
||||
bs->FreePool ( efi_autoexec );
|
||||
efi_autoexec = NULL;
|
||||
}
|
||||
|
||||
/** Autoexec script startup function */
|
||||
struct startup_fn efi_autoexec_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
|
||||
.name = "efi_autoexec",
|
||||
.startup = efi_autoexec_startup,
|
||||
};
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user