[ipxe-devel] [PATCH 4/4] Complete implementation of efi_boot.c
Aaron Young
Aaron.Young at oracle.com
Wed Jan 9 19:35:42 UTC 2019
Complete implementation of efi_boot.c which creates a static
map of available UEFI filesystems (i.e. "drives"). The map can be displayed
via efi_boot_display_map() which is used by the new efimap command. The
drives can be booted via efi_boot_local() which is called indirectly by the
sanboot command (via efi_block_boot()).
Note the map is ordered by device path (using strncmp()) which
will typically give a PCI BDF ordering as the device paths
typically begin with PciRoot()/Pci() nodes. This will help
maintain consistency between boots.
Also note that all PCI Bridges are "connected" so that all
UEFI PCI filesystems can be discovered. This works around
UEFI implementations which optimize the devices which are
automatically connected to the minimal set required to boot.
Signed-off-by: Aaron Young <Aaron.Young at oracle.com>
---
src/interface/efi/efi_boot.c | 334 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 330 insertions(+), 4 deletions(-)
diff --git a/src/interface/efi/efi_boot.c b/src/interface/efi/efi_boot.c
index 5d44f8b..3e5d72c 100644
--- a/src/interface/efi/efi_boot.c
+++ b/src/interface/efi/efi_boot.c
@@ -32,18 +32,344 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <ipxe/refcnt.h>
+#include <ipxe/list.h>
+#include <ipxe/uri.h>
+#include <ipxe/interface.h>
+#include <ipxe/blockdev.h>
+#include <ipxe/xfer.h>
+#include <ipxe/open.h>
+#include <ipxe/retry.h>
+#include <ipxe/timer.h>
+#include <ipxe/process.h>
+#include <ipxe/sanboot.h>
+#include <ipxe/iso9660.h>
+#include <ipxe/acpi.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/BlockIo.h>
+#include <ipxe/efi/Protocol/SimpleFileSystem.h>
+#include <ipxe/efi/Protocol/AcpiTable.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_snp.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/efi_block.h>
+
+static wchar_t efi_default_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME;
+
+static EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
+static UINTN DevicePathListNum;
+static BOOLEAN efi_boot_map_initialized = FALSE;
+
+static EFI_HANDLE * efi_boot_get_handlelist ( EFI_GUID *ProtocolGuid ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE *HandleList;
+ UINTN Size;
+ EFI_STATUS efirc;
+ EFI_LOCATE_SEARCH_TYPE SearchType;
+
+ /* NULL ProtocolGuid gets all handles in system */
+ if ( ProtocolGuid )
+ SearchType = ByProtocol;
+ else
+ SearchType = AllHandles;
+
+ Size = 0;
+ HandleList = NULL;
+
+ /* First call gets the handle list size and returns BUFFER_TOO_SMALL. */
+ efirc = bs->LocateHandle ( SearchType, (EFI_GUID*) ProtocolGuid,
+ NULL, &Size, HandleList );
+ if ( efirc == EFI_BUFFER_TOO_SMALL ) {
+ /* Alloc an extra handle for NULL list terminator */
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData,
+ ( Size +
+ sizeof ( EFI_HANDLE ) ),
+ (void **) &HandleList ) )
+ != 0 ) {
+ return ( NULL ) ;
+ }
+
+ efirc = bs->LocateHandle ( SearchType, (EFI_GUID*) ProtocolGuid,
+ NULL, &Size, HandleList );
+ if ( HandleList )
+ HandleList[Size / sizeof ( EFI_HANDLE )] = NULL;
+ }
+
+ if ( EFI_ERROR ( efirc ) ) {
+ if ( HandleList )
+ bs->FreePool ( HandleList );
+ return ( NULL ) ;
+ }
+
+ return ( HandleList );
+}
+
+static EFI_DEVICE_PATH_PROTOCOL * efi_boot_get_devpath ( EFI_HANDLE Handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS efirc;
+
+ efirc = bs->HandleProtocol ( Handle, &efi_device_path_protocol_guid,
+ (VOID *) &DevicePath );
+
+ if ( EFI_ERROR ( efirc ) )
+ return NULL;
+ else
+ return DevicePath;
+}
+
+static void efi_boot_connect_pcibridges ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE *HandleList;
+ UINTN Count;
+
+ HandleList = efi_boot_get_handlelist ( &efi_pci_root_bridge_io_protocol_guid );
+ if ( HandleList == NULL ) {
+ DBG ( "EFIBOOT efi_boot_connect_pcibridges: no handles!\n" );
+ return;
+ }
+
+ for ( Count = 0 ; HandleList[Count] != NULL ; Count++ ) {
+
+ DBG ( "EFIBOOT efi_boot_connect_pcibridges: connecting "
+ "handle %s\n", efi_handle_name ( HandleList[Count] ) );
+
+ (void) bs->ConnectController ( HandleList[Count], NULL,
+ NULL, 1 );
+
+ DBG ( "EFIBOOT: handle %s supports protocols:\n",
+ efi_handle_name ( HandleList[Count] ) );
+ DBG_EFI_PROTOCOLS_IF ( LOG, HandleList[Count] );
+ }
+
+ bs->FreePool ( HandleList );
-void efi_boot_display_map ( void ) {
return;
}
+static int efi_boot_create_map ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ UINTN Count;
+ UINTN i,j;
+ INTN NextIndex;
+ EFI_STATUS efirc;
+ EFI_HANDLE *SimpleFSHandleList;
+ EFI_DEVICE_PATH_PROTOCOL **TmpDevicePathList;
+ VOID *buffer;
+ const char *path2;
+ char path1buf[256]; // 256 is the max buf
+ // size used internally
+ // by efi_devpath_text()
+
+ DevicePathList = NULL;
+ DevicePathListNum = 0;
+
+ efi_boot_connect_pcibridges ();
+
+ SimpleFSHandleList = efi_boot_get_handlelist ( &efi_simple_file_system_protocol_guid );
+ if ( SimpleFSHandleList == NULL ) {
+ /* valid - no filesystems found */
+ efi_boot_map_initialized = TRUE;
+ return 0;
+ }
+
+ /* Count number of handles */
+ for ( Count = 0 ; SimpleFSHandleList[Count] != NULL ; Count++ );
+
+ /* Allocate our temporary/local device path list */
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData,
+ ( Count *
+ sizeof ( EFI_DEVICE_PATH_PROTOCOL* )
+ ),
+ &buffer ) ) != 0 ) {
+ DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" );
+ bs->FreePool ( SimpleFSHandleList );
+ efi_boot_map_initialized = TRUE;
+ return -1;
+ }
+ TmpDevicePathList = (EFI_DEVICE_PATH_PROTOCOL **) buffer;
+
+ /* Populate the devpath list */
+ for ( i = 0 ; i < Count; i++ ) {
+ TmpDevicePathList[i] = efi_boot_get_devpath ( SimpleFSHandleList[i] );
+ }
+
+ /* done with the SimpleFShandlelist */
+ bs->FreePool ( SimpleFSHandleList );
+
+ /* Allocate our global device path list */
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData,
+ ( Count *
+ sizeof ( EFI_DEVICE_PATH_PROTOCOL* )
+ ),
+ &buffer ) ) != 0 ) {
+ DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" );
+ bs->FreePool ( TmpDevicePathList );
+ efi_boot_map_initialized = TRUE;
+ return -1;
+ }
+ DevicePathList = (EFI_DEVICE_PATH_PROTOCOL **) buffer;
+
+ /*
+ * Populate the global devicepath list.
+ * For consistency, order the list.
+ * Since each device path begins with PciRoot()/Pci() nodes,
+ * this will essentially give PCI BDF ordering.
+ * Put NULL devicepaths at end of the list (should not happen).
+ */
+ for ( i = 0; i < Count; i++ ) {
+ NextIndex = -1;
+ path1buf[0]='\0';
+ for ( j = 0; j < Count; j++ ) {
+ if ( TmpDevicePathList[j] == NULL )
+ continue;
+ path2 = efi_devpath_text ( TmpDevicePathList[j] );
+ if ( !path2 )
+ continue;
+
+ DBG ( "EFIBOOT %d: next=%d, comparing %s to %s\n",
+ (int) i, (int) NextIndex, path2, path1buf );
+ if ( NextIndex == -1 || strncmp ( path2, path1buf,
+ 256 ) < 0 ) {
+ NextIndex = j;
+ strncpy ( path1buf, path2, 256 );
+ }
+ }
+ if ( NextIndex != -1 ) {
+ DevicePathList[i] = TmpDevicePathList[NextIndex];
+ TmpDevicePathList[NextIndex] = NULL;
+ } else {
+ DevicePathList[i] = NULL;
+ }
+ }
+
+ DevicePathListNum = Count;
+
+ bs->FreePool ( TmpDevicePathList );
+
+ efi_boot_map_initialized = TRUE;
+
+ return 0;
+}
+
+static int efi_boot_local_fs ( EFI_DEVICE_PATH_PROTOCOL *dp,
+ const char *filename ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_DEVICE_PATH_PROTOCOL *boot_path;
+ FILEPATH_DEVICE_PATH *filepath;
+ EFI_DEVICE_PATH_PROTOCOL *end;
+ size_t prefix_len;
+ size_t filepath_len;
+ size_t boot_path_len;
+ EFI_HANDLE image = NULL;
+ EFI_STATUS efirc;
+ int rc;
+
+ if ( dp == NULL )
+ return -1;
+
+ DBG ( "EFIBOOT efi_boot_local_fs: device path %s\n",
+ efi_devpath_text ( dp ) );
+
+ /* Construct device path for boot image */
+ end = efi_devpath_end ( dp );
+ prefix_len = ( ( (void *) end ) - ( (void *) dp ) );
+ filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH +
+ ( filename ? ( ( strlen ( filename ) + 1 ) *
+ sizeof ( filepath->PathName[0] ) ):
+ sizeof ( efi_default_boot_filename ) ) );
+
+ boot_path_len = ( prefix_len + filepath_len + sizeof ( *end ) );
+ boot_path = zalloc ( boot_path_len );
+ if ( !boot_path ) {
+ rc = -1;
+ goto err_alloc_path;
+ }
+
+ memcpy ( boot_path, dp, prefix_len );
+ filepath = ( ( (void *) boot_path ) + prefix_len );
+ filepath->Header.Type = MEDIA_DEVICE_PATH;
+ filepath->Header.SubType = MEDIA_FILEPATH_DP;
+ filepath->Header.Length[0] = ( filepath_len & 0xff );
+ filepath->Header.Length[1] = ( filepath_len >> 8 );
+
+ if ( filename ) {
+ efi_sprintf ( filepath->PathName, "%s", filename );
+ } else {
+ memcpy ( filepath->PathName, efi_default_boot_filename,
+ sizeof ( efi_default_boot_filename ) );
+ }
+
+ end = ( ( (void *) filepath ) + filepath_len );
+ end->Type = END_DEVICE_PATH_TYPE;
+ end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ end->Length[0] = sizeof ( *end );
+
+ /* Release SNP devices */
+ efi_snp_release ();
+
+ DBG ( "EFIBOOT attempt to load %s\n", efi_devpath_text ( boot_path ) );
+
+ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path,
+ NULL, 0, &image ) ) != 0 ) {
+ rc = -1;
+ DBG ( "EFIBOOT failed to load image\n" );
+ goto err_load_image;
+ }
+
+ DBG ( "EFIBOOT successfully loaded image\n" );
+ DBG ( "EFIBOOT trying to start %s\n", efi_devpath_text ( boot_path ) );
+
+ efirc = bs->StartImage ( image, NULL, NULL );
+ if ( EFI_ERROR ( efirc ) )
+ rc = -1;
+ else
+ rc = 0;
+
+ DBG ( "EFIBOOT boot image returned: %d\n", rc );
+
+ bs->UnloadImage ( image );
+
+err_load_image:
+ efi_snp_claim ();
+ free ( boot_path );
+err_alloc_path:
+
+ return rc;
+}
+
+void efi_boot_display_map ( void ) {
+ UINTN i;
+
+ if ( !efi_boot_map_initialized )
+ efi_boot_create_map ();
+
+ printf ( "Drive#\tPath\n" );
+ printf ( "------\t----\n" );
+ for ( i = 0 ; i < DevicePathListNum ; i++ ) {
+ if ( DevicePathList[i] != NULL )
+ printf ( "%d \t%s\n", (int) i,
+
+ efi_devpath_text ( DevicePathList[i] ) );
+ }
+}
+
int efi_boot_local ( unsigned int drive, const char *filename ) {
- // Dummy code to get around compiler for now.
- if (filename)
- return drive;
+ if ( !efi_boot_map_initialized )
+ efi_boot_create_map ();
+
+ if ( DevicePathListNum == 0 || drive > ( DevicePathListNum-1 ) ||
+ DevicePathList[drive] == NULL ) {
+ printf ( "ERROR: Invalid drive number %#02x\n", drive );
+ return -1;
+ }
+
+ efi_boot_local_fs ( DevicePathList[drive], filename );
return 0;
}
--
1.8.3.1
More information about the ipxe-devel
mailing list