summaryrefslogtreecommitdiffstats
path: root/src/drivers/block
diff options
context:
space:
mode:
authorMichael Brown2010-09-03 17:11:51 +0200
committerMichael Brown2010-09-14 21:37:15 +0200
commit220495f8bf2222e1dc1aa7db554d23997b545546 (patch)
treef04aa0d123a0b643fc41e178db81cfe63ba913bf /src/drivers/block
parent[hermon] Use correct alignment for doorbell records (diff)
downloadipxe-220495f8bf2222e1dc1aa7db554d23997b545546.tar.gz
ipxe-220495f8bf2222e1dc1aa7db554d23997b545546.tar.xz
ipxe-220495f8bf2222e1dc1aa7db554d23997b545546.zip
[block] Replace gPXE block-device API with an iPXE asynchronous interface
The block device interface used in gPXE predates the invention of even the old gPXE data-transfer interface, let alone the current iPXE generic asynchronous interface mechanism. Bring this old code up to date, with the following benefits: o Block device commands can be cancelled by the requestor. The INT 13 layer uses this to provide a global timeout on all INT 13 calls, with the result that an unexpected passive failure mode (such as an iSCSI target ACKing the request but never sending a response) will lead to a timeout that gets reported back to the INT 13 user, rather than simply freezing the system. o INT 13,00 (reset drive) is now able to reset the underlying block device. INT 13 users, such as DOS, that use INT 13,00 as a method for error recovery now have a chance of recovering. o All block device commands are tagged, with a numerical tag that will show up in debugging output and in packet captures; this will allow easier interpretation of bug reports that include both sources of information. o The extremely ugly hacks used to generate the boot firmware tables have been eradicated and replaced with a generic acpi_describe() method (exploiting the ability of iPXE interfaces to pass through methods to an underlying interface). The ACPI tables are now built in a shared data block within .bss16, rather than each requiring dedicated space in .data16. o The architecture-independent concept of a SAN device has been exposed to the iPXE core through the sanboot API, which provides calls to hook, unhook, boot, and describe SAN devices. This allows for much more flexible usage patterns (such as hooking an empty SAN device and then running an OS installer via TFTP). Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/block')
-rw-r--r--src/drivers/block/ata.c683
-rw-r--r--src/drivers/block/ibft.c471
-rw-r--r--src/drivers/block/ramdisk.c97
-rw-r--r--src/drivers/block/scsi.c878
-rw-r--r--src/drivers/block/srp.c830
5 files changed, 2242 insertions, 717 deletions
diff --git a/src/drivers/block/ata.c b/src/drivers/block/ata.c
index c59e788a..0197483b 100644
--- a/src/drivers/block/ata.c
+++ b/src/drivers/block/ata.c
@@ -19,12 +19,14 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <byteswap.h>
+#include <ipxe/list.h>
+#include <ipxe/interface.h>
#include <ipxe/blockdev.h>
-#include <ipxe/process.h>
#include <ipxe/ata.h>
/** @file
@@ -33,156 +35,625 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
-static inline __attribute__ (( always_inline )) struct ata_device *
-block_to_ata ( struct block_device *blockdev ) {
- return container_of ( blockdev, struct ata_device, blockdev );
-}
+/******************************************************************************
+ *
+ * Interface methods
+ *
+ ******************************************************************************
+ */
/**
* Issue ATA command
*
- * @v ata ATA device
+ * @v control ATA control interface
+ * @v data ATA data interface
* @v command ATA command
- * @ret rc Return status code
+ * @ret tag Command tag, or negative error
*/
-static inline __attribute__ (( always_inline )) int
-ata_command ( struct ata_device *ata, struct ata_command *command ) {
- int rc;
+int ata_command ( struct interface *control, struct interface *data,
+ struct ata_cmd *command ) {
+ struct interface *dest;
+ ata_command_TYPE ( void * ) *op =
+ intf_get_dest_op ( control, ata_command, &dest );
+ void *object = intf_object ( dest );
+ int tag;
+
+ if ( op ) {
+ tag = op ( object, data, command );
+ } else {
+ /* Default is to fail to issue the command */
+ tag = -EOPNOTSUPP;
+ }
+
+ intf_put ( dest );
+ return tag;
+}
+
+/******************************************************************************
+ *
+ * ATA devices and commands
+ *
+ ******************************************************************************
+ */
+
+/** List of all ATA commands */
+static LIST_HEAD ( ata_commands );
+
+/** An ATA device */
+struct ata_device {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Block control interface */
+ struct interface block;
+ /** ATA control interface */
+ struct interface ata;
+
+ /** Device number
+ *
+ * Must be ATA_DEV_MASTER or ATA_DEV_SLAVE.
+ */
+ unsigned int device;
+ /** Maximum number of blocks per single transfer */
+ unsigned int max_count;
+ /** Device uses LBA48 extended addressing */
+ int lba48;
+};
+
+/** An ATA command */
+struct ata_command {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** ATA device */
+ struct ata_device *atadev;
+ /** List of ATA commands */
+ struct list_head list;
+
+ /** Block data interface */
+ struct interface block;
+ /** ATA data interface */
+ struct interface ata;
+
+ /** Command type */
+ struct ata_command_type *type;
+ /** Command tag */
+ uint32_t tag;
+
+ /** Private data */
+ uint8_t priv[0];
+};
+
+/** An ATA command type */
+struct ata_command_type {
+ /** Name */
+ const char *name;
+ /** Additional working space */
+ size_t priv_len;
+ /** Command for non-LBA48-capable devices */
+ uint8_t cmd_lba;
+ /** Command for LBA48-capable devices */
+ uint8_t cmd_lba48;
+ /**
+ * Calculate data-in buffer
+ *
+ * @v atacmd ATA command
+ * @v buffer Available buffer
+ * @v len Available buffer length
+ * @ret data_in Data-in buffer
+ * @ret data_in_len Data-in buffer length
+ */
+ void ( * data_in ) ( struct ata_command *atacmd, userptr_t buffer,
+ size_t len, userptr_t *data_in,
+ size_t *data_in_len );
+ /**
+ * Calculate data-out buffer
+ *
+ *
+ * @v atacmd ATA command
+ * @v buffer Available buffer
+ * @v len Available buffer length
+ * @ret data_out Data-out buffer
+ * @ret data_out_len Data-out buffer length
+ */
+ void ( * data_out ) ( struct ata_command *atacmd, userptr_t buffer,
+ size_t len, userptr_t *data_out,
+ size_t *data_out_len );
+ /**
+ * Handle ATA command completion
+ *
+ * @v atacmd ATA command
+ * @v rc Reason for completion
+ */
+ void ( * done ) ( struct ata_command *atacmd, int rc );
+};
+
+/**
+ * Get reference to ATA device
+ *
+ * @v atadev ATA device
+ * @ret atadev ATA device
+ */
+static inline __attribute__ (( always_inline )) struct ata_device *
+atadev_get ( struct ata_device *atadev ) {
+ ref_get ( &atadev->refcnt );
+ return atadev;
+}
+
+/**
+ * Drop reference to ATA device
+ *
+ * @v atadev ATA device
+ */
+static inline __attribute__ (( always_inline )) void
+atadev_put ( struct ata_device *atadev ) {
+ ref_put ( &atadev->refcnt );
+}
+
+/**
+ * Get reference to ATA command
+ *
+ * @v atacmd ATA command
+ * @ret atacmd ATA command
+ */
+static inline __attribute__ (( always_inline )) struct ata_command *
+atacmd_get ( struct ata_command *atacmd ) {
+ ref_get ( &atacmd->refcnt );
+ return atacmd;
+}
+
+/**
+ * Drop reference to ATA command
+ *
+ * @v atacmd ATA command
+ */
+static inline __attribute__ (( always_inline )) void
+atacmd_put ( struct ata_command *atacmd ) {
+ ref_put ( &atacmd->refcnt );
+}
+
+/**
+ * Get ATA command private data
+ *
+ * @v atacmd ATA command
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+atacmd_priv ( struct ata_command *atacmd ) {
+ return atacmd->priv;
+}
+
+/**
+ * Free ATA command
+ *
+ * @v refcnt Reference count
+ */
+static void atacmd_free ( struct refcnt *refcnt ) {
+ struct ata_command *atacmd =
+ container_of ( refcnt, struct ata_command, refcnt );
+
+ /* Remove from list of commands */
+ list_del ( &atacmd->list );
+ atadev_put ( atacmd->atadev );
+
+ /* Free command */
+ free ( atacmd );
+}
+
+/**
+ * Close ATA command
+ *
+ * @v atacmd ATA command
+ * @v rc Reason for close
+ */
+static void atacmd_close ( struct ata_command *atacmd, int rc ) {
+ struct ata_device *atadev = atacmd->atadev;
+
+ if ( rc != 0 ) {
+ DBGC ( atadev, "ATA %p tag %08x closed: %s\n",
+ atadev, atacmd->tag, strerror ( rc ) );
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &atacmd->ata, rc );
+ intf_shutdown ( &atacmd->block, rc );
+}
+
+/**
+ * Handle ATA command completion
+ *
+ * @v atacmd ATA command
+ * @v rc Reason for close
+ */
+static void atacmd_done ( struct ata_command *atacmd, int rc ) {
+
+ /* Hand over to the command completion handler */
+ atacmd->type->done ( atacmd, rc );
+}
+
+/**
+ * Use provided data buffer for ATA command
+ *
+ * @v atacmd ATA command
+ * @v buffer Available buffer
+ * @v len Available buffer length
+ * @ret data Data buffer
+ * @ret data_len Data buffer length
+ */
+static void atacmd_data_buffer ( struct ata_command *atacmd __unused,
+ userptr_t buffer, size_t len,
+ userptr_t *data, size_t *data_len ) {
+ *data = buffer;
+ *data_len = len;
+}
- DBG ( "ATA cmd %02x dev %02x LBA%s %llx count %04x\n",
- command->cb.cmd_stat, command->cb.device,
- ( command->cb.lba48 ? "48" : "" ),
- ( unsigned long long ) command->cb.lba.native,
- command->cb.count.native );
+/**
+ * Use no data buffer for ATA command
+ *
+ * @v atacmd ATA command
+ * @v buffer Available buffer
+ * @v len Available buffer length
+ * @ret data Data buffer
+ * @ret data_len Data buffer length
+ */
+static void atacmd_data_none ( struct ata_command *atacmd __unused,
+ userptr_t buffer __unused, size_t len __unused,
+ userptr_t *data __unused,
+ size_t *data_len __unused ) {
+ /* Nothing to do */
+}
+
+/**
+ * Use private data buffer for ATA command
+ *
+ * @v atacmd ATA command
+ * @v buffer Available buffer
+ * @v len Available buffer length
+ * @ret data Data buffer
+ * @ret data_len Data buffer length
+ */
+static void atacmd_data_priv ( struct ata_command *atacmd,
+ userptr_t buffer __unused, size_t len __unused,
+ userptr_t *data, size_t *data_len ) {
+ *data = virt_to_user ( atacmd_priv ( atacmd ) );
+ *data_len = atacmd->type->priv_len;
+}
+
+/** ATA READ command type */
+static struct ata_command_type atacmd_read = {
+ .name = "READ",
+ .cmd_lba = ATA_CMD_READ,
+ .cmd_lba48 = ATA_CMD_READ_EXT,
+ .data_in = atacmd_data_buffer,
+ .data_out = atacmd_data_none,
+ .done = atacmd_close,
+};
+
+/** ATA WRITE command type */
+static struct ata_command_type atacmd_write = {
+ .name = "WRITE",
+ .cmd_lba = ATA_CMD_WRITE,
+ .cmd_lba48 = ATA_CMD_WRITE_EXT,
+ .data_in = atacmd_data_none,
+ .data_out = atacmd_data_buffer,
+ .done = atacmd_close,
+};
+
+/** ATA IDENTIFY private data */
+struct ata_identify_private {
+ /** Identity data */
+ struct ata_identity identity;
+};
+
+/**
+ * Return ATA model string (for debugging)
+ *
+ * @v identify ATA identity data
+ * @ret model Model string
+ */
+static const char * ata_model ( struct ata_identity *identity ) {
+ static union {
+ uint16_t words[ sizeof ( identity->model ) / 2 ];
+ char text[ sizeof ( identity->model ) + 1 /* NUL */ ];
+ } buf;
+ unsigned int i;
- /* Flag command as in-progress */
- command->rc = -EINPROGRESS;
+ for ( i = 0 ; i < ( sizeof ( identity->model ) / 2 ) ; i++ )
+ buf.words[i] = bswap_16 ( identity->model[i] );
- /* Issue ATA command */
- if ( ( rc = ata->command ( ata, command ) ) != 0 ) {
- /* Something went wrong with the issuing mechanism */
- DBG ( "ATA could not issue command: %s\n", strerror ( rc ) );
- return rc;
+ return buf.text;
+}
+
+/**
+ * Handle ATA IDENTIFY command completion
+ *
+ * @v atacmd ATA command
+ * @v rc Reason for completion
+ */
+static void atacmd_identify_done ( struct ata_command *atacmd, int rc ) {
+ struct ata_device *atadev = atacmd->atadev;
+ struct ata_identify_private *priv = atacmd_priv ( atacmd );
+ struct ata_identity *identity = &priv->identity;
+ struct block_device_capacity capacity;
+
+ /* Close if command failed */
+ if ( rc != 0 ) {
+ atacmd_close ( atacmd, rc );
+ return;
}
- /* Wait for command to complete */
- while ( command->rc == -EINPROGRESS )
- step();
- if ( ( rc = command->rc ) != 0 ) {
- /* Something went wrong with the command execution */
- DBG ( "ATA command failed: %s\n", strerror ( rc ) );
- return rc;
+ /* Extract capacity */
+ if ( identity->supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
+ atadev->lba48 = 1;
+ capacity.blocks = le64_to_cpu ( identity->lba48_sectors );
+ } else {
+ capacity.blocks = le32_to_cpu ( identity->lba_sectors );
}
+ capacity.blksize = ATA_SECTOR_SIZE;
+ capacity.max_count = atadev->max_count;
+ DBGC ( atadev, "ATA %p is a %s\n", atadev, ata_model ( identity ) );
+ DBGC ( atadev, "ATA %p has %#llx blocks (%ld MB) and uses %s\n",
+ atadev, capacity.blocks,
+ ( ( signed long ) ( capacity.blocks >> 11 ) ),
+ ( atadev->lba48 ? "LBA48" : "LBA" ) );
- return 0;
+ /* Return capacity to caller */
+ block_capacity ( &atacmd->block, &capacity );
+
+ /* Close command */
+ atacmd_close ( atacmd, 0 );
}
+/** ATA IDENTITY command type */
+static struct ata_command_type atacmd_identify = {
+ .name = "IDENTIFY",
+ .priv_len = sizeof ( struct ata_identify_private ),
+ .cmd_lba = ATA_CMD_IDENTIFY,
+ .cmd_lba48 = ATA_CMD_IDENTIFY,
+ .data_in = atacmd_data_priv,
+ .data_out = atacmd_data_none,
+ .done = atacmd_identify_done,
+};
+
+/** ATA command block interface operations */
+static struct interface_operation atacmd_block_op[] = {
+ INTF_OP ( intf_close, struct ata_command *, atacmd_close ),
+};
+
+/** ATA command block interface descriptor */
+static struct interface_descriptor atacmd_block_desc =
+ INTF_DESC_PASSTHRU ( struct ata_command, block,
+ atacmd_block_op, ata );
+
+/** ATA command ATA interface operations */
+static struct interface_operation atacmd_ata_op[] = {
+ INTF_OP ( intf_close, struct ata_command *, atacmd_done ),
+};
+
+/** ATA command ATA interface descriptor */
+static struct interface_descriptor atacmd_ata_desc =
+ INTF_DESC_PASSTHRU ( struct ata_command, ata,
+ atacmd_ata_op, block );
+
/**
- * Read block from ATA device
+ * Create ATA command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
+ * @v atadev ATA device
+ * @v block Block data interface
+ * @v type ATA command type
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
* @v buffer Data buffer
+ * @v len Length of data buffer
* @ret rc Return status code
*/
-static int ata_read ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ata_device *ata = block_to_ata ( blockdev );
- struct ata_command command;
+static int atadev_command ( struct ata_device *atadev,
+ struct interface *block,
+ struct ata_command_type *type,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ struct ata_command *atacmd;
+ struct ata_cmd command;
+ int tag;
+ int rc;
+
+ /* Allocate and initialise structure */
+ atacmd = zalloc ( sizeof ( *atacmd ) + type->priv_len );
+ if ( ! atacmd ) {
+ rc = -ENOMEM;
+ goto err_zalloc;
+ }
+ ref_init ( &atacmd->refcnt, atacmd_free );
+ intf_init ( &atacmd->block, &atacmd_block_desc, &atacmd->refcnt );
+ intf_init ( &atacmd->ata, &atacmd_ata_desc,
+ &atacmd->refcnt );
+ atacmd->atadev = atadev_get ( atadev );
+ list_add ( &atacmd->list, &ata_commands );
+ atacmd->type = type;
+ /* Sanity check */
+ if ( len != ( count * ATA_SECTOR_SIZE ) ) {
+ DBGC ( atadev, "ATA %p tag %08x buffer length mismatch (count "
+ "%d len %zd)\n", atadev, atacmd->tag, count, len );
+ rc = -EINVAL;
+ goto err_len;
+ }
+
+ /* Construct command */
memset ( &command, 0, sizeof ( command ) );
- command.cb.lba.native = block;
+ command.cb.lba.native = lba;
command.cb.count.native = count;
- command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
- command.cb.lba48 = ata->lba48;
- if ( ! ata->lba48 )
+ command.cb.device = ( atadev->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
+ command.cb.lba48 = atadev->lba48;
+ if ( ! atadev->lba48 )
command.cb.device |= command.cb.lba.bytes.low_prev;
- command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ );
- command.data_in = buffer;
- return ata_command ( ata, &command );
+ command.cb.cmd_stat =
+ ( atadev->lba48 ? type->cmd_lba48 : type->cmd_lba );
+ type->data_in ( atacmd, buffer, len,
+ &command.data_in, &command.data_in_len );
+ type->data_out ( atacmd, buffer, len,
+ &command.data_out, &command.data_out_len );
+
+ /* Issue command */
+ if ( ( tag = ata_command ( &atadev->ata, &atacmd->ata,
+ &command ) ) < 0 ) {
+ rc = tag;
+ DBGC ( atadev, "ATA %p tag %08x could not issue command: %s\n",
+ atadev, atacmd->tag, strerror ( rc ) );
+ goto err_command;
+ }
+ atacmd->tag = tag;
+
+ DBGC2 ( atadev, "ATA %p tag %08x %s cmd %02x dev %02x LBA%s %08llx "
+ "count %04x\n", atadev, atacmd->tag, atacmd->type->name,
+ command.cb.cmd_stat, command.cb.device,
+ ( command.cb.lba48 ? "48" : "" ),
+ ( unsigned long long ) command.cb.lba.native,
+ command.cb.count.native );
+
+ /* Attach to parent interface, mortalise self, and return */
+ intf_plug_plug ( &atacmd->block, block );
+ ref_put ( &atacmd->refcnt );
+ return 0;
+
+ err_command:
+ err_len:
+ atacmd_close ( atacmd, rc );
+ ref_put ( &atacmd->refcnt );
+ err_zalloc:
+ return rc;
}
/**
- * Write block to ATA device
+ * Issue ATA block read
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
+ * @v atadev ATA device
+ * @v block Block data interface
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
* @v buffer Data buffer
+ * @v len Length of data buffer
* @ret rc Return status code
+
*/
-static int ata_write ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ata_device *ata = block_to_ata ( blockdev );
- struct ata_command command;
-
- memset ( &command, 0, sizeof ( command ) );
- command.cb.lba.native = block;
- command.cb.count.native = count;
- command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
- command.cb.lba48 = ata->lba48;
- if ( ! ata->lba48 )
- command.cb.device |= command.cb.lba.bytes.low_prev;
- command.cb.cmd_stat = ( ata->lba48 ?
- ATA_CMD_WRITE_EXT : ATA_CMD_WRITE );
- command.data_out = buffer;
- return ata_command ( ata, &command );
+static int atadev_read ( struct ata_device *atadev,
+ struct interface *block,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ return atadev_command ( atadev, block, &atacmd_read,
+ lba, count, buffer, len );
}
/**
- * Identify ATA device
+ * Issue ATA block write
*
- * @v blockdev Block device
+ * @v atadev ATA device
+ * @v block Block data interface
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
+ * @v buffer Data buffer
+ * @v len Length of data buffer
* @ret rc Return status code
*/
-static int ata_identify ( struct block_device *blockdev ) {
- struct ata_device *ata = block_to_ata ( blockdev );
- struct ata_command command;
- struct ata_identity identity;
- int rc;
+static int atadev_write ( struct ata_device *atadev,
+ struct interface *block,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ return atadev_command ( atadev, block, &atacmd_write,
+ lba, count, buffer, len );
+}
- /* Issue IDENTIFY */
- memset ( &command, 0, sizeof ( command ) );
- command.cb.count.native = 1;
- command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA );
- command.cb.cmd_stat = ATA_CMD_IDENTIFY;
- command.data_in = virt_to_user ( &identity );
- linker_assert ( sizeof ( identity ) == ATA_SECTOR_SIZE,
- __ata_identity_bad_size__ );
- if ( ( rc = ata_command ( ata, &command ) ) != 0 )
- return rc;
-
- /* Fill in block device parameters */
- blockdev->blksize = ATA_SECTOR_SIZE;
- if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) {
- ata->lba48 = 1;
- blockdev->blocks = le64_to_cpu ( identity.lba48_sectors );
- } else {
- blockdev->blocks = le32_to_cpu ( identity.lba_sectors );
+/**
+ * Read ATA device capacity
+ *
+ * @v atadev ATA device
+ * @v block Block data interface
+ * @ret rc Return status code
+ */
+static int atadev_read_capacity ( struct ata_device *atadev,
+ struct interface *block ) {
+ struct ata_identity *identity;
+
+ assert ( atacmd_identify.priv_len == sizeof ( *identity ) );
+ assert ( atacmd_identify.priv_len == ATA_SECTOR_SIZE );
+ return atadev_command ( atadev, block, &atacmd_identify,
+ 0, 1, UNULL, ATA_SECTOR_SIZE );
+}
+
+/**
+ * Close ATA device
+ *
+ * @v atadev ATA device
+ * @v rc Reason for close
+ */
+static void atadev_close ( struct ata_device *atadev, int rc ) {
+ struct ata_command *atacmd;
+ struct ata_command *tmp;
+
+ /* Shut down interfaces */
+ intf_shutdown ( &atadev->block, rc );
+ intf_shutdown ( &atadev->ata, rc );
+
+ /* Shut down any remaining commands */
+ list_for_each_entry_safe ( atacmd, tmp, &ata_commands, list ) {
+ if ( atacmd->atadev != atadev )
+ continue;
+ atacmd_get ( atacmd );
+ atacmd_close ( atacmd, rc );
+ atacmd_put ( atacmd );
}
- return 0;
}
-static struct block_device_operations ata_operations = {
- .read = ata_read,
- .write = ata_write
+/** ATA device block interface operations */
+static struct interface_operation atadev_block_op[] = {
+ INTF_OP ( block_read, struct ata_device *, atadev_read ),
+ INTF_OP ( block_write, struct ata_device *, atadev_write ),
+ INTF_OP ( block_read_capacity, struct ata_device *,
+ atadev_read_capacity ),
+ INTF_OP ( intf_close, struct ata_device *, atadev_close ),
};
+/** ATA device block interface descriptor */
+static struct interface_descriptor atadev_block_desc =
+ INTF_DESC_PASSTHRU ( struct ata_device, block,
+ atadev_block_op, ata );
+
+/** ATA device ATA interface operations */
+static struct interface_operation atadev_ata_op[] = {
+ INTF_OP ( intf_close, struct ata_device *, atadev_close ),
+};
+
+/** ATA device ATA interface descriptor */
+static struct interface_descriptor atadev_ata_desc =
+ INTF_DESC_PASSTHRU ( struct ata_device, ata,
+ atadev_ata_op, block );
+
/**
- * Initialise ATA device
+ * Open ATA device
*
- * @v ata ATA device
+ * @v block Block control interface
+ * @v ata ATA control interface
+ * @v device ATA device number
+ * @v max_count Maximum number of blocks per single transfer
* @ret rc Return status code
- *
- * Initialises an ATA device. The ata_device::command field and the
- * @c ATA_FL_SLAVE portion of the ata_device::flags field must already
- * be filled in. This function will configure ata_device::blockdev,
- * including issuing an IDENTIFY DEVICE call to determine the block
- * size and total device size.
*/
-int init_atadev ( struct ata_device *ata ) {
- /** Fill in read and write methods, and get device capacity */
- ata->blockdev.op = &ata_operations;
- return ata_identify ( &ata->blockdev );
+int ata_open ( struct interface *block, struct interface *ata,
+ unsigned int device, unsigned int max_count ) {
+ struct ata_device *atadev;
+
+ /* Allocate and initialise structure */
+ atadev = zalloc ( sizeof ( *atadev ) );
+ if ( ! atadev )
+ return -ENOMEM;
+ ref_init ( &atadev->refcnt, NULL );
+ intf_init ( &atadev->block, &atadev_block_desc, &atadev->refcnt );
+ intf_init ( &atadev->ata, &atadev_ata_desc, &atadev->refcnt );
+ atadev->device = device;
+ atadev->max_count = max_count;
+
+ /* Attach to ATA and parent and interfaces, mortalise self,
+ * and return
+ */
+ intf_plug_plug ( &atadev->ata, ata );
+ intf_plug_plug ( &atadev->block, block );
+ ref_put ( &atadev->refcnt );
+ return 0;
}
diff --git a/src/drivers/block/ibft.c b/src/drivers/block/ibft.c
new file mode 100644
index 00000000..adf1d7d5
--- /dev/null
+++ b/src/drivers/block/ibft.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright Fen Systems Ltd. 2007. Portions of this code are derived
+ * from IBM Corporation Sample Programs. Copyright IBM Corporation
+ * 2004, 2007. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/acpi.h>
+#include <ipxe/in.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/iscsi.h>
+#include <ipxe/ibft.h>
+
+/** @file
+ *
+ * iSCSI boot firmware table
+ *
+ * The information in this file is derived from the document "iSCSI
+ * Boot Firmware Table (iBFT)" as published by IBM at
+ *
+ * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf
+ *
+ */
+
+/**
+ * An iBFT created by iPXE
+ *
+ */
+struct ipxe_ibft {
+ /** The fixed section */
+ struct ibft_table table;
+ /** The Initiator section */
+ struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) ));
+ /** The NIC section */
+ struct ibft_nic nic __attribute__ (( aligned ( 16 ) ));
+ /** The Target section */
+ struct ibft_target target __attribute__ (( aligned ( 16 ) ));
+ /** Strings block */
+ char strings[0];
+} __attribute__ (( packed, aligned ( 16 ) ));
+
+/**
+ * iSCSI string block descriptor
+ *
+ * This is an internal structure that we use to keep track of the
+ * allocation of string data.
+ */
+struct ibft_strings {
+ /** The iBFT containing these strings */
+ struct ibft_table *table;
+ /** Offset of first free byte within iBFT */
+ size_t offset;
+ /** Total length of the iBFT */
+ size_t len;
+};
+
+/**
+ * Fill in an IP address field within iBFT
+ *
+ * @v ipaddr IP address field
+ * @v in IPv4 address
+ */
+static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) {
+ memset ( ipaddr, 0, sizeof ( ipaddr ) );
+ if ( in.s_addr ) {
+ ipaddr->in = in;
+ ipaddr->ones = 0xffff;
+ }
+}
+
+/**
+ * Fill in an IP address within iBFT from configuration setting
+ *
+ * @v ipaddr IP address field
+ * @v setting Configuration setting
+ * @v tag DHCP option tag
+ */
+static void ibft_set_ipaddr_setting ( struct ibft_ipaddr *ipaddr,
+ struct setting *setting ) {
+ struct in_addr in;
+ fetch_ipv4_setting ( NULL, setting, &in );
+ ibft_set_ipaddr ( ipaddr, in );
+}
+
+/**
+ * Read IP address from iBFT (for debugging)
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @ret ipaddr IP address string
+ */
+static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) {
+ return inet_ntoa ( ipaddr->in );
+}
+
+/**
+ * Allocate a string within iBFT
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field to fill in
+ * @v len Length of string to allocate (excluding NUL)
+ * @ret dest String destination, or NULL
+ */
+static char * ibft_alloc_string ( struct ibft_strings *strings,
+ struct ibft_string *string, size_t len ) {
+
+ if ( ( strings->offset + len ) >= strings->len )
+ return NULL;
+
+ string->offset = cpu_to_le16 ( strings->offset );
+ string->len = cpu_to_le16 ( len );
+ strings->offset += ( len + 1 );
+
+ return ( ( ( char * ) strings->table ) + string->offset );
+}
+
+/**
+ * Fill in a string field within iBFT
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @v data String to fill in, or NULL
+ * @ret rc Return status code
+ */
+static int ibft_set_string ( struct ibft_strings *strings,
+ struct ibft_string *string, const char *data ) {
+ char *dest;
+
+ if ( ! data )
+ return 0;
+
+ dest = ibft_alloc_string ( strings, string, strlen ( data ) );
+ if ( ! dest )
+ return -ENOBUFS;
+ strcpy ( dest, data );
+
+ return 0;
+}
+
+/**
+ * Fill in a string field within iBFT from configuration setting
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @v setting Configuration setting
+ * @ret rc Return status code
+ */
+static int ibft_set_string_setting ( struct ibft_strings *strings,
+ struct ibft_string *string,
+ struct setting *setting ) {
+ int len;
+ char *dest;
+
+ len = fetch_setting_len ( NULL, setting );
+ if ( len < 0 ) {
+ string->offset = 0;
+ string->len = 0;
+ return 0;
+ }
+
+ dest = ibft_alloc_string ( strings, string, len );
+ if ( ! dest )
+ return -ENOBUFS;
+ fetch_string_setting ( NULL, setting, dest, ( len + 1 ) );
+
+ return 0;
+}
+
+/**
+ * Read string from iBFT (for debugging)
+ *
+ * @v strings iBFT string block descriptor
+ * @v string String field
+ * @ret data String content (or "<empty>")
+ */
+static const char * ibft_string ( struct ibft_strings *strings,
+ struct ibft_string *string ) {
+ return ( string->offset ?
+ ( ( ( char * ) strings->table ) + string->offset ) : NULL );
+}
+
+/**
+ * Fill in NIC portion of iBFT
+ *
+ * @v nic NIC portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int ibft_fill_nic ( struct ibft_nic *nic,
+ struct ibft_strings *strings,
+ struct net_device *netdev ) {
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ struct in_addr netmask_addr = { 0 };
+ unsigned int netmask_count = 0;
+ int rc;
+
+ /* Fill in common header */
+ nic->header.structure_id = IBFT_STRUCTURE_ID_NIC;
+ nic->header.version = 1;
+ nic->header.length = cpu_to_le16 ( sizeof ( *nic ) );
+ nic->header.flags = ( IBFT_FL_NIC_BLOCK_VALID |
+ IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED );
+
+ /* Extract values from configuration settings */
+ ibft_set_ipaddr_setting ( &nic->ip_address, &ip_setting );
+ DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) );
+ ibft_set_ipaddr_setting ( &nic->gateway, &gateway_setting );
+ DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) );
+ ibft_set_ipaddr_setting ( &nic->dns[0], &dns_setting );
+ DBG ( "iBFT NIC DNS = %s\n", ibft_ipaddr ( &nic->dns[0] ) );
+ if ( ( rc = ibft_set_string_setting ( strings, &nic->hostname,
+ &hostname_setting ) ) != 0 )
+ return rc;
+ DBG ( "iBFT NIC hostname = %s\n",
+ ibft_string ( strings, &nic->hostname ) );
+
+ /* Derive subnet mask prefix from subnet mask */
+ fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr );
+ while ( netmask_addr.s_addr ) {
+ if ( netmask_addr.s_addr & 0x1 )
+ netmask_count++;
+ netmask_addr.s_addr >>= 1;
+ }
+ nic->subnet_mask_prefix = netmask_count;
+ DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix );
+
+ /* Extract values from net-device configuration */
+ if ( ( rc = ll_protocol->eth_addr ( netdev->ll_addr,
+ nic->mac_address ) ) != 0 ) {
+ DBG ( "Could not determine iBFT MAC: %s\n", strerror ( rc ) );
+ return rc;
+ }
+ DBG ( "iBFT NIC MAC = %s\n", eth_ntoa ( nic->mac_address ) );
+ nic->pci_bus_dev_func = cpu_to_le16 ( netdev->dev->desc.location );
+ DBG ( "iBFT NIC PCI = %04x\n", le16_to_cpu ( nic->pci_bus_dev_func ) );
+
+ return 0;
+}
+
+/**
+ * Fill in Initiator portion of iBFT
+ *
+ * @v initiator Initiator portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @ret rc Return status code
+ */
+static int ibft_fill_initiator ( struct ibft_initiator *initiator,
+ struct ibft_strings *strings ) {
+ const char *initiator_iqn = iscsi_initiator_iqn();
+ int rc;
+
+ /* Fill in common header */
+ initiator->header.structure_id = IBFT_STRUCTURE_ID_INITIATOR;
+ initiator->header.version = 1;
+ initiator->header.length = cpu_to_le16 ( sizeof ( *initiator ) );
+ initiator->header.flags = ( IBFT_FL_INITIATOR_BLOCK_VALID |
+ IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED );
+
+ /* Fill in hostname */
+ if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name,
+ initiator_iqn ) ) != 0 )
+ return rc;
+ DBG ( "iBFT initiator hostname = %s\n",
+ ibft_string ( strings, &initiator->initiator_name ) );
+
+ return 0;
+}
+
+/**
+ * Fill in Target CHAP portion of iBFT
+ *
+ * @v target Target portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int ibft_fill_target_chap ( struct ibft_target *target,
+ struct ibft_strings *strings,
+ struct iscsi_session *iscsi ) {
+ int rc;
+
+ if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_FORWARD_REQUIRED ) )
+ return 0;
+
+ assert ( iscsi->initiator_username );
+ assert ( iscsi->initiator_password );
+
+ target->chap_type = IBFT_CHAP_ONE_WAY;
+ if ( ( rc = ibft_set_string ( strings, &target->chap_name,
+ iscsi->initiator_username ) ) != 0 )
+ return rc;
+ DBG ( "iBFT target username = %s\n",
+ ibft_string ( strings, &target->chap_name ) );
+ if ( ( rc = ibft_set_string ( strings, &target->chap_secret,
+ iscsi->initiator_password ) ) != 0 )
+ return rc;
+ DBG ( "iBFT target password = <redacted>\n" );
+
+ return 0;
+}
+
+/**
+ * Fill in Target Reverse CHAP portion of iBFT
+ *
+ * @v target Target portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int ibft_fill_target_reverse_chap ( struct ibft_target *target,
+ struct ibft_strings *strings,
+ struct iscsi_session *iscsi ) {
+ int rc;
+
+ if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) )
+ return 0;
+
+ assert ( iscsi->initiator_username );
+ assert ( iscsi->initiator_password );
+ assert ( iscsi->target_username );
+ assert ( iscsi->target_password );
+
+ target->chap_type = IBFT_CHAP_MUTUAL;
+ if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name,
+ iscsi->target_username ) ) != 0 )
+ return rc;
+ DBG ( "iBFT target reverse username = %s\n",
+ ibft_string ( strings, &target->chap_name ) );
+ if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret,
+ iscsi->target_password ) ) != 0 )
+ return rc;
+ DBG ( "iBFT target reverse password = <redacted>\n" );
+
+ return 0;
+}
+
+/**
+ * Fill in Target portion of iBFT
+ *
+ * @v target Target portion of iBFT
+ * @v strings iBFT string block descriptor
+ * @v iscsi iSCSI session
+ * @ret rc Return status code
+ */
+static int ibft_fill_target ( struct ibft_target *target,
+ struct ibft_strings *strings,
+ struct iscsi_session *iscsi ) {
+ struct sockaddr_in *sin_target =
+ ( struct sockaddr_in * ) &iscsi->target_sockaddr;
+ int rc;
+
+ /* Fill in common header */
+ target->header.structure_id = IBFT_STRUCTURE_ID_TARGET;
+ target->header.version = 1;
+ target->header.length = cpu_to_le16 ( sizeof ( *target ) );
+ target->header.flags = ( IBFT_FL_TARGET_BLOCK_VALID |
+ IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED );
+
+ /* Fill in Target values */
+ ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr );
+ DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) );
+ target->socket = cpu_to_le16 ( ntohs ( sin_target->sin_port ) );
+ DBG ( "iBFT target port = %d\n", target->socket );
+ memcpy ( &target->boot_lun, &iscsi->lun, sizeof ( target->boot_lun ) );
+ DBG ( "iBFT target boot LUN = " SCSI_LUN_FORMAT "\n",
+ SCSI_LUN_DATA ( target->boot_lun ) );
+ if ( ( rc = ibft_set_string ( strings, &target->target_name,
+ iscsi->target_iqn ) ) != 0 )
+ return rc;
+ DBG ( "iBFT target name = %s\n",
+ ibft_string ( strings, &target->target_name ) );
+ if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 )
+ return rc;
+ if ( ( rc = ibft_fill_target_reverse_chap ( target, strings,
+ iscsi ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Fill in iBFT
+ *
+ * @v iscsi iSCSI session
+ * @v acpi ACPI table
+ * @v len Length of ACPI table
+ * @ret rc Return status code
+ */
+int ibft_describe ( struct iscsi_session *iscsi,
+ struct acpi_description_header *acpi,
+ size_t len ) {
+ struct ipxe_ibft *ibft =
+ container_of ( acpi, struct ipxe_ibft, table.acpi );
+ struct ibft_strings strings = {
+ .table = &ibft->table,
+ .offset = offsetof ( typeof ( *ibft ), strings ),
+ .len = len,
+ };
+ struct net_device *netdev;
+ int rc;
+
+ /* Ugly hack. Now that we have a generic interface mechanism
+ * that can support ioctls, we can potentially eliminate this.
+ */
+ netdev = last_opened_netdev();
+ if ( ! netdev ) {
+ DBGC ( iscsi, "iSCSI %p cannot guess network device\n",
+ iscsi );
+ return -ENODEV;
+ }
+
+ /* Fill in ACPI header */
+ ibft->table.acpi.signature = cpu_to_le32 ( IBFT_SIG );
+ ibft->table.acpi.length = cpu_to_le32 ( len );
+ ibft->table.acpi.revision = 1;
+
+ /* Fill in Control block */
+ ibft->table.control.header.structure_id = IBFT_STRUCTURE_ID_CONTROL;
+ ibft->table.control.header.version = 1;
+ ibft->table.control.header.length =
+ cpu_to_le16 ( sizeof ( ibft->table.control ) );
+ ibft->table.control.initiator =
+ cpu_to_le16 ( offsetof ( typeof ( *ibft ), initiator ) );
+ ibft->table.control.nic_0 =
+ cpu_to_le16 ( offsetof ( typeof ( *ibft ), nic ) );
+ ibft->table.control.target_0 =
+ cpu_to_le16 ( offsetof ( typeof ( *ibft ), target ) );
+
+ /* Fill in NIC, Initiator and Target blocks */
+ if ( ( rc = ibft_fill_nic ( &ibft->nic, &strings, netdev ) ) != 0 )
+ return rc;
+ if ( ( rc = ibft_fill_initiator ( &ibft->initiator,
+ &strings ) ) != 0 )
+ return rc;
+ if ( ( rc = ibft_fill_target ( &ibft->target, &strings,
+ iscsi ) ) != 0 )
+ return rc;
+
+ return 0;
+}
diff --git a/src/drivers/block/ramdisk.c b/src/drivers/block/ramdisk.c
deleted file mode 100644
index 55c0d2d5..00000000
--- a/src/drivers/block/ramdisk.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <ipxe/blockdev.h>
-#include <ipxe/ramdisk.h>
-
-/**
- * @file
- *
- * RAM disks
- *
- */
-
-static inline __attribute__ (( always_inline )) struct ramdisk *
-block_to_ramdisk ( struct block_device *blockdev ) {
- return container_of ( blockdev, struct ramdisk, blockdev );
-}
-
-/**
- * Read block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
-static int ramdisk_read ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
- unsigned long offset = ( block * blockdev->blksize );
- unsigned long length = ( count * blockdev->blksize );
-
- DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n",
- ramdisk, offset, length );
-
- memcpy_user ( buffer, 0, ramdisk->data, offset, length );
- return 0;
-}
-
-/**
- * Write block
- *
- * @v blockdev Block device
- * @v block Block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
- */
-static int ramdisk_write ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
- unsigned long offset = ( block * blockdev->blksize );
- unsigned long length = ( count * blockdev->blksize );
-
- DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n",
- ramdisk, offset, length );
-
- memcpy_user ( ramdisk->data, offset, buffer, 0, length );
- return 0;
-}
-
-static struct block_device_operations ramdisk_operations = {
- .read = ramdisk_read,
- .write = ramdisk_write
-};
-
-int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
- unsigned int blksize ) {
-
- if ( ! blksize )
- blksize = 512;
-
- ramdisk->data = data;
- ramdisk->blockdev.op = &ramdisk_operations;
- ramdisk->blockdev.blksize = blksize;
- ramdisk->blockdev.blocks = ( len / blksize );
-
- return 0;
-}
diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c
index 94e283b6..d7f7b4db 100644
--- a/src/drivers/block/scsi.c
+++ b/src/drivers/block/scsi.c
@@ -23,8 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <byteswap.h>
#include <errno.h>
+#include <ipxe/list.h>
#include <ipxe/blockdev.h>
-#include <ipxe/process.h>
#include <ipxe/scsi.h>
/** @file
@@ -33,334 +33,738 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
-/** Maximum number of dummy "read capacity (10)" operations
+/** Maximum number of command retries */
+#define SCSICMD_MAX_RETRIES 10
+
+/******************************************************************************
+ *
+ * Utility functions
*
- * These are issued at connection setup to draw out various useless
- * power-on messages.
+ ******************************************************************************
*/
-#define SCSI_MAX_DUMMY_READ_CAP 10
-
-static inline __attribute__ (( always_inline )) struct scsi_device *
-block_to_scsi ( struct block_device *blockdev ) {
- return container_of ( blockdev, struct scsi_device, blockdev );
-}
/**
- * Handle SCSI command with no backing device
+ * Parse SCSI LUN
*
- * @v scsi SCSI device
- * @v command SCSI command
+ * @v lun_string LUN string representation
+ * @v lun LUN to fill in
* @ret rc Return status code
*/
-int scsi_detached_command ( struct scsi_device *scsi __unused,
- struct scsi_command *command __unused ) {
- return -ENODEV;
+int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
+ char *p;
+ int i;
+
+ memset ( lun, 0, sizeof ( *lun ) );
+ if ( lun_string ) {
+ p = ( char * ) lun_string;
+ for ( i = 0 ; i < 4 ; i++ ) {
+ lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
+ if ( *p == '\0' )
+ break;
+ if ( *p != '-' )
+ return -EINVAL;
+ p++;
+ }
+ if ( *p )
+ return -EINVAL;
+ }
+
+ return 0;
}
+/******************************************************************************
+ *
+ * Interface methods
+ *
+ ******************************************************************************
+ */
+
/**
* Issue SCSI command
*
- * @v scsi SCSI device
+ * @v control SCSI control interface
+ * @v data SCSI data interface
* @v command SCSI command
- * @ret rc Return status code
+ * @ret tag Command tag, or negative error
*/
-static int scsi_command ( struct scsi_device *scsi,
- struct scsi_command *command ) {
- int rc;
+int scsi_command ( struct interface *control, struct interface *data,
+ struct scsi_cmd *command ) {
+ struct interface *dest;
+ scsi_command_TYPE ( void * ) *op =
+ intf_get_dest_op ( control, scsi_command, &dest );
+ void *object = intf_object ( dest );
+ int tap;
+
+ if ( op ) {
+ tap = op ( object, data, command );
+ } else {
+ /* Default is to fail to issue the command */
+ tap = -EOPNOTSUPP;
+ }
- DBGC2 ( scsi, "SCSI %p " SCSI_CDB_FORMAT "\n",
- scsi, SCSI_CDB_DATA ( command->cdb ) );
+ intf_put ( dest );
+ return tap;
+}
- /* Clear sense response code before issuing command */
- command->sense_response = 0;
+/**
+ * Report SCSI response
+ *
+ * @v interface SCSI command interface
+ * @v response SCSI response
+ */
+void scsi_response ( struct interface *intf, struct scsi_rsp *response ) {
+ struct interface *dest;
+ scsi_response_TYPE ( void * ) *op =
+ intf_get_dest_op ( intf, scsi_response, &dest );
+ void *object = intf_object ( dest );
+
+ if ( op ) {
+ op ( object, response );
+ } else {
+ /* Default is to ignore the response */
+ }
- /* Flag command as in-progress */
- command->rc = -EINPROGRESS;
+ intf_put ( dest );
+}
- /* Issue SCSI command */
- if ( ( rc = scsi->command ( scsi, command ) ) != 0 ) {
- /* Something went wrong with the issuing mechanism */
- DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n",
- scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) );
- return rc;
- }
+/******************************************************************************
+ *
+ * SCSI devices and commands
+ *
+ ******************************************************************************
+ */
- /* Wait for command to complete */
- while ( command->rc == -EINPROGRESS )
- step();
- if ( ( rc = command->rc ) != 0 ) {
- /* Something went wrong with the command execution */
- DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " err %s\n",
- scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) );
- return rc;
- }
+/** A SCSI device */
+struct scsi_device {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Block control interface */
+ struct interface block;
+ /** SCSI control interface */
+ struct interface scsi;
- /* Check for SCSI errors */
- if ( command->status != 0 ) {
- DBGC ( scsi, "SCSI %p " SCSI_CDB_FORMAT " status %02x sense "
- "%02x\n", scsi, SCSI_CDB_DATA ( command->cdb ),
- command->status, command->sense_response );
- return -EIO;
- }
+ /** SCSI LUN */
+ struct scsi_lun lun;
- return 0;
+ /** List of commands */
+ struct list_head cmds;
+};
+
+/** A SCSI command */
+struct scsi_command {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** SCSI device */
+ struct scsi_device *scsidev;
+ /** List of SCSI commands */
+ struct list_head list;
+
+ /** Block data interface */
+ struct interface block;
+ /** SCSI data interface */
+ struct interface scsi;
+
+ /** Command type */
+ struct scsi_command_type *type;
+ /** Starting logical block address */
+ uint64_t lba;
+ /** Number of blocks */
+ unsigned int count;
+ /** Data buffer */
+ userptr_t buffer;
+ /** Length of data buffer */
+ size_t len;
+ /** Command tag */
+ uint32_t tag;
+
+ /** Retry count */
+ unsigned int retries;
+
+ /** Private data */
+ uint8_t priv[0];
+};
+
+/** A SCSI command type */
+struct scsi_command_type {
+ /** Name */
+ const char *name;
+ /** Additional working space */
+ size_t priv_len;
+ /**
+ * Construct SCSI command IU
+ *
+ * @v scsicmd SCSI command
+ * @v command SCSI command IU
+ */
+ void ( * cmd ) ( struct scsi_command *scsicmd,
+ struct scsi_cmd *command );
+ /**
+ * Handle SCSI command completion
+ *
+ * @v scsicmd SCSI command
+ * @v rc Reason for completion
+ */
+ void ( * done ) ( struct scsi_command *scsicmd, int rc );
+};
+
+/**
+ * Get reference to SCSI device
+ *
+ * @v scsidev SCSI device
+ * @ret scsidev SCSI device
+ */
+static inline __attribute__ (( always_inline )) struct scsi_device *
+scsidev_get ( struct scsi_device *scsidev ) {
+ ref_get ( &scsidev->refcnt );
+ return scsidev;
}
/**
- * Read block from SCSI device using READ (10)
+ * Drop reference to SCSI device
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsidev SCSI device
*/
-static int scsi_read_10 ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_read_10 *cdb = &command.cdb.read10;
+static inline __attribute__ (( always_inline )) void
+scsidev_put ( struct scsi_device *scsidev ) {
+ ref_put ( &scsidev->refcnt );
+}
- /* Issue READ (10) */
- memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_READ_10;
- cdb->lba = cpu_to_be32 ( block );
- cdb->len = cpu_to_be16 ( count );
- command.data_in = buffer;
- command.data_in_len = ( count * blockdev->blksize );
- return scsi_command ( scsi, &command );
+/**
+ * Get reference to SCSI command
+ *
+ * @v scsicmd SCSI command
+ * @ret scsicmd SCSI command
+ */
+static inline __attribute__ (( always_inline )) struct scsi_command *
+scsicmd_get ( struct scsi_command *scsicmd ) {
+ ref_get ( &scsicmd->refcnt );
+ return scsicmd;
}
/**
- * Read block from SCSI device using READ (16)
+ * Drop reference to SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsicmd SCSI command
*/
-static int scsi_read_16 ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_read_16 *cdb = &command.cdb.read16;
+static inline __attribute__ (( always_inline )) void
+scsicmd_put ( struct scsi_command *scsicmd ) {
+ ref_put ( &scsicmd->refcnt );
+}
- /* Issue READ (16) */
- memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_READ_16;
- cdb->lba = cpu_to_be64 ( block );
- cdb->len = cpu_to_be32 ( count );
- command.data_in = buffer;
- command.data_in_len = ( count * blockdev->blksize );
- return scsi_command ( scsi, &command );
+/**
+ * Get SCSI command private data
+ *
+ * @v scsicmd SCSI command
+ * @ret priv Private data
+ */
+static inline __attribute__ (( always_inline )) void *
+scsicmd_priv ( struct scsi_command *scsicmd ) {
+ return scsicmd->priv;
}
/**
- * Write block to SCSI device using WRITE (10)
+ * Free SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v refcnt Reference count
*/
-static int scsi_write_10 ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_write_10 *cdb = &command.cdb.write10;
+static void scsicmd_free ( struct refcnt *refcnt ) {
+ struct scsi_command *scsicmd =
+ container_of ( refcnt, struct scsi_command, refcnt );
- /* Issue WRITE (10) */
- memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_WRITE_10;
- cdb->lba = cpu_to_be32 ( block );
- cdb->len = cpu_to_be16 ( count );
- command.data_out = buffer;
- command.data_out_len = ( count * blockdev->blksize );
- return scsi_command ( scsi, &command );
+ /* Remove from list of commands */
+ list_del ( &scsicmd->list );
+ scsidev_put ( scsicmd->scsidev );
+
+ /* Free command */
+ free ( scsicmd );
}
/**
- * Write block to SCSI device using WRITE (16)
+ * Close SCSI command
*
- * @v blockdev Block device
- * @v block LBA block number
- * @v count Block count
- * @v buffer Data buffer
- * @ret rc Return status code
+ * @v scsicmd SCSI command
+ * @v rc Reason for close
*/
-static int scsi_write_16 ( struct block_device *blockdev, uint64_t block,
- unsigned long count, userptr_t buffer ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_write_16 *cdb = &command.cdb.write16;
+static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
- /* Issue WRITE (16) */
- memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_WRITE_16;
- cdb->lba = cpu_to_be64 ( block );
- cdb->len = cpu_to_be32 ( count );
- command.data_out = buffer;
- command.data_out_len = ( count * blockdev->blksize );
- return scsi_command ( scsi, &command );
+ if ( rc != 0 ) {
+ DBGC ( scsidev, "SCSI %p tag %08x closed: %s\n",
+ scsidev, scsicmd->tag, strerror ( rc ) );
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &scsicmd->scsi, rc );
+ intf_shutdown ( &scsicmd->block, rc );
}
/**
- * Read capacity of SCSI device via READ CAPACITY (10)
+ * Construct and issue SCSI command
*
- * @v blockdev Block device
* @ret rc Return status code
*/
-static int scsi_read_capacity_10 ( struct block_device *blockdev ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_read_capacity_10 *cdb = &command.cdb.readcap10;
- struct scsi_capacity_10 capacity;
+static int scsicmd_command ( struct scsi_command *scsicmd ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
+ struct scsi_cmd command;
+ int tag;
int rc;
- /* Issue READ CAPACITY (10) */
+ /* Construct command */
memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_READ_CAPACITY_10;
- command.data_in = virt_to_user ( &capacity );
- command.data_in_len = sizeof ( capacity );
-
- if ( ( rc = scsi_command ( scsi, &command ) ) != 0 )
+ memcpy ( &command.lun, &scsidev->lun, sizeof ( command.lun ) );
+ scsicmd->type->cmd ( scsicmd, &command );
+
+ /* Issue command */
+ if ( ( tag = scsi_command ( &scsidev->scsi, &scsicmd->scsi,
+ &command ) ) < 0 ) {
+ rc = tag;
+ DBGC ( scsidev, "SCSI %p could not issue command: %s\n",
+ scsidev, strerror ( rc ) );
return rc;
+ }
- /* Fill in block device fields */
- blockdev->blksize = be32_to_cpu ( capacity.blksize );
- blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 );
+ /* Record tag */
+ if ( scsicmd->tag ) {
+ DBGC ( scsidev, "SCSI %p tag %08x is now tag %08x\n",
+ scsidev, scsicmd->tag, tag );
+ }
+ scsicmd->tag = tag;
+ DBGC2 ( scsidev, "SCSI %p tag %08x %s " SCSI_CDB_FORMAT "\n",
+ scsidev, scsicmd->tag, scsicmd->type->name,
+ SCSI_CDB_DATA ( command.cdb ) );
return 0;
}
/**
- * Read capacity of SCSI device via READ CAPACITY (16)
+ * Handle SCSI command completion
*
- * @v blockdev Block device
- * @ret rc Return status code
+ * @v scsicmd SCSI command
+ * @v rc Reason for close
*/
-static int scsi_read_capacity_16 ( struct block_device *blockdev ) {
- struct scsi_device *scsi = block_to_scsi ( blockdev );
- struct scsi_command command;
- struct scsi_cdb_read_capacity_16 *cdb = &command.cdb.readcap16;
- struct scsi_capacity_16 capacity;
- int rc;
+static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
- /* Issue READ CAPACITY (16) */
- memset ( &command, 0, sizeof ( command ) );
- cdb->opcode = SCSI_OPCODE_SERVICE_ACTION_IN;
- cdb->service_action = SCSI_SERVICE_ACTION_READ_CAPACITY_16;
- cdb->len = cpu_to_be32 ( sizeof ( capacity ) );
- command.data_in = virt_to_user ( &capacity );
- command.data_in_len = sizeof ( capacity );
+ /* Restart SCSI interface */
+ intf_restart ( &scsicmd->scsi, rc );
- if ( ( rc = scsi_command ( scsi, &command ) ) != 0 )
- return rc;
+ /* SCSI targets have an annoying habit of returning occasional
+ * pointless "error" messages such as "power-on occurred", so
+ * we have to be prepared to retry commands.
+ */
+ if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) {
+ /* Retry command */
+ DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n",
+ scsidev, scsicmd->tag, strerror ( rc ) );
+ DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n",
+ scsidev, scsicmd->tag, scsicmd->retries );
+ if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 )
+ return;
+ }
- /* Fill in block device fields */
- blockdev->blksize = be32_to_cpu ( capacity.blksize );
- blockdev->blocks = ( be64_to_cpu ( capacity.lba ) + 1 );
- return 0;
+ /* If we didn't (successfully) reissue the command, hand over
+ * to the command completion handler.
+ */
+ scsicmd->type->done ( scsicmd, rc );
}
-static struct block_device_operations scsi_operations_16 = {
- .read = scsi_read_16,
- .write = scsi_write_16,
-};
+/**
+ * Handle SCSI response
+ *
+ * @v scsicmd SCSI command
+ * @v response SCSI response
+ */
+static void scsicmd_response ( struct scsi_command *scsicmd,
+ struct scsi_rsp *response ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
+ size_t overrun;
+ size_t underrun;
+
+ if ( response->status == 0 ) {
+ scsicmd_done ( scsicmd, 0 );
+ } else {
+ DBGC ( scsidev, "SCSI %p tag %08x status %02x",
+ scsidev, scsicmd->tag, response->status );
+ if ( response->overrun > 0 ) {
+ overrun = response->overrun;
+ DBGC ( scsidev, " overrun +%zd", overrun );
+ } else if ( response->overrun < 0 ) {
+ underrun = -(response->overrun);
+ DBGC ( scsidev, " underrun -%zd", underrun );
+ }
+ DBGC ( scsidev, " sense %02x:%02x:%08x\n",
+ response->sense.code, response->sense.key,
+ ntohl ( response->sense.info ) );
+ scsicmd_done ( scsicmd, -EIO );
+ }
+}
-static struct block_device_operations scsi_operations_10 = {
- .read = scsi_read_10,
- .write = scsi_write_10,
+/**
+ * Construct SCSI READ command
+ *
+ * @v scsicmd SCSI command
+ * @v command SCSI command IU
+ */
+static void scsicmd_read_cmd ( struct scsi_command *scsicmd,
+ struct scsi_cmd *command ) {
+
+ if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) {
+ /* Use READ (16) */
+ command->cdb.read16.opcode = SCSI_OPCODE_READ_16;
+ command->cdb.read16.lba = cpu_to_be64 ( scsicmd->lba );
+ command->cdb.read16.len = cpu_to_be32 ( scsicmd->count );
+ } else {
+ /* Use READ (10) */
+ command->cdb.read10.opcode = SCSI_OPCODE_READ_10;
+ command->cdb.read10.lba = cpu_to_be32 ( scsicmd->lba );
+ command->cdb.read10.len = cpu_to_be16 ( scsicmd->count );
+ }
+ command->data_in = scsicmd->buffer;
+ command->data_in_len = scsicmd->len;
+}
+
+/** SCSI READ command type */
+static struct scsi_command_type scsicmd_read = {
+ .name = "READ",
+ .cmd = scsicmd_read_cmd,
+ .done = scsicmd_close,
};
/**
- * Initialise SCSI device
+ * Construct SCSI WRITE command
*
- * @v scsi SCSI device
- * @ret rc Return status code
- *
- * Initialises a SCSI device. The scsi_device::command and
- * scsi_device::lun fields must already be filled in. This function
- * will configure scsi_device::blockdev, including issuing a READ
- * CAPACITY call to determine the block size and total device size.
+ * @v scsicmd SCSI command
+ * @v command SCSI command IU
*/
-int init_scsidev ( struct scsi_device *scsi ) {
- unsigned int i;
- int rc;
+static void scsicmd_write_cmd ( struct scsi_command *scsicmd,
+ struct scsi_cmd *command ) {
+
+ if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) {
+ /* Use WRITE (16) */
+ command->cdb.write16.opcode = SCSI_OPCODE_WRITE_16;
+ command->cdb.write16.lba = cpu_to_be64 ( scsicmd->lba );
+ command->cdb.write16.len = cpu_to_be32 ( scsicmd->count );
+ } else {
+ /* Use WRITE (10) */
+ command->cdb.write10.opcode = SCSI_OPCODE_WRITE_10;
+ command->cdb.write10.lba = cpu_to_be32 ( scsicmd->lba );
+ command->cdb.write10.len = cpu_to_be16 ( scsicmd->count );
+ }
+ command->data_out = scsicmd->buffer;
+ command->data_out_len = scsicmd->len;
+}
- /* Issue some theoretically extraneous READ CAPACITY (10)
- * commands, solely in order to draw out the "CHECK CONDITION
- * (power-on occurred)", "CHECK CONDITION (reported LUNs data
- * has changed)" etc. that some dumb targets insist on sending
- * as an error at start of day. The precise command that we
- * use is unimportant; we just need to provide the target with
- * an opportunity to send its responses.
- */
- for ( i = 0 ; i < SCSI_MAX_DUMMY_READ_CAP ; i++ ) {
- if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) == 0 )
- break;
- DBGC ( scsi, "SCSI %p ignoring start-of-day error (#%d)\n",
- scsi, ( i + 1 ) );
+/** SCSI WRITE command type */
+static struct scsi_command_type scsicmd_write = {
+ .name = "WRITE",
+ .cmd = scsicmd_write_cmd,
+ .done = scsicmd_close,
+};
+
+/** SCSI READ CAPACITY private data */
+struct scsi_read_capacity_private {
+ /** Use READ CAPACITY (16) */
+ int use16;
+ /** Data buffer for READ CAPACITY commands */
+ union {
+ /** Data buffer for READ CAPACITY (10) */
+ struct scsi_capacity_10 capacity10;
+ /** Data buffer for READ CAPACITY (16) */
+ struct scsi_capacity_16 capacity16;
+ } capacity;
+};
+
+/**
+ * Construct SCSI READ CAPACITY command
+ *
+ * @v scsicmd SCSI command
+ * @v command SCSI command IU
+ */
+static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
+ struct scsi_cmd *command ) {
+ struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
+ struct scsi_cdb_read_capacity_16 *readcap16 = &command->cdb.readcap16;
+ struct scsi_cdb_read_capacity_10 *readcap10 = &command->cdb.readcap10;
+ struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
+ struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
+
+ if ( priv->use16 ) {
+ /* Use READ CAPACITY (16) */
+ readcap16->opcode = SCSI_OPCODE_SERVICE_ACTION_IN;
+ readcap16->service_action =
+ SCSI_SERVICE_ACTION_READ_CAPACITY_16;
+ readcap16->len = cpu_to_be32 ( sizeof ( *capacity16 ) );
+ command->data_in = virt_to_user ( capacity16 );
+ command->data_in_len = sizeof ( *capacity16 );
+ } else {
+ /* Use READ CAPACITY (10) */
+ readcap10->opcode = SCSI_OPCODE_READ_CAPACITY_10;
+ command->data_in = virt_to_user ( capacity10 );
+ command->data_in_len = sizeof ( *capacity10 );
}
+}
- /* Try READ CAPACITY (10), which is a mandatory command, first. */
- scsi->blockdev.op = &scsi_operations_10;
- if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 ) {
- DBGC ( scsi, "SCSI %p could not READ CAPACITY (10): %s\n",
- scsi, strerror ( rc ) );
- return rc;
+/**
+ * Handle SCSI READ CAPACITY command completion
+ *
+ * @v scsicmd SCSI command
+ * @v rc Reason for completion
+ */
+static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
+ int rc ) {
+ struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
+ struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
+ struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
+ struct block_device_capacity capacity;
+
+ /* Close if command failed */
+ if ( rc != 0 ) {
+ scsicmd_close ( scsicmd, rc );
+ return;
}
- /* If capacity range was exceeded (i.e. capacity.lba was
- * 0xffffffff, meaning that blockdev->blocks is now zero), use
- * READ CAPACITY (16) instead. READ CAPACITY (16) is not
- * mandatory, so we can't just use it straight off.
- */
- if ( scsi->blockdev.blocks == 0 ) {
- scsi->blockdev.op = &scsi_operations_16;
- if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 ){
- DBGC ( scsi, "SCSI %p could not READ CAPACITY (16): "
- "%s\n", scsi, strerror ( rc ) );
- return rc;
+ /* Extract capacity */
+ if ( priv->use16 ) {
+ capacity.blocks = ( be64_to_cpu ( capacity16->lba ) + 1 );
+ capacity.blksize = be32_to_cpu ( capacity16->blksize );
+ } else {
+ capacity.blocks = ( be32_to_cpu ( capacity10->lba ) + 1 );
+ capacity.blksize = be32_to_cpu ( capacity10->blksize );
+
+ /* If capacity range was exceeded (i.e. capacity.lba
+ * was 0xffffffff, meaning that blockdev->blocks is
+ * now zero), use READ CAPACITY (16) instead. READ
+ * CAPACITY (16) is not mandatory, so we can't just
+ * use it straight off.
+ */
+ if ( capacity.blocks == 0 ) {
+ priv->use16 = 1;
+ if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 ) {
+ scsicmd_close ( scsicmd, rc );
+ return;
+ }
+ return;
}
}
+ capacity.max_count = -1U;
+
+ /* Return capacity to caller */
+ block_capacity ( &scsicmd->block, &capacity );
+
+ /* Close command */
+ scsicmd_close ( scsicmd, 0 );
+}
+
+/** SCSI READ CAPACITY command type */
+static struct scsi_command_type scsicmd_read_capacity = {
+ .name = "READ CAPACITY",
+ .priv_len = sizeof ( struct scsi_read_capacity_private ),
+ .cmd = scsicmd_read_capacity_cmd,
+ .done = scsicmd_read_capacity_done,
+};
+
+/** SCSI command block interface operations */
+static struct interface_operation scsicmd_block_op[] = {
+ INTF_OP ( intf_close, struct scsi_command *, scsicmd_close ),
+};
+
+/** SCSI command block interface descriptor */
+static struct interface_descriptor scsicmd_block_desc =
+ INTF_DESC_PASSTHRU ( struct scsi_command, block,
+ scsicmd_block_op, scsi );
+
+/** SCSI command SCSI interface operations */
+static struct interface_operation scsicmd_scsi_op[] = {
+ INTF_OP ( intf_close, struct scsi_command *, scsicmd_done ),
+ INTF_OP ( scsi_response, struct scsi_command *, scsicmd_response ),
+};
- DBGC ( scsi, "SCSI %p using READ/WRITE (%d) commands\n", scsi,
- ( ( scsi->blockdev.op == &scsi_operations_10 ) ? 10 : 16 ) );
- DBGC ( scsi, "SCSI %p capacity is %ld MB (%#llx blocks)\n", scsi,
- ( ( unsigned long ) ( scsi->blockdev.blocks >> 11 ) ),
- scsi->blockdev.blocks );
+/** SCSI command SCSI interface descriptor */
+static struct interface_descriptor scsicmd_scsi_desc =
+ INTF_DESC_PASSTHRU ( struct scsi_command, scsi,
+ scsicmd_scsi_op, block );
+/**
+ * Create SCSI command
+ *
+ * @v scsidev SCSI device
+ * @v block Block data interface
+ * @v type SCSI command type
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
+ * @v buffer Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+static int scsidev_command ( struct scsi_device *scsidev,
+ struct interface *block,
+ struct scsi_command_type *type,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ struct scsi_command *scsicmd;
+ int rc;
+
+ /* Allocate and initialise structure */
+ scsicmd = zalloc ( sizeof ( *scsicmd ) + type->priv_len );
+ if ( ! scsicmd ) {
+ rc = -ENOMEM;
+ goto err_zalloc;
+ }
+ ref_init ( &scsicmd->refcnt, scsicmd_free );
+ intf_init ( &scsicmd->block, &scsicmd_block_desc, &scsicmd->refcnt );
+ intf_init ( &scsicmd->scsi, &scsicmd_scsi_desc,
+ &scsicmd->refcnt );
+ scsicmd->scsidev = scsidev_get ( scsidev );
+ list_add ( &scsicmd->list, &scsidev->cmds );
+ scsicmd->type = type;
+ scsicmd->lba = lba;
+ scsicmd->count = count;
+ scsicmd->buffer = buffer;
+ scsicmd->len = len;
+
+ /* Issue SCSI command */
+ if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 )
+ goto err_command;
+
+ /* Attach to parent interface, mortalise self, and return */
+ intf_plug_plug ( &scsicmd->block, block );
+ ref_put ( &scsicmd->refcnt );
return 0;
+
+ err_command:
+ scsicmd_close ( scsicmd, rc );
+ ref_put ( &scsicmd->refcnt );
+ err_zalloc:
+ return rc;
}
/**
- * Parse SCSI LUN
+ * Issue SCSI block read
*
- * @v lun_string LUN string representation
- * @v lun LUN to fill in
+ * @v scsidev SCSI device
+ * @v block Block data interface
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
+ * @v buffer Data buffer
+ * @v len Length of data buffer
* @ret rc Return status code
+
*/
-int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
- char *p;
- int i;
+static int scsidev_read ( struct scsi_device *scsidev,
+ struct interface *block,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ return scsidev_command ( scsidev, block, &scsicmd_read,
+ lba, count, buffer, len );
+}
- memset ( lun, 0, sizeof ( *lun ) );
- if ( lun_string ) {
- p = ( char * ) lun_string;
- for ( i = 0 ; i < 4 ; i++ ) {
- lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
- if ( *p == '\0' )
- break;
- if ( *p != '-' )
- return -EINVAL;
- p++;
- }
- if ( *p )
- return -EINVAL;
+/**
+ * Issue SCSI block write
+ *
+ * @v scsidev SCSI device
+ * @v block Block data interface
+ * @v lba Starting logical block address
+ * @v count Number of blocks to transfer
+ * @v buffer Data buffer
+ * @v len Length of data buffer
+ * @ret rc Return status code
+ */
+static int scsidev_write ( struct scsi_device *scsidev,
+ struct interface *block,
+ uint64_t lba, unsigned int count,
+ userptr_t buffer, size_t len ) {
+ return scsidev_command ( scsidev, block, &scsicmd_write,
+ lba, count, buffer, len );
+}
+
+/**
+ * Read SCSI device capacity
+ *
+ * @v scsidev SCSI device
+ * @v block Block data interface
+ * @ret rc Return status code
+ */
+static int scsidev_read_capacity ( struct scsi_device *scsidev,
+ struct interface *block ) {
+ return scsidev_command ( scsidev, block, &scsicmd_read_capacity,
+ 0, 0, UNULL, 0 );
+}
+
+/**
+ * Close SCSI device
+ *
+ * @v scsidev SCSI device
+ * @v rc Reason for close
+ */
+static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
+ struct scsi_command *scsicmd;
+ struct scsi_command *tmp;
+
+ /* Shut down interfaces */
+ intf_shutdown ( &scsidev->block, rc );
+ intf_shutdown ( &scsidev->scsi, rc );
+
+ /* Shut down any remaining commands */
+ list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
+ scsicmd_get ( scsicmd );
+ scsicmd_close ( scsicmd, rc );
+ scsicmd_put ( scsicmd );
}
+}
+
+/** SCSI device block interface operations */
+static struct interface_operation scsidev_block_op[] = {
+ INTF_OP ( block_read, struct scsi_device *, scsidev_read ),
+ INTF_OP ( block_write, struct scsi_device *, scsidev_write ),
+ INTF_OP ( block_read_capacity, struct scsi_device *,
+ scsidev_read_capacity ),
+ INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
+};
+
+/** SCSI device block interface descriptor */
+static struct interface_descriptor scsidev_block_desc =
+ INTF_DESC_PASSTHRU ( struct scsi_device, block,
+ scsidev_block_op, scsi );
+
+/** SCSI device SCSI interface operations */
+static struct interface_operation scsidev_scsi_op[] = {
+ INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
+};
+/** SCSI device SCSI interface descriptor */
+static struct interface_descriptor scsidev_scsi_desc =
+ INTF_DESC_PASSTHRU ( struct scsi_device, scsi,
+ scsidev_scsi_op, block );
+
+/**
+ * Open SCSI device
+ *
+ * @v block Block control interface
+ * @v scsi SCSI control interface
+ * @v lun SCSI LUN
+ * @ret rc Return status code
+ */
+int scsi_open ( struct interface *block, struct interface *scsi,
+ struct scsi_lun *lun ) {
+ struct scsi_device *scsidev;
+
+ /* Allocate and initialise structure */
+ scsidev = zalloc ( sizeof ( *scsidev ) );
+ if ( ! scsidev )
+ return -ENOMEM;
+ ref_init ( &scsidev->refcnt, NULL );
+ intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt );
+ intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt );
+ INIT_LIST_HEAD ( &scsidev->cmds );
+ memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) );
+ DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n",
+ scsidev, SCSI_LUN_DATA ( scsidev->lun ) );
+
+ /* Attach to SCSI and parent and interfaces, mortalise self,
+ * and return
+ */
+ intf_plug_plug ( &scsidev->scsi, scsi );
+ intf_plug_plug ( &scsidev->block, block );
+ ref_put ( &scsidev->refcnt );
return 0;
}
diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c
index a8deab15..4b592e95 100644
--- a/src/drivers/block/srp.c
+++ b/src/drivers/block/srp.c
@@ -36,7 +36,6 @@ FILE_LICENCE ( BSD2 );
#include <ipxe/scsi.h>
#include <ipxe/xfer.h>
#include <ipxe/features.h>
-#include <ipxe/ib_srp.h>
#include <ipxe/srp.h>
/**
@@ -48,334 +47,555 @@ FILE_LICENCE ( BSD2 );
FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
-/** Tag to be used for next SRP IU */
-static unsigned int srp_tag = 0;
+/** Maximum length of any initiator-to-target IU that we will send
+ *
+ * The longest IU is a SRP_CMD with no additional CDB and two direct
+ * data buffer descriptors, which comes to 80 bytes.
+ */
+#define SRP_MAX_I_T_IU_LEN 80
+
+/** An SRP device */
+struct srp_device {
+ /** Reference count */
+ struct refcnt refcnt;
+
+ /** SCSI command issuing interface */
+ struct interface scsi;
+ /** Underlying data transfer interface */
+ struct interface socket;
+
+ /** RDMA memory handle */
+ uint32_t memory_handle;
+ /** Login completed successfully */
+ int logged_in;
+
+ /** Initiator port ID (for boot firmware table) */
+ union srp_port_id initiator;
+ /** Target port ID (for boot firmware table) */
+ union srp_port_id target;
+ /** SCSI LUN (for boot firmware table) */
+ struct scsi_lun lun;
+
+ /** List of active commands */
+ struct list_head commands;
+};
+
+/** An SRP command */
+struct srp_command {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** SRP device */
+ struct srp_device *srpdev;
+ /** List of active commands */
+ struct list_head list;
+
+ /** SCSI command interface */
+ struct interface scsi;
+ /** Command tag */
+ uint32_t tag;
+};
+
+/**
+ * Get reference to SRP device
+ *
+ * @v srpdev SRP device
+ * @ret srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) struct srp_device *
+srpdev_get ( struct srp_device *srpdev ) {
+ ref_get ( &srpdev->refcnt );
+ return srpdev;
+}
-static void srp_login ( struct srp_device *srp );
-static void srp_cmd ( struct srp_device *srp );
+/**
+ * Drop reference to SRP device
+ *
+ * @v srpdev SRP device
+ */
+static inline __attribute__ (( always_inline )) void
+srpdev_put ( struct srp_device *srpdev ) {
+ ref_put ( &srpdev->refcnt );
+}
+
+/**
+ * Get reference to SRP command
+ *
+ * @v srpcmd SRP command
+ * @ret srpcmd SRP command
+ */
+static inline __attribute__ (( always_inline )) struct srp_command *
+srpcmd_get ( struct srp_command *srpcmd ) {
+ ref_get ( &srpcmd->refcnt );
+ return srpcmd;
+}
/**
- * Mark SRP SCSI command as complete
+ * Drop reference to SRP command
*
- * @v srp SRP device
- * @v rc Status code
+ * @v srpcmd SRP command
*/
-static void srp_scsi_done ( struct srp_device *srp, int rc ) {
- if ( srp->command )
- srp->command->rc = rc;
- srp->command = NULL;
+static inline __attribute__ (( always_inline )) void
+srpcmd_put ( struct srp_command *srpcmd ) {
+ ref_put ( &srpcmd->refcnt );
}
/**
- * Handle SRP session failure
+ * Free SRP command
*
- * @v srp SRP device
- * @v rc Reason for failure
+ * @v refcnt Reference count
*/
-static void srp_fail ( struct srp_device *srp, int rc ) {
+static void srpcmd_free ( struct refcnt *refcnt ) {
+ struct srp_command *srpcmd =
+ container_of ( refcnt, struct srp_command, refcnt );
- /* Close underlying socket */
- intf_restart ( &srp->socket, rc );
+ assert ( list_empty ( &srpcmd->list ) );
- /* Clear session state */
- srp->state = 0;
+ srpdev_put ( srpcmd->srpdev );
+ free ( srpcmd );
+}
- /* If we have reached the retry limit, report the failure */
- if ( srp->retry_count >= SRP_MAX_RETRIES ) {
- srp_scsi_done ( srp, rc );
- return;
+/**
+ * Close SRP command
+ *
+ * @v srpcmd SRP command
+ * @v rc Reason for close
+ */
+static void srpcmd_close ( struct srp_command *srpcmd, int rc ) {
+ struct srp_device *srpdev = srpcmd->srpdev;
+
+ if ( rc != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x closed: %s\n",
+ srpdev, srpcmd->tag, strerror ( rc ) );
}
- /* Otherwise, increment the retry count and try to reopen the
- * connection
- */
- srp->retry_count++;
- srp_login ( srp );
+ /* Remove from list of commands */
+ if ( ! list_empty ( &srpcmd->list ) ) {
+ list_del ( &srpcmd->list );
+ INIT_LIST_HEAD ( &srpcmd->list );
+ srpcmd_put ( srpcmd );
+ }
+
+ /* Shut down interfaces */
+ intf_shutdown ( &srpcmd->scsi, rc );
}
/**
- * Initiate SRP login
+ * Close SRP device
*
- * @v srp SRP device
+ * @v srpdev SRP device
+ * @v rc Reason for close
*/
-static void srp_login ( struct srp_device *srp ) {
- struct io_buffer *iobuf;
- struct srp_login_req *login_req;
- int rc;
+static void srpdev_close ( struct srp_device *srpdev, int rc ) {
+ struct srp_command *srpcmd;
+ struct srp_command *tmp;
- assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
+ if ( rc != 0 ) {
+ DBGC ( srpdev, "SRP %p closed: %s\n",
+ srpdev, strerror ( rc ) );
+ }
- /* Open underlying socket */
- if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not open socket: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ /* Shut down interfaces */
+ intf_shutdown ( &srpdev->socket, rc );
+ intf_shutdown ( &srpdev->scsi, rc );
+
+ /* Shut down any active commands */
+ list_for_each_entry_safe ( srpcmd, tmp, &srpdev->commands, list ) {
+ srpcmd_get ( srpcmd );
+ srpcmd_close ( srpcmd, rc );
+ srpcmd_put ( srpcmd );
}
- srp->state |= SRP_STATE_SOCKET_OPEN;
+}
- /* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err;
+/**
+ * Identify SRP command by tag
+ *
+ * @v srpdev SRP device
+ * @v tag Command tag
+ * @ret srpcmd SRP command, or NULL
+ */
+static struct srp_command * srp_find_tag ( struct srp_device *srpdev,
+ uint32_t tag ) {
+ struct srp_command *srpcmd;
+
+ list_for_each_entry ( srpcmd, &srpdev->commands, list ) {
+ if ( srpcmd->tag == tag )
+ return srpcmd;
+ }
+ return NULL;
+}
+
+/**
+ * Choose an SRP command tag
+ *
+ * @v srpdev SRP device
+ * @ret tag New tag, or negative error
+ */
+static int srp_new_tag ( struct srp_device *srpdev ) {
+ static uint16_t tag_idx;
+ unsigned int i;
+
+ for ( i = 0 ; i < 65536 ; i++ ) {
+ tag_idx++;
+ if ( srp_find_tag ( srpdev, tag_idx ) == NULL )
+ return tag_idx;
}
+ return -EADDRINUSE;
+}
+
+/**
+ * Transmit SRP login request
+ *
+ * @v srpdev SRP device
+ * @v initiator Initiator port ID
+ * @v target Target port ID
+ * @v tag Command tag
+ * @ret rc Return status code
+ */
+static int srp_login ( struct srp_device *srpdev, union srp_port_id *initiator,
+ union srp_port_id *target, uint32_t tag ) {
+ struct io_buffer *iobuf;
+ struct srp_login_req *login_req;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &srpdev->socket, sizeof ( *login_req ) );
+ if ( ! iobuf )
+ return -ENOMEM;
/* Construct login request IU */
login_req = iob_put ( iobuf, sizeof ( *login_req ) );
memset ( login_req, 0, sizeof ( *login_req ) );
login_req->type = SRP_LOGIN_REQ;
- login_req->tag.dwords[1] = htonl ( ++srp_tag );
+ login_req->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
+ login_req->tag.dwords[1] = htonl ( tag );
login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
- memcpy ( &login_req->port_ids, &srp->port_ids,
- sizeof ( login_req->port_ids ) );
+ memcpy ( &login_req->initiator, initiator,
+ sizeof ( login_req->initiator ) );
+ memcpy ( &login_req->target, target, sizeof ( login_req->target ) );
- DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
- srp, ntohl ( login_req->tag.dwords[0] ),
- ntohl ( login_req->tag.dwords[1] ) );
- DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_REQ:\n", srpdev, tag );
+ DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
/* Send login request IU */
- if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not send login request: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x could not send LOGIN_REQ: "
+ "%s\n", srpdev, tag, strerror ( rc ) );
+ return rc;
}
- return;
-
- err:
- srp_fail ( srp, rc );
+ return 0;
}
/**
- * Handle SRP login response
+ * Receive SRP login response
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Return status code
*/
-static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_login_rsp *login_rsp = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
- srp, ntohl ( login_rsp->tag.dwords[0] ),
- ntohl ( login_rsp->tag.dwords[1] ) );
+static int srp_login_rsp ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_login_rsp *login_rsp = data;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
- DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
- srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *login_rsp ) ) {
+ DBGC ( srpdev, "SRP %p LOGIN_RSP too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
-
- DBGC ( srp, "SRP %p logged in\n", srp );
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_RSP:\n",
+ srpdev, ntohl ( login_rsp->tag.dwords[1] ) );
+ DBGC_HDA ( srpdev, 0, data, len );
/* Mark as logged in */
- srp->state |= SRP_STATE_LOGGED_IN;
+ srpdev->logged_in = 1;
+ DBGC ( srpdev, "SRP %p logged in\n", srpdev );
- /* Reset error counter */
- srp->retry_count = 0;
+ /* Notify of window change */
+ xfer_window_changed ( &srpdev->scsi );
- /* Issue pending command */
- srp_cmd ( srp );
-
- rc = 0;
- out:
- free_iob ( iobuf );
- return rc;
+ return 0;
}
/**
- * Handle SRP login rejection
+ * Receive SRP login rejection
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Return status code
*/
-static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_login_rej *login_rej = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
- srp, ntohl ( login_rej->tag.dwords[0] ),
- ntohl ( login_rej->tag.dwords[1] ) );
+static int srp_login_rej ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_login_rej *login_rej = data;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
- DBGC ( srp, "SRP %p RX login rejection too short (%zd "
- "bytes)\n", srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *login_rej ) ) {
+ DBGC ( srpdev, "SRP %p LOGIN_REJ too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
+ DBGC ( srpdev, "SRP %p tag %08x LOGIN_REJ:\n",
+ srpdev, ntohl ( login_rej->tag.dwords[1] ) );
+ DBGC_HDA ( srpdev, 0, data, len );
/* Login rejection always indicates an error */
- DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
- srp, ntohl ( login_rej->reason ) );
- rc = -EPERM;
-
- out:
- free_iob ( iobuf );
- return rc;
+ DBGC ( srpdev, "SRP %p login rejected (reason %08x)\n",
+ srpdev, ntohl ( login_rej->reason ) );
+ return -EPERM;
}
/**
* Transmit SRP SCSI command
*
- * @v srp SRP device
+ * @v srpdev SRP device
+ * @v command SCSI command
+ * @v tag Command tag
+ * @ret rc Return status code
*/
-static void srp_cmd ( struct srp_device *srp ) {
+static int srp_cmd ( struct srp_device *srpdev,
+ struct scsi_cmd *command,
+ uint32_t tag ) {
struct io_buffer *iobuf;
struct srp_cmd *cmd;
struct srp_memory_descriptor *data_out;
struct srp_memory_descriptor *data_in;
int rc;
- assert ( srp->state & SRP_STATE_LOGGED_IN );
+ /* Sanity check */
+ if ( ! srpdev->logged_in ) {
+ DBGC ( srpdev, "SRP %p tag %08x cannot send CMD before "
+ "login completes\n", srpdev, tag );
+ return -EBUSY;
+ }
/* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err;
- }
+ iobuf = xfer_alloc_iob ( &srpdev->socket, SRP_MAX_I_T_IU_LEN );
+ if ( ! iobuf )
+ return -ENOMEM;
/* Construct base portion */
cmd = iob_put ( iobuf, sizeof ( *cmd ) );
memset ( cmd, 0, sizeof ( *cmd ) );
cmd->type = SRP_CMD;
- cmd->tag.dwords[1] = htonl ( ++srp_tag );
- cmd->lun = srp->lun;
- memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
+ cmd->tag.dwords[0] = htonl ( SRP_TAG_MAGIC );
+ cmd->tag.dwords[1] = htonl ( tag );
+ memcpy ( &cmd->lun, &command->lun, sizeof ( cmd->lun ) );
+ memcpy ( &cmd->cdb, &command->cdb, sizeof ( cmd->cdb ) );
/* Construct data-out descriptor, if present */
- if ( srp->command->data_out ) {
+ if ( command->data_out ) {
cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
data_out = iob_put ( iobuf, sizeof ( *data_out ) );
data_out->address =
- cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
- data_out->handle = ntohl ( srp->memory_handle );
- data_out->len = ntohl ( srp->command->data_out_len );
+ cpu_to_be64 ( user_to_phys ( command->data_out, 0 ) );
+ data_out->handle = ntohl ( srpdev->memory_handle );
+ data_out->len = ntohl ( command->data_out_len );
}
/* Construct data-in descriptor, if present */
- if ( srp->command->data_in ) {
+ if ( command->data_in ) {
cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
data_in = iob_put ( iobuf, sizeof ( *data_in ) );
data_in->address =
- cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
- data_in->handle = ntohl ( srp->memory_handle );
- data_in->len = ntohl ( srp->command->data_in_len );
+ cpu_to_be64 ( user_to_phys ( command->data_in, 0 ) );
+ data_in->handle = ntohl ( srpdev->memory_handle );
+ data_in->len = ntohl ( command->data_in_len );
}
- DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
- ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
- DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+ DBGC2 ( srpdev, "SRP %p tag %08x CMD " SCSI_CDB_FORMAT "\n",
+ srpdev, tag, SCSI_CDB_DATA ( cmd->cdb ) );
/* Send IU */
- if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not send command: %s\n",
- srp, strerror ( rc ) );
- goto err;
+ if ( ( rc = xfer_deliver_iob ( &srpdev->socket, iobuf ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p tag %08x could not send CMD: %s\n",
+ srpdev, tag, strerror ( rc ) );
+ return rc;
}
- return;
-
- err:
- srp_fail ( srp, rc );
+ return 0;
}
/**
- * Handle SRP SCSI response
+ * Receive SRP SCSI response
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Returns status code
*/
-static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
- struct srp_rsp *rsp = iobuf->data;
- int rc;
-
- DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
- ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
+static int srp_rsp ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_rsp *rsp = data;
+ struct srp_command *srpcmd;
+ struct scsi_rsp response;
+ const void *sense;
+ ssize_t data_out_residual_count;
+ ssize_t data_in_residual_count;
/* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
- DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
- srp, iob_len ( iobuf ) );
- rc = -EINVAL;
- goto out;
+ if ( len < sizeof ( *rsp ) ) {
+ DBGC ( srpdev, "SRP %p RSP too short (%zd bytes)\n",
+ srpdev, len );
+ return -EINVAL;
}
-
- /* Report SCSI errors */
- if ( rsp->status != 0 ) {
- DBGC ( srp, "SRP %p response status %02x\n",
- srp, rsp->status );
- if ( srp_rsp_sense_data ( rsp ) ) {
- DBGC ( srp, "SRP %p sense data:\n", srp );
- DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
- srp_rsp_sense_data_len ( rsp ) );
- }
+ DBGC2 ( srpdev, "SRP %p tag %08x RSP stat %02x dores %08x dires "
+ "%08x valid %02x%s%s%s%s%s%s\n",
+ srpdev, ntohl ( rsp->tag.dwords[1] ), rsp->status,
+ ntohl ( rsp->data_out_residual_count ),
+ ntohl ( rsp->data_in_residual_count ), rsp->valid,
+ ( ( rsp->valid & SRP_RSP_VALID_DIUNDER ) ? " diunder" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DIOVER ) ? " diover" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DOUNDER ) ? " dounder" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_DOOVER ) ? " doover" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ? " sns" : "" ),
+ ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ? " rsp" : "" ) );
+
+ /* Identify command by tag */
+ srpcmd = srp_find_tag ( srpdev, ntohl ( rsp->tag.dwords[1] ) );
+ if ( ! srpcmd ) {
+ DBGC ( srpdev, "SRP %p tag %08x unrecognised RSP\n",
+ srpdev, ntohl ( rsp->tag.dwords[1] ) );
+ return -ENOENT;
}
- if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
- DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
- srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
- ? "under" : "over" ),
- ntohl ( rsp->data_out_residual_count ) );
- }
- if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
- DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
- srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
- ? "under" : "over" ),
- ntohl ( rsp->data_in_residual_count ) );
+
+ /* Hold command reference for remainder of function */
+ srpcmd_get ( srpcmd );
+
+ /* Build SCSI response */
+ memset ( &response, 0, sizeof ( response ) );
+ response.status = rsp->status;
+ data_out_residual_count = ntohl ( rsp->data_out_residual_count );
+ data_in_residual_count = ntohl ( rsp->data_in_residual_count );
+ if ( rsp->valid & SRP_RSP_VALID_DOOVER ) {
+ response.overrun = data_out_residual_count;
+ } else if ( rsp->valid & SRP_RSP_VALID_DOUNDER ) {
+ response.overrun = -(data_out_residual_count);
+ } else if ( rsp->valid & SRP_RSP_VALID_DIOVER ) {
+ response.overrun = data_in_residual_count;
+ } else if ( rsp->valid & SRP_RSP_VALID_DIUNDER ) {
+ response.overrun = -(data_in_residual_count);
}
- srp->command->status = rsp->status;
+ sense = srp_rsp_sense_data ( rsp );
+ if ( sense )
+ memcpy ( &response.sense, sense, sizeof ( response.sense ) );
- /* Mark SCSI command as complete */
- srp_scsi_done ( srp, 0 );
+ /* Report SCSI response */
+ scsi_response ( &srpcmd->scsi, &response );
- rc = 0;
- out:
- free_iob ( iobuf );
- return rc;
+ /* Close SCSI command */
+ srpcmd_close ( srpcmd, 0 );
+
+ /* Drop temporary command reference */
+ srpcmd_put ( srpcmd );
+
+ return 0;
}
/**
- * Handle SRP unrecognised response
+ * Receive SRP unrecognised response IU
*
- * @v srp SRP device
- * @v iobuf I/O buffer
+ * @v srpdev SRP device
+ * @v data SRP IU
+ * @v len Length of SRP IU
* @ret rc Returns status code
*/
-static int srp_unrecognised ( struct srp_device *srp,
- struct io_buffer *iobuf ) {
- struct srp_common *common = iobuf->data;
+static int srp_unrecognised ( struct srp_device *srpdev,
+ const void *data, size_t len ) {
+ const struct srp_common *common = data;
- DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
- srp, ntohl ( common->tag.dwords[0] ),
- ntohl ( common->tag.dwords[1] ), common->type );
+ DBGC ( srpdev, "SRP %p tag %08x unrecognised IU type %02x:\n",
+ srpdev, ntohl ( common->tag.dwords[1] ), common->type );
+ DBGC_HDA ( srpdev, 0, data, len );
- free_iob ( iobuf );
return -ENOTSUP;
}
+/** SRP command SCSI interface operations */
+static struct interface_operation srpcmd_scsi_op[] = {
+ INTF_OP ( intf_close, struct srp_command *, srpcmd_close ),
+};
+
+/** SRP command SCSI interface descriptor */
+static struct interface_descriptor srpcmd_scsi_desc =
+ INTF_DESC ( struct srp_command, scsi, srpcmd_scsi_op );
+
+/**
+ * Issue SRP SCSI command
+ *
+ * @v srpdev SRP device
+ * @v parent Parent interface
+ * @v command SCSI command
+ * @ret tag Command tag, or negative error
+ */
+static int srpdev_scsi_command ( struct srp_device *srpdev,
+ struct interface *parent,
+ struct scsi_cmd *command ) {
+ struct srp_command *srpcmd;
+ int tag;
+ int rc;
+
+ /* Allocate command tag */
+ tag = srp_new_tag ( srpdev );
+ if ( tag < 0 ) {
+ rc = tag;
+ goto err_tag;
+ }
+
+ /* Allocate and initialise structure */
+ srpcmd = zalloc ( sizeof ( *srpcmd ) );
+ if ( ! srpcmd ) {
+ rc = -ENOMEM;
+ goto err_zalloc;
+ }
+ ref_init ( &srpcmd->refcnt, srpcmd_free );
+ intf_init ( &srpcmd->scsi, &srpcmd_scsi_desc, &srpcmd->refcnt );
+ srpcmd->srpdev = srpdev_get ( srpdev );
+ list_add ( &srpcmd->list, &srpdev->commands );
+ srpcmd->tag = tag;
+
+ /* Send command IU */
+ if ( ( rc = srp_cmd ( srpdev, command, srpcmd->tag ) ) != 0 )
+ goto err_cmd;
+
+ /* Attach to parent interface, leave reference with command
+ * list, and return.
+ */
+ intf_plug_plug ( &srpcmd->scsi, parent );
+ return srpcmd->tag;
+
+ err_cmd:
+ srpcmd_close ( srpcmd, rc );
+ err_zalloc:
+ err_tag:
+ return rc;
+}
+
/**
- * Receive data from underlying socket
+ * Receive data from SRP socket
*
- * @v srp SRP device
+ * @v srpdev SRP device
* @v iobuf Datagram I/O buffer
* @v meta Data transfer metadata
* @ret rc Return status code
*/
-static int srp_xfer_deliver ( struct srp_device *srp,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
+static int srpdev_deliver ( struct srp_device *srpdev,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
struct srp_common *common = iobuf->data;
- int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
+ int ( * type ) ( struct srp_device *srp, const void *data, size_t len );
int rc;
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *common ) ) {
+ DBGC ( srpdev, "SRP %p IU too short (%zd bytes)\n",
+ srpdev, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto err;
+ }
+
/* Determine IU type */
switch ( common->type ) {
case SRP_LOGIN_RSP:
@@ -393,114 +613,170 @@ static int srp_xfer_deliver ( struct srp_device *srp,
}
/* Handle IU */
- if ( ( rc = type ( srp, iobuf ) ) != 0 )
+ if ( ( rc = type ( srpdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 )
goto err;
+ free_iob ( iobuf );
return 0;
err:
- srp_fail ( srp, rc );
+ DBGC ( srpdev, "SRP %p closing due to received IU (%s):\n",
+ srpdev, strerror ( rc ) );
+ DBGC_HDA ( srpdev, 0, iobuf->data, iob_len ( iobuf ) );
+ free_iob ( iobuf );
+ srpdev_close ( srpdev, rc );
return rc;
}
-/** SRP data transfer interface operations */
-static struct interface_operation srp_xfer_operations[] = {
- INTF_OP ( xfer_deliver, struct srp_device *, srp_xfer_deliver ),
- INTF_OP ( intf_close, struct srp_device *, srp_fail ),
-};
+/**
+ * Check SRP device flow-control window
+ *
+ * @v srpdev SRP device
+ * @ret len Length of window
+ */
+static size_t srpdev_window ( struct srp_device *srpdev ) {
+ return ( srpdev->logged_in ? ~( ( size_t ) 0 ) : 0 );
+}
-/** SRP data transfer interface descriptor */
-static struct interface_descriptor srp_xfer_desc =
- INTF_DESC ( struct srp_device, socket, srp_xfer_operations );
+/**
+ * A (transport-independent) sBFT created by iPXE
+ */
+struct ipxe_sbft {
+ /** The table header */
+ struct sbft_table table;
+ /** The SCSI subtable */
+ struct sbft_scsi_subtable scsi;
+ /** The SRP subtable */
+ struct sbft_srp_subtable srp;
+} __attribute__ (( packed, aligned ( 16 ) ));
/**
- * Issue SCSI command via SRP
+ * Describe SRP device in an ACPI table
*
- * @v scsi SCSI device
- * @v command SCSI command
+ * @v srpdev SRP device
+ * @v acpi ACPI table
+ * @v len Length of ACPI table
* @ret rc Return status code
*/
-static int srp_command ( struct scsi_device *scsi,
- struct scsi_command *command ) {
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
-
- /* Store SCSI command */
- if ( srp->command ) {
- DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
- srp );
- return -EBUSY;
- }
- srp->command = command;
-
- /* Log in or issue command as appropriate */
- if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
- srp_login ( srp );
- } else if ( srp->state & SRP_STATE_LOGGED_IN ) {
- srp_cmd ( srp );
- } else {
- /* Still waiting for login; do nothing */
+static int srpdev_describe ( struct srp_device *srpdev,
+ struct acpi_description_header *acpi,
+ size_t len ) {
+ struct ipxe_sbft *sbft =
+ container_of ( acpi, struct ipxe_sbft, table.acpi );
+ int rc;
+
+ /* Sanity check */
+ if ( len < sizeof ( *sbft ) )
+ return -ENOBUFS;
+
+ /* Populate table */
+ sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG );
+ sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) );
+ sbft->table.acpi.revision = 1;
+ sbft->table.scsi_offset =
+ cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) );
+ memcpy ( &sbft->scsi.lun, &srpdev->lun, sizeof ( sbft->scsi.lun ) );
+ sbft->table.srp_offset =
+ cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) );
+ memcpy ( &sbft->srp.initiator, &srpdev->initiator,
+ sizeof ( sbft->srp.initiator ) );
+ memcpy ( &sbft->srp.target, &srpdev->target,
+ sizeof ( sbft->srp.target ) );
+
+ /* Ask transport layer to describe transport-specific portions */
+ if ( ( rc = acpi_describe ( &srpdev->socket, acpi, len ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p cannot describe transport layer: %s\n",
+ srpdev, strerror ( rc ) );
+ return rc;
}
return 0;
}
+/** SRP device socket interface operations */
+static struct interface_operation srpdev_socket_op[] = {
+ INTF_OP ( xfer_deliver, struct srp_device *, srpdev_deliver ),
+ INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
+};
+
+/** SRP device socket interface descriptor */
+static struct interface_descriptor srpdev_socket_desc =
+ INTF_DESC ( struct srp_device, socket, srpdev_socket_op );
+
+/** SRP device SCSI interface operations */
+static struct interface_operation srpdev_scsi_op[] = {
+ INTF_OP ( scsi_command, struct srp_device *, srpdev_scsi_command ),
+ INTF_OP ( xfer_window, struct srp_device *, srpdev_window ),
+ INTF_OP ( intf_close, struct srp_device *, srpdev_close ),
+ INTF_OP ( acpi_describe, struct srp_device *, srpdev_describe ),
+};
+
+/** SRP device SCSI interface descriptor */
+static struct interface_descriptor srpdev_scsi_desc =
+ INTF_DESC ( struct srp_device, scsi, srpdev_scsi_op );
+
/**
- * Attach SRP device
+ * Open SRP device
*
- * @v scsi SCSI device
- * @v root_path Root path
+ * @v block Block control interface
+ * @v socket Socket interface
+ * @v initiator Initiator port ID
+ * @v target Target port ID
+ * @v memory_handle RDMA memory handle
+ * @v lun SCSI LUN
+ * @ret rc Return status code
*/
-int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
- struct srp_transport_type *transport;
- struct srp_device *srp;
+int srp_open ( struct interface *block, struct interface *socket,
+ union srp_port_id *initiator, union srp_port_id *target,
+ uint32_t memory_handle, struct scsi_lun *lun ) {
+ struct srp_device *srpdev;
+ int tag;
int rc;
- /* Hard-code an IB SRP back-end for now */
- transport = &ib_srp_transport;
-
/* Allocate and initialise structure */
- srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
- if ( ! srp ) {
+ srpdev = zalloc ( sizeof ( *srpdev ) );
+ if ( ! srpdev ) {
rc = -ENOMEM;
- goto err_alloc;
+ goto err_zalloc;
}
- ref_init ( &srp->refcnt, NULL );
- intf_init ( &srp->socket, &srp_xfer_desc, &srp->refcnt );
- srp->transport = transport;
- DBGC ( srp, "SRP %p using %s\n", srp, root_path );
-
- /* Parse root path */
- if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not parse root path: %s\n",
- srp, strerror ( rc ) );
- goto err_parse_root_path;
+ ref_init ( &srpdev->refcnt, NULL );
+ intf_init ( &srpdev->scsi, &srpdev_scsi_desc, &srpdev->refcnt );
+ intf_init ( &srpdev->socket, &srpdev_socket_desc, &srpdev->refcnt );
+ INIT_LIST_HEAD ( &srpdev->commands );
+ srpdev->memory_handle = memory_handle;
+ DBGC ( srpdev, "SRP %p %08x%08x%08x%08x->%08x%08x%08x%08x\n", srpdev,
+ ntohl ( initiator->dwords[0] ), ntohl ( initiator->dwords[1] ),
+ ntohl ( initiator->dwords[2] ), ntohl ( initiator->dwords[3] ),
+ ntohl ( target->dwords[0] ), ntohl ( target->dwords[1] ),
+ ntohl ( target->dwords[2] ), ntohl ( target->dwords[3] ) );
+
+ /* Preserve parameters required for boot firmware table */
+ memcpy ( &srpdev->initiator, initiator, sizeof ( srpdev->initiator ) );
+ memcpy ( &srpdev->target, target, sizeof ( srpdev->target ) );
+ memcpy ( &srpdev->lun, lun, sizeof ( srpdev->lun ) );
+
+ /* Attach to socket interface and initiate login */
+ intf_plug_plug ( &srpdev->socket, socket );
+ tag = srp_new_tag ( srpdev );
+ assert ( tag >= 0 ); /* Cannot fail when no commands in progress */
+ if ( ( rc = srp_login ( srpdev, initiator, target, tag ) ) != 0 )
+ goto err_login;
+
+ /* Attach SCSI device to parent interface */
+ if ( ( rc = scsi_open ( block, &srpdev->scsi, lun ) ) != 0 ) {
+ DBGC ( srpdev, "SRP %p could not create SCSI device: %s\n",
+ srpdev, strerror ( rc ) );
+ goto err_scsi_open;
}
- /* Attach parent interface, mortalise self, and return */
- scsi->backend = ref_get ( &srp->refcnt );
- scsi->command = srp_command;
- ref_put ( &srp->refcnt );
+ /* Mortalise self and return */
+ ref_put ( &srpdev->refcnt );
return 0;
- err_parse_root_path:
- ref_put ( &srp->refcnt );
- err_alloc:
+ err_scsi_open:
+ err_login:
+ srpdev_close ( srpdev, rc );
+ ref_put ( &srpdev->refcnt );
+ err_zalloc:
return rc;
}
-
-/**
- * Detach SRP device
- *
- * @v scsi SCSI device
- */
-void srp_detach ( struct scsi_device *scsi ) {
- struct srp_device *srp =
- container_of ( scsi->backend, struct srp_device, refcnt );
-
- /* Close socket */
- intf_shutdown ( &srp->socket, 0 );
- scsi->command = scsi_detached_command;
- ref_put ( scsi->backend );
- scsi->backend = NULL;
-}