From 23d388418e8066fc5d609d57c4a0032a982a2dbc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 6 Mar 2017 14:22:51 +0000 Subject: [PATCH] [efi] Refactor to use centralised SAN device abstraction Signed-off-by: Michael Brown --- src/interface/efi/efi_block.c | 835 +++++++--------------------------- 1 file changed, 173 insertions(+), 662 deletions(-) diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index ee26b37d..8eb300a1 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -55,9 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** Timeout for EFI block device commands (in ticks) */ -#define EFI_BLOCK_TIMEOUT ( 15 * TICKS_PER_SEC ) - /** Boot filename */ static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; @@ -78,38 +75,10 @@ struct efi_block_vendor_path { CHAR16 uri[0]; } __attribute__ (( packed )); -/** An EFI block device */ -struct efi_block { - /** Reference count */ - struct refcnt refcnt; - /** List of all registered block devices */ - struct list_head list; - - /** Block device URI */ - struct uri *uri; - /** Drive number */ - unsigned int drive; - /** Underlying block device interface */ - struct interface intf; - - /** Current device status */ - int block_rc; - /** Raw block device capacity */ - struct block_device_capacity capacity; - /** Block size shift - * - * To allow for emulation of CD-ROM access, this represents - * the left-shift required to translate from EFI I/O blocks to - * underlying blocks. - * - * Note that the LogicalBlocksPerPhysicalBlock field in - * EFI_BLOCK_IO_MEDIA is unable to encapsulate this - * information, since it does not allow us to describe a - * situation in which there are multiple "physical" - * (i.e. underlying) blocks per logical (i.e. exposed) block. - */ - unsigned int blksize_shift; - +/** EFI SAN device private data */ +struct efi_block_data { + /** SAN device */ + struct san_device *sandev; /** EFI handle */ EFI_HANDLE handle; /** Media descriptor */ @@ -118,319 +87,41 @@ struct efi_block { EFI_BLOCK_IO_PROTOCOL block_io; /** Device path protocol */ EFI_DEVICE_PATH_PROTOCOL *path; - - /** Command interface */ - struct interface command; - /** Command timeout timer */ - struct retry_timer timer; - /** Command status */ - int command_rc; }; /** - * Free EFI block device + * Read from or write to EFI block device * - * @v refcnt Reference count - */ -static void efi_block_free ( struct refcnt *refcnt ) { - struct efi_block *block = - container_of ( refcnt, struct efi_block, refcnt ); - - assert ( ! timer_running ( &block->timer ) ); - uri_put ( block->uri ); - free ( block->path ); - free ( block ); -} - -/** List of EFI block devices */ -static LIST_HEAD ( efi_block_devices ); - -/** - * Find EFI block device - * - * @v drive Drive number - * @ret block Block device, or NULL if not found - */ -static struct efi_block * efi_block_find ( unsigned int drive ) { - struct efi_block *block; - - list_for_each_entry ( block, &efi_block_devices, list ) { - if ( block->drive == drive ) - return block; - } - - return NULL; -} - -/** - * Close EFI block device command - * - * @v block Block device - * @v rc Reason for close - */ -static void efi_block_cmd_close ( struct efi_block *block, int rc ) { - - /* Stop timer */ - stop_timer ( &block->timer ); - - /* Restart interface */ - intf_restart ( &block->command, rc ); - - /* Record command status */ - block->command_rc = rc; -} - -/** - * Record EFI block device capacity - * - * @v block Block device - * @v capacity Block device capacity - */ -static void efi_block_cmd_capacity ( struct efi_block *block, - struct block_device_capacity *capacity ) { - - /* Record raw capacity information */ - memcpy ( &block->capacity, capacity, sizeof ( block->capacity ) ); -} - -/** EFI block device command interface operations */ -static struct interface_operation efi_block_cmd_op[] = { - INTF_OP ( intf_close, struct efi_block *, efi_block_cmd_close ), - INTF_OP ( block_capacity, struct efi_block *, efi_block_cmd_capacity ), -}; - -/** EFI block device command interface descriptor */ -static struct interface_descriptor efi_block_cmd_desc = - INTF_DESC ( struct efi_block, command, efi_block_cmd_op ); - -/** - * Handle EFI block device command timeout - * - * @v retry Retry timer - */ -static void efi_block_cmd_expired ( struct retry_timer *timer, - int over __unused ) { - struct efi_block *block = - container_of ( timer, struct efi_block, timer ); - - efi_block_cmd_close ( block, -ETIMEDOUT ); -} - -/** - * Restart EFI block device interface - * - * @v block Block device - * @v rc Reason for restart - */ -static void efi_block_restart ( struct efi_block *block, int rc ) { - - /* Restart block device interface */ - intf_nullify ( &block->command ); /* avoid potential loops */ - intf_restart ( &block->intf, rc ); - - /* Close any outstanding command */ - efi_block_cmd_close ( block, rc ); - - /* Record device error */ - block->block_rc = rc; -} - -/** - * (Re)open EFI block device - * - * @v block Block device - * @ret rc Return status code - * - * This function will block until the device is available. - */ -static int efi_block_reopen ( struct efi_block *block ) { - int rc; - - /* Close any outstanding command and restart interface */ - efi_block_restart ( block, -ECONNRESET ); - - /* Mark device as being not yet open */ - block->block_rc = -EINPROGRESS; - - /* Open block device interface */ - if ( ( rc = xfer_open_uri ( &block->intf, block->uri ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not (re)open URI: %s\n", - block->drive, strerror ( rc ) ); - return rc; - } - - /* Wait for device to become available */ - while ( block->block_rc == -EINPROGRESS ) { - step(); - if ( xfer_window ( &block->intf ) != 0 ) { - block->block_rc = 0; - return 0; - } - } - - DBGC ( block, "EFIBLK %#02x never became available: %s\n", - block->drive, strerror ( block->block_rc ) ); - return block->block_rc; -} - -/** - * Handle closure of underlying block device interface - * - * @v block Block device - * @ret rc Reason for close - */ -static void efi_block_close ( struct efi_block *block, int rc ) { - - /* Any closure is an error from our point of view */ - if ( rc == 0 ) - rc = -ENOTCONN; - DBGC ( block, "EFIBLK %#02x went away: %s\n", - block->drive, strerror ( rc ) ); - - /* Close any outstanding command and restart interface */ - efi_block_restart ( block, rc ); -} - -/** - * Check EFI block device flow control window - * - * @v block Block device - */ -static size_t efi_block_window ( struct efi_block *block __unused ) { - - /* We are never ready to receive data via this interface. - * This prevents objects that support both block and stream - * interfaces from attempting to send us stream data. - */ - return 0; -} - -/** EFI block device interface operations */ -static struct interface_operation efi_block_op[] = { - INTF_OP ( intf_close, struct efi_block *, efi_block_close ), - INTF_OP ( xfer_window, struct efi_block *, efi_block_window ), -}; - -/** EFI block device interface descriptor */ -static struct interface_descriptor efi_block_desc = - INTF_DESC ( struct efi_block, intf, efi_block_op ); - -/** EFI block device command context */ -struct efi_block_command_context { - /** Starting LBA (using EFI block numbering) */ - uint64_t lba; - /** Data buffer */ - void *data; - /** Length of data buffer */ - size_t len; - /** Block device read/write operation (if any) */ - int ( * block_rw ) ( struct interface *control, struct interface *data, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len ); -}; - -/** - * Initiate EFI block device command - * - * @v block Block device - * @v op Command operation - * @v context Command context, or NULL if not required + * @v sandev SAN device + * @v lba Starting LBA + * @v data Data buffer + * @v len Size of buffer + * @v block_rw Block read/write method * @ret rc Return status code */ -static int efi_block_command ( struct efi_block *block, - int ( * op ) ( struct efi_block *block, - struct efi_block_command_context - *context ), - struct efi_block_command_context *context ) { - int rc; - - /* Sanity check */ - assert ( ! timer_running ( &block->timer ) ); - - /* Reopen block device if applicable */ - if ( ( block->block_rc != 0 ) && - ( ( rc = efi_block_reopen ( block ) ) != 0 ) ) { - goto err_reopen; - } - - /* Start expiry timer */ - start_timer_fixed ( &block->timer, EFI_BLOCK_TIMEOUT ); - - /* Initiate block device operation */ - if ( ( rc = op ( block, context ) ) != 0 ) - goto err_op; - - /* Wait for command to complete */ - while ( timer_running ( &block->timer ) ) - step(); - - /* Collect return status */ - rc = block->command_rc; - - return rc; - - err_op: - stop_timer ( &block->timer ); - err_reopen: - return rc; -} - -/** - * Initiate EFI block device read/write command - * - * @v block Block device - * @v context Command context - * @ret rc Return status code - */ -static int efi_block_cmd_rw ( struct efi_block *block, - struct efi_block_command_context *context ) { - uint64_t lba; +static int efi_block_rw ( struct san_device *sandev, uint64_t lba, + void *data, size_t len, + int ( * block_rw ) ( struct interface *control, + struct interface *data, + uint64_t lba, unsigned int count, + userptr_t buffer, size_t len ) ){ + struct efi_block_data *block = sandev->priv; unsigned int count; int rc; - /* Calculate underlying starting LBA and block count */ - if ( block->capacity.blksize == 0 ) { - DBGC ( block, "EFIBLK %#02x has zero block size\n", - block->drive ); - return -EINVAL; - } - lba = ( context->lba << block->blksize_shift ); - count = ( context->len / block->capacity.blksize ); - if ( ( count * block->capacity.blksize ) != context->len ) { - DBGC ( block, "EFIBLK %#02x invalid read/write length %#zx\n", - block->drive, context->len ); + /* Sanity check */ + count = ( len / block->media.BlockSize ); + if ( ( count * block->media.BlockSize ) != len ) { + DBGC ( sandev, "EFIBLK %#02x impossible length %#zx\n", + sandev->drive, len ); return -EINVAL; } - /* Initiate read/write command */ - if ( ( rc = context->block_rw ( &block->intf, &block->command, lba, - count, virt_to_user ( context->data ), - context->len ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not initiate read/write: " - "%s\n", block->drive, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Initiate EFI block device read capacity command - * - * @v block Block device - * @v context Command context - * @ret rc Return status code - */ -static int efi_block_cmd_read_capacity ( struct efi_block *block, - struct efi_block_command_context - *context __unused ) { - int rc; - - /* Initiate read capacity command */ - if ( ( rc = block_read_capacity ( &block->intf, - &block->command ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not read capacity: %s\n", - block->drive, strerror ( rc ) ); + /* Read from / write to block device */ + if ( ( rc = sandev_rw ( sandev, lba, count, virt_to_user ( data ), + block_rw ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x I/O failed: %s\n", + sandev->drive, strerror ( rc ) ); return rc; } @@ -446,20 +137,14 @@ static int efi_block_cmd_read_capacity ( struct efi_block *block, */ static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, BOOLEAN verify __unused ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x reset\n", block->drive ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x reset\n", sandev->drive ); efi_snp_claim(); - - /* Reopen block device */ - if ( ( rc = efi_block_reopen ( block ) ) != 0 ) - goto err_reopen; - - err_reopen: + rc = sandev_reset ( sandev ); efi_snp_release(); return EFIRC ( rc ); } @@ -477,28 +162,15 @@ static EFI_STATUS EFIAPI efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *block_io, static EFI_STATUS EFIAPI efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, EFI_LBA lba, UINTN len, VOID *data ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); - struct efi_block_command_context context = { - .lba = lba, - .data = data, - .len = len, - .block_rw = block_read, - }; + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", - block->drive, context.lba, context.data, context.len ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x read LBA %#08llx to %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - - /* Issue read command */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) - goto err_command; - - err_command: + rc = efi_block_rw ( sandev, lba, data, len, block_read ); efi_snp_release(); return EFIRC ( rc ); } @@ -516,28 +188,15 @@ efi_block_io_read ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, static EFI_STATUS EFIAPI efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, EFI_LBA lba, UINTN len, VOID *data ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); - struct efi_block_command_context context = { - .lba = lba, - .data = data, - .len = len, - .block_rw = block_write, - }; + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; int rc; - DBGC2 ( block, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", - block->drive, context.lba, context.data, context.len ); - - /* Claim network devices for use by iPXE */ + DBGC2 ( sandev, "EFIBLK %#02x write LBA %#08llx from %p+%#08zx\n", + sandev->drive, lba, data, ( ( size_t ) len ) ); efi_snp_claim(); - - /* Issue write command */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) - goto err_command; - - err_command: + rc = efi_block_rw ( sandev, lba, data, len, block_write ); efi_snp_release(); return EFIRC ( rc ); } @@ -550,52 +209,100 @@ efi_block_io_write ( EFI_BLOCK_IO_PROTOCOL *block_io, UINT32 media __unused, */ static EFI_STATUS EFIAPI efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) { - struct efi_block *block = - container_of ( block_io, struct efi_block, block_io ); + struct efi_block_data *block = + container_of ( block_io, struct efi_block_data, block_io ); + struct san_device *sandev = block->sandev; - DBGC2 ( block, "EFIBLK %#02x flush\n", block->drive ); + DBGC2 ( sandev, "EFIBLK %#02x flush\n", sandev->drive ); /* Nothing to do */ return 0; } /** - * Create device path for EFI block device + * Connect all possible drivers to EFI block device * - * @v uri Block device URI - * @v parent Parent device path - * @ret path Device path, or NULL on failure - * - * The caller must eventually free() the device path. + * @v sandev SAN device */ -static EFI_DEVICE_PATH_PROTOCOL * -efi_block_path ( struct uri *uri, EFI_DEVICE_PATH_PROTOCOL *parent ) { - EFI_DEVICE_PATH_PROTOCOL *path; - struct efi_block_vendor_path *vendor; +static void efi_block_connect ( struct san_device *sandev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; + EFI_STATUS efirc; + int rc; + + /* Try to connect all possible drivers to this block device */ + if ( ( efirc = bs->ConnectController ( block->handle, NULL, + NULL, 1 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( sandev, "EFIBLK %#02x could not connect drivers: %s\n", + sandev->drive, strerror ( rc ) ); + /* May not be an error; may already be connected */ + } + DBGC2 ( sandev, "EFIBLK %#02x supports protocols:\n", sandev->drive ); + DBGC2_EFI_PROTOCOLS ( sandev, block->handle ); +} + +/** + * Hook EFI block device + * + * @v uri URI + * @v drive Drive number + * @ret drive Drive number, or negative error + */ +static int efi_block_hook ( struct uri *uri, unsigned int drive ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_DEVICE_PATH_PROTOCOL *end; + struct efi_block_vendor_path *vendor; + struct efi_snp_device *snpdev; + struct san_device *sandev; + struct efi_block_data *block; size_t prefix_len; size_t uri_len; size_t vendor_len; size_t len; char *uri_buf; + EFI_STATUS efirc; + int rc; - /* Calculate device path lengths */ - end = efi_devpath_end ( parent ); - prefix_len = ( ( void * ) end - ( void * ) parent ); + /* Find an appropriate parent device handle */ + snpdev = last_opened_snpdev(); + if ( ! snpdev ) { + DBG ( "EFIBLK could not identify SNP device\n" ); + rc = -ENODEV; + goto err_no_snpdev; + } + + /* Calculate length of private data */ + prefix_len = efi_devpath_len ( snpdev->path ); uri_len = format_uri ( uri, NULL, 0 ); vendor_len = ( sizeof ( *vendor ) + ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); - len = ( prefix_len + vendor_len + sizeof ( *end ) ); + len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len + + vendor_len + sizeof ( *end ) ); - /* Allocate device path and space for URI buffer */ - path = zalloc ( len + uri_len + 1 /* NUL */ ); - if ( ! path ) - return NULL; - uri_buf = ( ( ( void * ) path ) + len ); + /* Allocate and initialise structure */ + sandev = alloc_sandev ( uri, len ); + if ( ! sandev ) { + rc = -ENOMEM; + goto err_alloc; + } + sandev->drive = drive; + block = sandev->priv; + block->sandev = sandev; + block->media.MediaPresent = 1; + block->media.LogicalBlocksPerPhysicalBlock = 1; + block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + block->block_io.Media = &block->media; + block->block_io.Reset = efi_block_io_reset; + block->block_io.ReadBlocks = efi_block_io_read; + block->block_io.WriteBlocks = efi_block_io_write; + block->block_io.FlushBlocks = efi_block_io_flush; + uri_buf = ( ( ( void * ) block ) + sizeof ( *block ) ); + block->path = ( ( ( void * ) uri_buf ) + uri_len + 1 /* NUL */ ); /* Construct device path */ - memcpy ( path, parent, prefix_len ); - vendor = ( ( ( void * ) path ) + prefix_len ); + memcpy ( block->path, snpdev->path, prefix_len ); + vendor = ( ( ( void * ) block->path ) + prefix_len ); vendor->vendor.Header.Type = HARDWARE_DEVICE_PATH; vendor->vendor.Header.SubType = HW_VENDOR_DP; vendor->vendor.Header.Length[0] = ( vendor_len & 0xff ); @@ -608,211 +315,21 @@ efi_block_path ( struct uri *uri, EFI_DEVICE_PATH_PROTOCOL *parent ) { end->Type = END_DEVICE_PATH_TYPE; end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; end->Length[0] = sizeof ( *end ); + DBGC ( sandev, "EFIBLK %#02x has device path %s\n", + sandev->drive, efi_devpath_text ( block->path ) ); - return path; -} - -/** - * Configure EFI block device as a CD-ROM, if applicable - * - * @v block Block device - * @v scratch Scratch data area - * @ret rc Return status code - * - * The EDK2 code will ignore CD-ROM devices with a block size other - * than 2048. While we could require the user to configure the block - * size appropriately, this is non-trivial and would impose a - * substantial learning effort on the user. Instead, we perform - * essentially the same auto-detection as under a BIOS SAN boot; if - * the ISO9660 primary volume descriptor is present then we force a - * block size of 2048 and map read/write requests appropriately. - */ -static int efi_block_parse_iso9660 ( struct efi_block *block, void *scratch ) { - static const struct iso9660_primary_descriptor_fixed primary_check = { - .type = ISO9660_TYPE_PRIMARY, - .id = ISO9660_ID, - }; - struct iso9660_primary_descriptor *primary = scratch; - struct efi_block_command_context context; - unsigned int blksize; - unsigned int blksize_shift; - int rc; - - /* Calculate required blocksize shift for potential CD-ROM access */ - blksize = block->capacity.blksize; - blksize_shift = 0; - while ( blksize < ISO9660_BLKSIZE ) { - blksize <<= 1; - blksize_shift++; + /* Register SAN device */ + if ( ( rc = register_sandev ( sandev ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x could not register: %s\n", + sandev->drive, strerror ( rc ) ); + goto err_register; } - if ( blksize > ISO9660_BLKSIZE ) { - /* Cannot be a CD-ROM */ - return 0; - } - - /* Read primary volume descriptor */ - memset ( &context, 0, sizeof ( context ) ); - context.lba = ( ISO9660_PRIMARY_LBA << blksize_shift ); - context.data = primary; - context.len = ISO9660_BLKSIZE; - context.block_rw = block_read; - if ( ( rc = efi_block_command ( block, efi_block_cmd_rw, - &context ) ) != 0 ) { - DBGC ( block, "EFIBLK %#02x could not read ISO9660 primary " - "volume descriptor: %s\n", - block->drive, strerror ( rc ) ); - return rc; - } - - /* Do nothing unless this is an ISO image */ - if ( memcmp ( primary, &primary_check, sizeof ( primary_check ) ) != 0 ) - return 0; - DBGC ( block, "EFIBLK %#02x contains an ISO9660 filesystem; treating " - "as CD-ROM\n", block->drive ); - block->blksize_shift = blksize_shift; - - return 0; -} - -/** - * Determing EFI block device capacity and block size - * - * @v block Block device - * @ret rc Return status code - */ -static int efi_block_capacity ( struct efi_block *block ) { - size_t scratch_len; - void *scratch; - int rc; - - /* Read read block capacity */ - if ( ( rc = efi_block_command ( block, efi_block_cmd_read_capacity, - NULL ) ) != 0 ) - goto err_read_capacity; - block->blksize_shift = 0; - - /* Allocate scratch area */ - scratch_len = ( block->capacity.blksize ); - if ( scratch_len < ISO9660_BLKSIZE ) - scratch_len = ISO9660_BLKSIZE; - scratch = malloc ( scratch_len ); - if ( ! scratch ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Configure as a CD-ROM, if applicable */ - if ( ( rc = efi_block_parse_iso9660 ( block, scratch ) ) != 0 ) - goto err_parse_iso9660; /* Update media descriptor */ block->media.BlockSize = - ( block->capacity.blksize << block->blksize_shift ); + ( sandev->capacity.blksize << sandev->blksize_shift ); block->media.LastBlock = - ( ( block->capacity.blocks >> block->blksize_shift ) - 1 ); - - /* Success */ - rc = 0; - - err_parse_iso9660: - free ( scratch ); - err_alloc: - err_read_capacity: - return rc; -} - -/** - * Connect all possible drivers to EFI block device - * - * @v block Block device - */ -static void efi_block_connect ( struct efi_block *block ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_STATUS efirc; - int rc; - - /* Try to connect all possible drivers to this block device */ - if ( ( efirc = bs->ConnectController ( block->handle, NULL, - NULL, 1 ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not connect drivers: %s\n", - block->drive, strerror ( rc ) ); - /* May not be an error; may already be connected */ - } - DBGC2 ( block, "EFIBLK %#02x supports protocols:\n", block->drive ); - DBGC2_EFI_PROTOCOLS ( block, block->handle ); -} - -/** - * Hook EFI block device - * - * @v uri URI - * @v drive Drive number - * @ret drive Drive number, or negative error - */ -static int efi_block_hook ( struct uri *uri, unsigned int drive ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_snp_device *snpdev; - struct efi_block *block; - EFI_STATUS efirc; - int rc; - - /* Check that drive number is not already in use */ - if ( efi_block_find ( drive ) ) { - rc = -EADDRINUSE; - goto err_in_use; - } - - /* Allocate and initialise structure */ - block = zalloc ( sizeof ( *block ) ); - if ( ! block ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &block->refcnt, efi_block_free ); - intf_init ( &block->intf, &efi_block_desc, &block->refcnt ); - block->uri = uri_get ( uri ); - block->drive = drive; - block->block_rc = -EINPROGRESS; - block->media.MediaPresent = 1; - block->media.LogicalBlocksPerPhysicalBlock = 1; - block->block_io.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; - block->block_io.Media = &block->media; - block->block_io.Reset = efi_block_io_reset; - block->block_io.ReadBlocks = efi_block_io_read; - block->block_io.WriteBlocks = efi_block_io_write; - block->block_io.FlushBlocks = efi_block_io_flush; - intf_init ( &block->command, &efi_block_cmd_desc, &block->refcnt ); - timer_init ( &block->timer, efi_block_cmd_expired, &block->refcnt ); - - /* Find an appropriate parent device handle */ - snpdev = last_opened_snpdev(); - if ( ! snpdev ) { - DBGC ( block, "EFIBLK %#02x could not identify SNP device\n", - block->drive ); - rc = -ENODEV; - goto err_no_snpdev; - } - - /* Construct device path */ - block->path = efi_block_path ( block->uri, snpdev->path ); - if ( ! block->path ) { - rc = -ENOMEM; - goto err_path; - } - DBGC ( block, "EFIBLK %#02x has device path %s\n", - block->drive, efi_devpath_text ( block->path ) ); - - /* Add to list of block devices */ - list_add ( &block->list, &efi_block_devices ); - - /* Open block device interface */ - if ( ( rc = efi_block_reopen ( block ) ) != 0 ) - goto err_reopen; - - /* Determine capacity and block size */ - if ( ( rc = efi_block_capacity ( block ) ) != 0 ) - goto err_capacity; + ( ( sandev->capacity.blocks >> sandev->blksize_shift ) - 1 ); /* Install protocols */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( @@ -821,13 +338,13 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { &efi_device_path_protocol_guid, block->path, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not install protocols: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x could not install protocols: %s\n", + sandev->drive, strerror ( rc ) ); goto err_install; } /* Connect all possible protocols */ - efi_block_connect ( block ); + efi_block_connect ( sandev ); return drive; @@ -836,16 +353,11 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { &efi_block_io_protocol_guid, &block->block_io, &efi_device_path_protocol_guid, block->path, NULL ); err_install: - err_capacity: - efi_block_restart ( block, rc ); - intf_shutdown ( &block->intf, rc ); - err_reopen: - list_del ( &block->list ); + unregister_sandev ( sandev ); + err_register: + sandev_put ( sandev ); + err_alloc: err_no_snpdev: - err_path: - ref_put ( &block->refcnt ); - err_zalloc: - err_in_use: return rc; } @@ -856,14 +368,16 @@ static int efi_block_hook ( struct uri *uri, unsigned int drive ) { */ static void efi_block_unhook ( unsigned int drive ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_block *block; + struct san_device *sandev; + struct efi_block_data *block; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); return; } + block = sandev->priv; /* Uninstall protocols */ bs->UninstallMultipleProtocolInterfaces ( @@ -871,15 +385,11 @@ static void efi_block_unhook ( unsigned int drive ) { &efi_block_io_protocol_guid, &block->block_io, &efi_device_path_protocol_guid, block->path, NULL ); - /* Close any outstanding commands and shut down interface */ - efi_block_restart ( block, 0 ); - intf_shutdown ( &block->intf, 0 ); + /* Unregister SAN device */ + unregister_sandev ( sandev ); - /* Remove from list of block devices */ - list_del ( &block->list ); - - /* Drop list's reference to drive */ - ref_put ( &block->refcnt ); + /* Drop reference to drive */ + sandev_put ( sandev ); } /** @@ -889,11 +399,11 @@ static void efi_block_unhook ( unsigned int drive ) { * @ret rc Return status code */ static int efi_block_describe ( unsigned int drive ) { - struct efi_block *block; + struct san_device *sandev; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); return -ENODEV; } @@ -904,13 +414,14 @@ static int efi_block_describe ( unsigned int drive ) { /** * Try booting from child device of EFI block device * - * @v block Block device + * @v sandev SAN device * @v handle EFI handle * @ret rc Return status code */ -static int efi_block_boot_image ( struct efi_block *block, - EFI_HANDLE handle, EFI_HANDLE *image ) { +static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, + EFI_HANDLE *image ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_block_data *block = sandev->priv; union { EFI_DEVICE_PATH_PROTOCOL *path; void *interface; @@ -930,22 +441,21 @@ static int efi_block_boot_image ( struct efi_block *block, &path.interface, efi_image_handle, handle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - DBGC ( block, "EFIBLK %#02x found filesystem with no device " - "path??", block->drive ); + DBGC ( sandev, "EFIBLK %#02x found filesystem with no device " + "path??", sandev->drive ); rc = -EEFI ( efirc ); goto err_open_device_path; } /* Check if this device is a child of our block device */ - end = efi_devpath_end ( block->path ); - prefix_len = ( ( ( void * ) end ) - ( ( void * ) block->path ) ); + prefix_len = efi_devpath_len ( block->path ); if ( memcmp ( path.path, block->path, prefix_len ) != 0 ) { /* Not a child device */ rc = -ENOTTY; goto err_not_child; } - DBGC ( block, "EFIBLK %#02x found child device %s\n", - block->drive, efi_devpath_text ( path.path ) ); + DBGC ( sandev, "EFIBLK %#02x found child device %s\n", + sandev->drive, efi_devpath_text ( path.path ) ); /* Construct device path for boot image */ end = efi_devpath_end ( path.path ); @@ -970,15 +480,15 @@ static int efi_block_boot_image ( struct efi_block *block, end->Type = END_DEVICE_PATH_TYPE; end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; end->Length[0] = sizeof ( *end ); - DBGC ( block, "EFIBLK %#02x trying to load %s\n", - block->drive, efi_devpath_text ( boot_path ) ); + DBGC ( sandev, "EFIBLK %#02x trying to load %s\n", + sandev->drive, efi_devpath_text ( boot_path ) ); /* Try loading boot image from this device */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path, NULL, 0, image ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x could not load image: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x could not load image: %s\n", + sandev->drive, strerror ( rc ) ); goto err_load_image; } @@ -1001,7 +511,7 @@ static int efi_block_boot_image ( struct efi_block *block, */ static int efi_block_boot ( unsigned int drive ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_block *block; + struct san_device *sandev; EFI_HANDLE *handles; EFI_HANDLE image = NULL; UINTN count; @@ -1009,24 +519,24 @@ static int efi_block_boot ( unsigned int drive ) { EFI_STATUS efirc; int rc; - /* Find block device */ - block = efi_block_find ( drive ); - if ( ! block ) { + /* Find SAN device */ + sandev = sandev_find ( drive ); + if ( ! sandev ) { DBG ( "EFIBLK cannot find drive %#02x\n", drive ); rc = -ENODEV; - goto err_block_find; + goto err_sandev_find; } /* Connect all possible protocols */ - efi_block_connect ( block ); + efi_block_connect ( sandev ); /* Locate all handles supporting the Simple File System protocol */ if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, &efi_simple_file_system_protocol_guid, NULL, &count, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( block, "EFIBLK %#02x cannot locate file systems: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x cannot locate file systems: %s\n", + sandev->drive, strerror ( rc ) ); goto err_locate_file_systems; } @@ -1037,21 +547,22 @@ static int efi_block_boot ( unsigned int drive ) { */ rc = -ENOENT; for ( i = 0 ; i < count ; i++ ) { - if ( ( rc = efi_block_boot_image ( block, handles[i], + if ( ( rc = efi_block_boot_image ( sandev, handles[i], &image ) ) != 0 ) continue; - DBGC ( block, "EFIBLK %#02x found boot image\n", block->drive ); + DBGC ( sandev, "EFIBLK %#02x found boot image\n", + sandev->drive ); efirc = bs->StartImage ( image, NULL, NULL ); rc = ( efirc ? -EEFI ( efirc ) : 0 ); bs->UnloadImage ( image ); - DBGC ( block, "EFIBLK %#02x boot image returned: %s\n", - block->drive, strerror ( rc ) ); + DBGC ( sandev, "EFIBLK %#02x boot image returned: %s\n", + sandev->drive, strerror ( rc ) ); break; } bs->FreePool ( handles ); err_locate_file_systems: - err_block_find: + err_sandev_find: return rc; }