diff options
Diffstat (limited to 'src/interface/efi')
-rw-r--r-- | src/interface/efi/efi_block.c | 2 | ||||
-rw-r--r-- | src/interface/efi/efi_boot.c | 475 | ||||
-rw-r--r-- | src/interface/efi/efi_fbcon.c | 157 | ||||
-rw-r--r-- | src/interface/efi/efi_local.c | 88 |
4 files changed, 652 insertions, 70 deletions
diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 2f0187a0..a9c3d656 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -60,6 +60,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_null.h> #include <ipxe/efi/efi_block.h> +#include <usr/efiboot.h> /** ACPI table protocol protocol */ static EFI_ACPI_TABLE_PROTOCOL *acpi; @@ -990,6 +991,7 @@ static int efi_block_boot ( unsigned int drive, EFI_STATUS efirc; int rc; + /* Release SNP devices */ efi_snp_release(); diff --git a/src/interface/efi/efi_boot.c b/src/interface/efi/efi_boot.c new file mode 100644 index 00000000..19c31886 --- /dev/null +++ b/src/interface/efi/efi_boot.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2019 Oracle. All rights reserved. + * + * 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 boot local protocols + * + */ + +#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/efi_driver.h> +#include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_snp.h> +#include <ipxe/efi/efi_path.h> +#include <ipxe/efi/efi_block.h> +#include <ipxe/efi/Guid/FileInfo.h> +#include <ipxe/efi/Guid/FileSystemInfo.h> +#include <ipxe/efi/Protocol/BlockIo.h> +#include <ipxe/efi/Protocol/SimpleFileSystem.h> +#include <ipxe/efi/Protocol/AcpiTable.h> + +static wchar_t efi_default_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; + +static EFI_DEVICE_PATH_PROTOCOL **DevicePathList; +static UINTN DevicePathListNum; +static EFI_HANDLE *SimpleFSHandleList; +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 ); + + return; +} + +static int efi_vol_label( EFI_HANDLE handle, char *label_buf, + size_t label_buf_size ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_FILE_SYSTEM_INFO *info; + EFI_FILE_PROTOCOL *root; + EFI_STATUS efirc; + UINTN size; + int rc; + union { + void *interface; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + } u; + + /* Open file system protocol */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_simple_file_system_protocol_guid, + &u.interface, efi_image_handle, + handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -1; + DBG ( "Could not open filesystem on %s\n", + efi_handle_name ( handle ) ); + goto err_filesystem; + } + + /* Open root directory */ + if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) { + rc = -1; + DBG ( "Could not open volume on %s\n", + efi_handle_name ( handle ) ); + goto err_volume; + } + + /* Get length of file system information */ + size = 0; + root->GetInfo ( root, &efi_file_system_info_id, &size, NULL ); + + /* Allocate file system information */ + info = malloc ( size ); + if ( ! info ) { + rc = -1; + goto err_alloc_info; + } + + /* Get file system information */ + if ( ( efirc = root->GetInfo ( root, &efi_file_system_info_id, &size, + info ) ) != 0 ) { + rc = -1; + DBG ( "could not get file system info on %s\n", + efi_handle_name ( handle ) ); + goto err_get_info; + } + DBG ( "Found %s with label \"%ls\"\n", + efi_handle_name ( handle ), info->VolumeLabel ); + + snprintf ( label_buf, label_buf_size, "%ls", info->VolumeLabel ); + + /* Success */ + rc = 0; + + err_get_info: + free ( info ); + err_alloc_info: + root->Close ( root ); + err_volume: + bs->CloseProtocol ( handle, &efi_simple_file_system_protocol_guid, + efi_image_handle, handle ); + err_filesystem: + return rc; +} + +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 *TmpSimpleFSHandleList; + 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; + SimpleFSHandleList = NULL; + + efi_boot_connect_pcibridges (); + + TmpSimpleFSHandleList = efi_boot_get_handlelist ( &efi_simple_file_system_protocol_guid ); + if ( TmpSimpleFSHandleList == NULL ) { + /* valid - no filesystems found */ + efi_boot_map_initialized = TRUE; + return 0; + } + + /* Count number of handles */ + for ( Count = 0 ; TmpSimpleFSHandleList[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 ( TmpSimpleFSHandleList ); + 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 ( TmpSimpleFSHandleList[i] ); + } + + /* 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 ( TmpSimpleFSHandleList ); + bs->FreePool ( TmpDevicePathList ); + efi_boot_map_initialized = TRUE; + + return -1; + } + DevicePathList = (EFI_DEVICE_PATH_PROTOCOL **) buffer; + + /* Allocate our global SimpleFSHandle list */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, + ( Count * + sizeof ( EFI_HANDLE ) + ), + &buffer ) ) != 0 ) { + DBG ( "EFIBOOT efi_boot_create_map: AllocatePool failed!\n" ); + bs->FreePool ( DevicePathList ); + DevicePathList = NULL; + bs->FreePool ( TmpSimpleFSHandleList ); + bs->FreePool ( TmpDevicePathList ); + efi_boot_map_initialized = TRUE; + + return -1; + } + SimpleFSHandleList = (EFI_HANDLE *) buffer; + + /* + * Populate the global SimpleFSHandle and 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]; + SimpleFSHandleList[i] = TmpSimpleFSHandleList[NextIndex]; + TmpDevicePathList[NextIndex] = NULL; + } else { + DevicePathList[i] = NULL; + } + } + + DevicePathListNum = Count; + + bs->FreePool ( TmpSimpleFSHandleList ); + 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_path_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 ) { + char vol_label[256]; + UINTN i; + int rc; + + if ( !efi_boot_map_initialized ) + efi_boot_create_map (); + + printf ( "Drive#\t[Volume Label] Path\n" ); + printf ( "------\t-------------------\n" ); + for ( i = 0 ; i < DevicePathListNum ; i++ ) { + if ( DevicePathList[i] != NULL ) { + rc = efi_vol_label ( SimpleFSHandleList[i], + vol_label, 256 ); + if ( rc || *vol_label == '\0' ) + strcpy(vol_label, "NO VOLUME LABEL"); + printf ( "%d \t[%s] %s\n", (int) i, vol_label, + efi_devpath_text ( DevicePathList[i] ) ); + } + } +} + +int efi_boot_local ( unsigned int drive, const char *filename ) { + + 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; +} diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index d388e031..e3c4d001 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -519,68 +519,70 @@ static int efifb_init ( struct console_configuration *config ) { EFI_STATUS efirc; int rc; - /* Locate graphics output protocol */ - if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid, - NULL, &interface ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not locate graphics output " - "protocol: %s\n", strerror ( rc ) ); - goto err_locate_gop; - } - efifb.gop = interface; + if ( ! config->lazy_update ) { + /* Locate graphics output protocol */ + if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid, + NULL, &interface ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not locate graphics output " + "protocol: %s\n", strerror ( rc ) ); + goto err_locate_gop; + } + efifb.gop = interface; - /* Locate HII font protocol */ - if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid, - NULL, &interface ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n", - strerror ( rc ) ); - goto err_locate_hiifont; - } - efifb.hiifont = interface; + /* Locate HII font protocol */ + if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid, + NULL, &interface ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n", + strerror ( rc ) ); + goto err_locate_hiifont; + } + efifb.hiifont = interface; - /* Locate glyphs */ - if ( ( rc = efifb_glyphs() ) != 0 ) - goto err_glyphs; + /* Locate glyphs */ + if ( ( rc = efifb_glyphs() ) != 0 ) + goto err_glyphs; - /* Save original mode */ - efifb.saved_mode = efifb.gop->Mode->Mode; + /* Save original mode */ + efifb.saved_mode = efifb.gop->Mode->Mode; - /* Select mode */ - if ( ( mode = efifb_select_mode ( config->width, config->height, - config->depth ) ) < 0 ) { - rc = mode; - goto err_select_mode; - } + /* Select mode */ + if ( ( mode = efifb_select_mode ( config->width, config->height, + config->depth ) ) < 0 ) { + rc = mode; + goto err_select_mode; + } - /* Set mode */ - if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not set mode %d: %s\n", - mode, strerror ( rc ) ); - goto err_set_mode; - } - info = efifb.gop->Mode->Info; - - /* Populate colour map */ - bpp = efifb_colour_map ( info, &efifb.map ); - if ( bpp < 0 ) { - rc = bpp; - DBGC ( &efifb, "EFIFB could not build colour map for " - "mode %d: %s\n", mode, strerror ( rc ) ); - goto err_map; - } + /* Set mode */ + if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not set mode %d: %s\n", + mode, strerror ( rc ) ); + goto err_set_mode; + } + info = efifb.gop->Mode->Info; - /* Populate pixel geometry */ - efifb.pixel.width = info->HorizontalResolution; - efifb.pixel.height = info->VerticalResolution; - efifb.pixel.len = ( ( bpp + 7 ) / 8 ); - efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine ); + /* Populate colour map */ + bpp = efifb_colour_map ( info, &efifb.map ); + if ( bpp < 0 ) { + rc = bpp; + DBGC ( &efifb, "EFIFB could not build colour map for " + "mode %d: %s\n", mode, strerror ( rc ) ); + goto err_map; + } - /* Populate frame buffer address */ - efifb.start = efifb.gop->Mode->FrameBufferBase; - DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n", - mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start ); + /* Populate pixel geometry */ + efifb.pixel.width = info->HorizontalResolution; + efifb.pixel.height = info->VerticalResolution; + efifb.pixel.len = ( ( bpp + 7 ) / 8 ); + efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine ); + + /* Populate frame buffer address */ + efifb.start = efifb.gop->Mode->FrameBufferBase; + DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n", + mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start ); +} /* Initialise frame buffer console */ if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ), @@ -638,13 +640,32 @@ static void efifb_putchar ( int character ) { static int efifb_configure ( struct console_configuration *config ) { int rc; - /* Reset console, if applicable */ - if ( ! efifb_console.disabled ) { - efifb_fini(); - efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; - ansicol_reset_magic(); + if ( config && config->lazy_update ) { + if ( efifb_console.disabled ) + return 0; + /* No width/height given, use current so we can update the border */ + if ( ( config->width == 0 ) || ( config->height == 0 ) ) { + if ( ( efifb.pixel.width == 0 ) || ( efifb.pixel.height == 0 ) ) { + return -EINVAL; + } + config->width = efifb.pixel.width; + config->height = efifb.pixel.height; + } else { + /* Otherwise make sure the new dimensions match the old ones */ + if ( ( efifb.pixel.width != config->width ) || + ( efifb.pixel.height != config->height ) ) { + return -EINVAL; + } + } + } else { + /* Reset console, if applicable */ + if ( ! efifb_console.disabled ) { + efifb_fini(); + efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT; + ansicol_reset_magic(); + } + efifb_console.disabled = CONSOLE_DISABLED; } - efifb_console.disabled = CONSOLE_DISABLED; /* Do nothing more unless we have a usable configuration */ if ( ( config == NULL ) || @@ -656,13 +677,15 @@ static int efifb_configure ( struct console_configuration *config ) { if ( ( rc = efifb_init ( config ) ) != 0 ) return rc; - /* Mark console as enabled */ - efifb_console.disabled = 0; - efi_console.disabled |= CONSOLE_DISABLED_OUTPUT; + if ( ! config->lazy_update ) { + /* Mark console as enabled */ + efifb_console.disabled = 0; + efi_console.disabled |= CONSOLE_DISABLED_OUTPUT; - /* Set magic colour to transparent if we have a background picture */ - if ( config->pixbuf ) - ansicol_set_magic_transparent(); + /* Set magic colour to transparent if we have a background picture */ + if ( config->pixbuf ) + ansicol_set_magic_transparent(); + } return 0; } diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index ec6d93a1..b2881424 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -247,6 +247,62 @@ static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, } /** + * Open root filesystem of specified volume by index + * + * @v local Local file + * @v index Volume index + * @ret rc Return status code + */ +static int efi_local_open_volume_index ( struct efi_local *local, + UINTN index ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; + EFI_FILE_PROTOCOL *root; + EFI_HANDLE *handles; + EFI_HANDLE device; + UINTN num_handles; + EFI_STATUS efirc; + int rc; + + /* Locate all filesystem handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, protocol, + NULL, &num_handles, + &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not enumerate handles: " + "%s\n", local, strerror ( rc ) ); + return rc; + } + + /* Find matching handle */ + if ( index >= num_handles ) { + rc = -ENOENT; + } else { + /* Get this device handle */ + device = handles[index]; + + /* Open root directory */ + if ( ( rc = efi_local_open_root ( local, device, &root ) ) == 0 && root ) { + local->root = root; + rc = 0; + } else { + rc = -EACCES; + } + } + + /* Free handles, if applicable */ + bs->FreePool ( handles ); + + /* Fail if we found no matching handle */ + if ( ! local->root ) { + DBGC ( local, "LOCAL %p found no matching device with index %u\n", + local, (unsigned int)index ); + } + + return rc; +} + +/** * Open root filesystem of specified volume * * @v local Local file @@ -581,19 +637,45 @@ static struct process_descriptor efi_local_process_desc = */ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { struct efi_local *local; + int vol; + int rc = 0; /* Allocate and initialise structure */ local = zalloc ( sizeof ( *local ) ); if ( ! local ) return -ENOMEM; ref_init ( &local->refcnt, efi_local_free ); - intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); - process_init_stopped ( &local->process, &efi_local_process_desc, - &local->refcnt ); 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 ) ) != 0 ) { + local->root->Close ( local->root ); + local->root = NULL; + continue; + } + /* Success */ + break; + } + if ( rc != 0 ) { + DBGC ( local, "LOCAL %p could not find %s on any partition\n", + local, local->path ); + ref_put ( &local->refcnt ); + return -ENOENT; + } + } + + 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 ); |