mirror of https://github.com/ipxe/ipxe.git
[efi] Generalise block device boot to support arbitrary EFI handles
SAN devices created by iPXE are visible to the firmware, and may be accessed using the firmware's standard block I/O device interface (e.g. INT 13 for BIOS, or EFI_BLOCK_IO_PROTOCOL for UEFI). The iPXE code to perform a SAN boot acts as a client of this standard block I/O device interface, even when the underlying block I/O is being performed by iPXE itself. We rely on this separation to allow the "sanboot" command to be used to boot from a local disk: since the code to perform a SAN boot does not need direct access to an underlying iPXE SAN device, it may be used to boot from any device providing the firmware's standard block I/O device interface. Clean up the EFI SAN boot code to require only a drive number and an EFI_BLOCK_IO_PROTOCOL handle, in preparation for adding support for booting from a local disk under UEFI. Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/1168/head
parent
eb720d2224
commit
37edfea72b
|
@ -211,25 +211,24 @@ efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) {
|
||||||
/**
|
/**
|
||||||
* Connect all possible drivers to EFI block device
|
* Connect all possible drivers to EFI block device
|
||||||
*
|
*
|
||||||
* @v sandev SAN device
|
* @v drive Drive number
|
||||||
|
* @v handle Block device handle
|
||||||
*/
|
*/
|
||||||
static void efi_block_connect ( struct san_device *sandev ) {
|
static void efi_block_connect ( unsigned int drive, EFI_HANDLE handle ) {
|
||||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||||
struct efi_block_data *block = sandev->priv;
|
|
||||||
EFI_STATUS efirc;
|
EFI_STATUS efirc;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Try to connect all possible drivers to this block device */
|
/* Try to connect all possible drivers to this block device */
|
||||||
if ( ( efirc = bs->ConnectController ( block->handle, NULL,
|
if ( ( efirc = bs->ConnectController ( handle, NULL, NULL,
|
||||||
NULL, TRUE ) ) != 0 ) {
|
TRUE ) ) != 0 ) {
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not connect "
|
DBGC ( drive, "EFIBLK %#02x could not connect drivers: %s\n",
|
||||||
"drivers: %s\n", sandev->drive, strerror ( rc ) );
|
drive, strerror ( rc ) );
|
||||||
/* May not be an error; may already be connected */
|
/* May not be an error; may already be connected */
|
||||||
}
|
}
|
||||||
DBGC2 ( sandev->drive, "EFIBLK %#02x supports protocols:\n",
|
DBGC2 ( drive, "EFIBLK %#02x supports protocols:\n", drive );
|
||||||
sandev->drive );
|
DBGC2_EFI_PROTOCOLS ( drive, handle );
|
||||||
DBGC2_EFI_PROTOCOLS ( sandev->drive, block->handle );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -316,7 +315,7 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connect all possible protocols */
|
/* Connect all possible protocols */
|
||||||
efi_block_connect ( sandev );
|
efi_block_connect ( drive, block->handle );
|
||||||
|
|
||||||
return drive;
|
return drive;
|
||||||
|
|
||||||
|
@ -507,12 +506,12 @@ static int efi_block_describe ( void ) {
|
||||||
/**
|
/**
|
||||||
* Check for existence of a file within a filesystem
|
* Check for existence of a file within a filesystem
|
||||||
*
|
*
|
||||||
* @v sandev SAN device
|
* @v drive Drive number
|
||||||
* @v handle Filesystem handle
|
* @v handle Filesystem handle
|
||||||
* @v filename Filename (or NULL to use default)
|
* @v filename Filename (or NULL to use default)
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int efi_block_filename ( struct san_device *sandev, EFI_HANDLE handle,
|
static int efi_block_filename ( unsigned int drive, EFI_HANDLE handle,
|
||||||
const char *filename ) {
|
const char *filename ) {
|
||||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||||
EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
|
EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
|
||||||
|
@ -540,8 +539,8 @@ static int efi_block_filename ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
efi_image_handle, handle,
|
efi_image_handle, handle,
|
||||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not open %s device "
|
DBGC ( drive, "EFIBLK %#02x could not open %s device path: "
|
||||||
"path: %s\n", sandev->drive, efi_handle_name ( handle ),
|
"%s\n", drive, efi_handle_name ( handle ),
|
||||||
strerror ( rc ) );
|
strerror ( rc ) );
|
||||||
goto err_open;
|
goto err_open;
|
||||||
}
|
}
|
||||||
|
@ -549,9 +548,8 @@ static int efi_block_filename ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
/* Open root volume */
|
/* Open root volume */
|
||||||
if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
|
if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not open %s root: "
|
DBGC ( drive, "EFIBLK %#02x could not open %s root: %s\n",
|
||||||
"%s\n", sandev->drive, efi_handle_name ( handle ),
|
drive, efi_handle_name ( handle ), strerror ( rc ) );
|
||||||
strerror ( rc ) );
|
|
||||||
goto err_volume;
|
goto err_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,9 +557,9 @@ static int efi_block_filename ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
if ( ( efirc = root->Open ( root, &file, wname,
|
if ( ( efirc = root->Open ( root, &file, wname,
|
||||||
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
|
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not open %s/%ls: "
|
DBGC ( drive, "EFIBLK %#02x could not open %s/%ls: %s\n",
|
||||||
"%s\n", sandev->drive, efi_handle_name ( handle ),
|
drive, efi_handle_name ( handle ), wname,
|
||||||
wname, strerror ( rc ) );
|
strerror ( rc ) );
|
||||||
goto err_file;
|
goto err_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,14 +578,14 @@ static int efi_block_filename ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
/**
|
/**
|
||||||
* Check EFI block device filesystem match
|
* Check EFI block device filesystem match
|
||||||
*
|
*
|
||||||
* @v sandev SAN device
|
* @v drive Drive number
|
||||||
* @v handle Filesystem handle
|
* @v handle Filesystem handle
|
||||||
* @v path Block device path
|
* @v path Block device path
|
||||||
* @v filename Filename (or NULL to use default)
|
* @v filename Filename (or NULL to use default)
|
||||||
* @v fspath Filesystem device path to fill in
|
* @v fspath Filesystem device path to fill in
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int efi_block_match ( struct san_device *sandev, EFI_HANDLE handle,
|
static int efi_block_match ( unsigned int drive, EFI_HANDLE handle,
|
||||||
EFI_DEVICE_PATH_PROTOCOL *path,
|
EFI_DEVICE_PATH_PROTOCOL *path,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
EFI_DEVICE_PATH_PROTOCOL **fspath ) {
|
EFI_DEVICE_PATH_PROTOCOL **fspath ) {
|
||||||
|
@ -605,8 +603,8 @@ static int efi_block_match ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
efi_image_handle, handle,
|
efi_image_handle, handle,
|
||||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not open %s device "
|
DBGC ( drive, "EFIBLK %#02x could not open %s device path: "
|
||||||
"path: %s\n", sandev->drive, efi_handle_name ( handle ),
|
"%s\n", drive, efi_handle_name ( handle ),
|
||||||
strerror ( rc ) );
|
strerror ( rc ) );
|
||||||
goto err_open;
|
goto err_open;
|
||||||
}
|
}
|
||||||
|
@ -616,15 +614,15 @@ static int efi_block_match ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
if ( memcmp ( u.path, path, efi_path_len ( path ) ) != 0 ) {
|
if ( memcmp ( u.path, path, efi_path_len ( path ) ) != 0 ) {
|
||||||
/* Not a child device */
|
/* Not a child device */
|
||||||
rc = -ENOTTY;
|
rc = -ENOTTY;
|
||||||
DBGC2 ( sandev->drive, "EFIBLK %#02x is not parent of %s\n",
|
DBGC2 ( drive, "EFIBLK %#02x is not parent of %s\n",
|
||||||
sandev->drive, efi_handle_name ( handle ) );
|
drive, efi_handle_name ( handle ) );
|
||||||
goto err_not_child;
|
goto err_not_child;
|
||||||
}
|
}
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x contains filesystem %s\n",
|
DBGC ( drive, "EFIBLK %#02x contains filesystem %s\n",
|
||||||
sandev->drive, efi_devpath_text ( u.path ) );
|
drive, efi_devpath_text ( u.path ) );
|
||||||
|
|
||||||
/* Check if filesystem contains boot filename */
|
/* Check if filesystem contains boot filename */
|
||||||
if ( ( rc = efi_block_filename ( sandev, handle, filename ) ) != 0 )
|
if ( ( rc = efi_block_filename ( drive, handle, filename ) ) != 0 )
|
||||||
goto err_filename;
|
goto err_filename;
|
||||||
|
|
||||||
/* Success */
|
/* Success */
|
||||||
|
@ -640,15 +638,21 @@ static int efi_block_match ( struct san_device *sandev, EFI_HANDLE handle,
|
||||||
/**
|
/**
|
||||||
* Scan EFI block device for a matching filesystem
|
* Scan EFI block device for a matching filesystem
|
||||||
*
|
*
|
||||||
* @v sandev SAN device
|
* @v drive Drive number
|
||||||
|
* @v handle Block device handle
|
||||||
* @v filename Filename (or NULL to use default)
|
* @v filename Filename (or NULL to use default)
|
||||||
* @v fspath Filesystem device path to fill in
|
* @v fspath Filesystem device path to fill in
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int efi_block_scan ( struct san_device *sandev, const char *filename,
|
static int efi_block_scan ( unsigned int drive, EFI_HANDLE handle,
|
||||||
|
const char *filename,
|
||||||
EFI_DEVICE_PATH_PROTOCOL **fspath ) {
|
EFI_DEVICE_PATH_PROTOCOL **fspath ) {
|
||||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||||
struct efi_block_data *block = sandev->priv;
|
EFI_GUID *protocol = &efi_device_path_protocol_guid;
|
||||||
|
union {
|
||||||
|
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||||
|
void *interface;
|
||||||
|
} u;
|
||||||
EFI_HANDLE *handles;
|
EFI_HANDLE *handles;
|
||||||
UINTN count;
|
UINTN count;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -656,15 +660,25 @@ static int efi_block_scan ( struct san_device *sandev, const char *filename,
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Connect up possible file system drivers */
|
/* Connect up possible file system drivers */
|
||||||
efi_block_connect ( sandev );
|
efi_block_connect ( drive, handle );
|
||||||
|
|
||||||
|
/* Identify device path */
|
||||||
|
if ( ( efirc = bs->OpenProtocol ( handle, protocol, &u.interface,
|
||||||
|
efi_image_handle, handle,
|
||||||
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
||||||
|
rc = -EEFI ( efirc );
|
||||||
|
DBGC ( drive, "EFIBLK %#02x could not open device path: %s\n",
|
||||||
|
drive, strerror ( rc ) );
|
||||||
|
goto err_open;
|
||||||
|
}
|
||||||
|
|
||||||
/* Locate all Simple File System protocol handles */
|
/* Locate all Simple File System protocol handles */
|
||||||
if ( ( efirc = bs->LocateHandleBuffer (
|
if ( ( efirc = bs->LocateHandleBuffer (
|
||||||
ByProtocol, &efi_simple_file_system_protocol_guid,
|
ByProtocol, &efi_simple_file_system_protocol_guid,
|
||||||
NULL, &count, &handles ) ) != 0 ) {
|
NULL, &count, &handles ) ) != 0 ) {
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x cannot locate file "
|
DBGC ( drive, "EFIBLK %#02x cannot locate file systems: %s\n",
|
||||||
"systems: %s\n", sandev->drive, strerror ( rc ) );
|
drive, strerror ( rc ) );
|
||||||
goto err_locate;
|
goto err_locate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,7 +687,7 @@ static int efi_block_scan ( struct san_device *sandev, const char *filename,
|
||||||
for ( i = 0 ; i < count ; i++ ) {
|
for ( i = 0 ; i < count ; i++ ) {
|
||||||
|
|
||||||
/* Check for a matching filesystem */
|
/* Check for a matching filesystem */
|
||||||
if ( ( rc = efi_block_match ( sandev, handles[i], block->path,
|
if ( ( rc = efi_block_match ( drive, handles[i], u.path,
|
||||||
filename, fspath ) ) != 0 )
|
filename, fspath ) ) != 0 )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -682,18 +696,20 @@ static int efi_block_scan ( struct san_device *sandev, const char *filename,
|
||||||
|
|
||||||
bs->FreePool ( handles );
|
bs->FreePool ( handles );
|
||||||
err_locate:
|
err_locate:
|
||||||
|
bs->CloseProtocol ( handle, protocol, efi_image_handle, handle );
|
||||||
|
err_open:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boot from EFI block device filesystem boot image
|
* Boot from EFI block device filesystem boot image
|
||||||
*
|
*
|
||||||
* @v sandev SAN device
|
* @v drive Drive number
|
||||||
* @v fspath Filesystem device path
|
* @v fspath Filesystem device path
|
||||||
* @v filename Filename (or NULL to use default)
|
* @v filename Filename (or NULL to use default)
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int efi_block_exec ( struct san_device *sandev,
|
static int efi_block_exec ( unsigned int drive,
|
||||||
EFI_DEVICE_PATH_PROTOCOL *fspath,
|
EFI_DEVICE_PATH_PROTOCOL *fspath,
|
||||||
const char *filename ) {
|
const char *filename ) {
|
||||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||||
|
@ -735,16 +751,16 @@ static int efi_block_exec ( struct san_device *sandev,
|
||||||
}
|
}
|
||||||
end = ( ( ( void * ) filepath ) + filepath_len );
|
end = ( ( ( void * ) filepath ) + filepath_len );
|
||||||
efi_path_terminate ( end );
|
efi_path_terminate ( end );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x trying to load %s\n",
|
DBGC ( drive, "EFIBLK %#02x trying to load %s\n",
|
||||||
sandev->drive, efi_devpath_text ( path ) );
|
drive, efi_devpath_text ( path ) );
|
||||||
|
|
||||||
/* Load image */
|
/* Load image */
|
||||||
image = NULL;
|
image = NULL;
|
||||||
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path, NULL, 0,
|
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path, NULL, 0,
|
||||||
&image ) ) != 0 ) {
|
&image ) ) != 0 ) {
|
||||||
rc = -EEFI ( efirc );
|
rc = -EEFI ( efirc );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x could not load: %s\n",
|
DBGC ( drive, "EFIBLK %#02x could not load: %s\n",
|
||||||
sandev->drive, strerror ( rc ) );
|
drive, strerror ( rc ) );
|
||||||
if ( efirc == EFI_SECURITY_VIOLATION ) {
|
if ( efirc == EFI_SECURITY_VIOLATION ) {
|
||||||
goto err_load_security_violation;
|
goto err_load_security_violation;
|
||||||
} else {
|
} else {
|
||||||
|
@ -755,8 +771,8 @@ static int efi_block_exec ( struct san_device *sandev,
|
||||||
/* Start image */
|
/* Start image */
|
||||||
efirc = bs->StartImage ( image, NULL, NULL );
|
efirc = bs->StartImage ( image, NULL, NULL );
|
||||||
rc = ( efirc ? -EEFI ( efirc ) : 0 );
|
rc = ( efirc ? -EEFI ( efirc ) : 0 );
|
||||||
DBGC ( sandev->drive, "EFIBLK %#02x boot image returned: %s\n",
|
DBGC ( drive, "EFIBLK %#02x boot image returned: %s\n",
|
||||||
sandev->drive, strerror ( rc ) );
|
drive, strerror ( rc ) );
|
||||||
|
|
||||||
err_load_security_violation:
|
err_load_security_violation:
|
||||||
bs->UnloadImage ( image );
|
bs->UnloadImage ( image );
|
||||||
|
@ -776,6 +792,7 @@ static int efi_block_exec ( struct san_device *sandev,
|
||||||
static int efi_block_boot ( unsigned int drive, const char *filename ) {
|
static int efi_block_boot ( unsigned int drive, const char *filename ) {
|
||||||
EFI_DEVICE_PATH_PROTOCOL *fspath = NULL;
|
EFI_DEVICE_PATH_PROTOCOL *fspath = NULL;
|
||||||
struct san_device *sandev;
|
struct san_device *sandev;
|
||||||
|
struct efi_block_data *block;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Find SAN device */
|
/* Find SAN device */
|
||||||
|
@ -785,17 +802,19 @@ static int efi_block_boot ( unsigned int drive, const char *filename ) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto err_sandev_find;
|
goto err_sandev_find;
|
||||||
}
|
}
|
||||||
|
block = sandev->priv;
|
||||||
|
|
||||||
/* Release SNP devices */
|
/* Release SNP devices */
|
||||||
efi_snp_release();
|
efi_snp_release();
|
||||||
|
|
||||||
/* Scan for a matching filesystem within this block device */
|
/* Scan for a matching filesystem within this block device */
|
||||||
if ( ( rc = efi_block_scan ( sandev, filename, &fspath ) ) != 0 ) {
|
if ( ( rc = efi_block_scan ( drive, block->handle, filename,
|
||||||
|
&fspath ) ) != 0 ) {
|
||||||
goto err_scan;
|
goto err_scan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to boot from this filesystem */
|
/* Attempt to boot from this filesystem */
|
||||||
if ( ( rc = efi_block_exec ( sandev, fspath, filename ) ) != 0 )
|
if ( ( rc = efi_block_exec ( drive, fspath, filename ) ) != 0 )
|
||||||
goto err_exec;
|
goto err_exec;
|
||||||
|
|
||||||
err_exec:
|
err_exec:
|
||||||
|
|
Loading…
Reference in New Issue