From 402ba15c6449f8dc7d5a31a04a9d7eb2eef330a3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 19 May 2006 18:54:38 +0000 Subject: [PATCH] READ CAPACITY (16) turns out to be an optional command (even though READ(16) is mandatory); we must use READ CAPACITY (10) first and then use READ CAPACITY (16) if the READ CAPACITY (10) returns "out of range". --- src/drivers/block/scsi.c | 59 ++++++++++++++++++++++++++++++++++++++-- src/include/gpxe/scsi.h | 27 ++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index 1a1d70d8..1e906e13 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -96,12 +96,41 @@ static int scsi_write ( struct block_device *blockdev, uint64_t block, } /** - * Read capacity of SCSI device + * Read capacity of SCSI device via READ CAPACITY (10) * * @v blockdev Block device * @ret rc Return status code */ -static int scsi_read_capacity ( struct block_device *blockdev ) { +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; + int rc; + + /* Issue READ CAPACITY (10) */ + 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 ) + return rc; + + /* Fill in block device fields */ + blockdev->blksize = be32_to_cpu ( capacity.blksize ); + blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 ); + + return 0; +} + +/** + * Read capacity of SCSI device via READ CAPACITY (16) + * + * @v blockdev Block device + * @ret rc Return status code + */ +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; @@ -125,6 +154,32 @@ static int scsi_read_capacity ( struct block_device *blockdev ) { return 0; } +/** + * Read capacity of SCSI device + * + * @v blockdev Block device + * @ret rc Return status code + */ +static int scsi_read_capacity ( struct block_device *blockdev ) { + int rc; + + /* Try READ CAPACITY (10), which is a mandatory command, first. */ + if ( ( rc = scsi_read_capacity_10 ( blockdev ) ) != 0 ) + return rc; + + /* 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 ( blockdev->blocks == 0 ) { + if ( ( rc = scsi_read_capacity_16 ( blockdev ) ) != 0 ) + return rc; + } + + return 0; +} + /** * Initialise SCSI device * diff --git a/src/include/gpxe/scsi.h b/src/include/gpxe/scsi.h index d7da1255..d5620ef8 100644 --- a/src/include/gpxe/scsi.h +++ b/src/include/gpxe/scsi.h @@ -12,6 +12,7 @@ #define SCSI_OPCODE_READ_16 0x88 /**< READ (16) */ #define SCSI_OPCODE_WRITE_16 0x8a /**< WRITE (16) */ +#define SCSI_OPCODE_READ_CAPACITY_10 0x25 /**< READ CAPACITY (10) */ #define SCSI_OPCODE_SERVICE_ACTION_IN 0x9e /**< SERVICE ACTION IN */ #define SCSI_SERVICE_ACTION_READ_CAPACITY_16 0x10 /**< READ CAPACITY (16) */ @@ -77,6 +78,31 @@ struct scsi_cdb_write_16 { uint8_t control; } __attribute__ (( packed )); +/** A SCSI "READ CAPACITY (10)" CDB */ +struct scsi_cdb_read_capacity_10 { + /** Opcode (0x25) */ + uint8_t opcode; + /** Reserved */ + uint8_t reserved_a; + /** Logical block address + * + * Applicable only if the PMI bit is set. + */ + uint32_t lba; + /** Reserved */ + uint8_t reserved_b[3]; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** SCSI "READ CAPACITY (10)" parameter data */ +struct scsi_capacity_10 { + /** Maximum logical block number */ + uint32_t lba; + /** Block length in bytes */ + uint32_t blksize; +} __attribute__ (( packed )); + /** A SCSI "READ CAPACITY (16)" CDB */ struct scsi_cdb_read_capacity_16 { /** Opcode (0x9e) */ @@ -113,6 +139,7 @@ struct scsi_capacity_16 { union scsi_cdb { struct scsi_cdb_read_16 read16; struct scsi_cdb_write_16 write16; + struct scsi_cdb_read_capacity_10 readcap10; struct scsi_cdb_read_capacity_16 readcap16; char bytes[16]; };