diff --git a/src/drivers/block/ata.c b/src/drivers/block/ata.c index 8bd88fe4..3fcfa71c 100644 --- a/src/drivers/block/ata.c +++ b/src/drivers/block/ata.c @@ -50,39 +50,6 @@ ata_command ( struct ata_device *ata, struct ata_command *command ) { return ata->command ( ata, command ); } -/** - * Read block from / write block to ATA device - * - * @v write Write flag (ATA_FL_WRITE or 0) - * @v blockdev Block device - * @v block LBA block number - * @v count Block count - * @v buffer Data buffer - * @ret rc Return status code - */ -static __attribute__ (( regparm ( 1 ) )) int -ata_rw ( int 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; - int lba48 = ( ata->flags & ATA_FL_LBA48 ); - - memset ( &command, 0, sizeof ( command ) ); - command.cb.lba.native = block; - command.cb.count.native = count; - command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); - command.cb.flags = ( ata->flags | write ); - command.cb.cmd_stat = ( write ? ATA_CMD_WRITE : ATA_CMD_READ ); - if ( lba48 ) { - command.cb.cmd_stat |= ATA_CMD_EXT; - } else { - command.cb.device |= command.cb.lba.bytes.low_prev; - } - command.data = buffer; - command.data_len = ( count * blockdev->blksize ); - return ata_command ( ata, &command ); -} - /** * Read block from ATA device * @@ -94,10 +61,20 @@ ata_rw ( int write, struct block_device *blockdev, uint64_t block, */ static int ata_read ( struct block_device *blockdev, uint64_t block, unsigned long count, userptr_t buffer ) { - /* Pass through to ata_rw(). Since ata_rw is regparm(1), this - * is extremely efficient; just a mov and a jmp. - */ - return ata_rw ( 0, blockdev, block, count, 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_READ_EXT : ATA_CMD_READ ); + command.data_in = buffer; + command.data_in_len = ( count * blockdev->blksize ); + return ata_command ( ata, &command ); } /** @@ -111,10 +88,21 @@ static int ata_read ( struct block_device *blockdev, uint64_t block, */ static int ata_write ( struct block_device *blockdev, uint64_t block, unsigned long count, userptr_t buffer ) { - /* Pass through to ata_rw(). Since ata_rw is regparm(1), this - * is extremely efficient; just a mov and a jmp. - */ - return ata_rw ( ATA_FL_WRITE, blockdev, block, count, 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; + command.data_out_len = ( count * blockdev->blksize ); + return ata_command ( ata, &command ); } /** @@ -131,17 +119,19 @@ static int ata_identify ( struct block_device *blockdev ) { /* Issue IDENTIFY */ memset ( &command, 0, sizeof ( command ) ); - command.cb.device = ( ata->flags | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); + command.cb.count.native = 1; /* n/a according to spec, but at least + * AoE vblade devices require it. */ + command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); command.cb.cmd_stat = ATA_CMD_IDENTIFY; - command.data = virt_to_user ( &identity ); - command.data_len = sizeof ( identity ); + command.data_in = virt_to_user ( &identity ); + command.data_in_len = sizeof ( identity ); 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->flags |= ATA_FL_LBA48; + ata->lba48 = 1; blockdev->blocks = le64_to_cpu ( identity.lba48_sectors ); } else { blockdev->blocks = le32_to_cpu ( identity.lba_sectors ); diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h index 5aeb82e6..ecc9c5b4 100644 --- a/src/include/gpxe/ata.h +++ b/src/include/gpxe/ata.h @@ -56,9 +56,9 @@ union ata_lba { uint8_t low_prev; uint8_t mid_prev; uint8_t high_prev; - uint8_t pad[2]; + uint16_t pad; #elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t pad[2]; + uint16_t pad; uint8_t high_prev; uint8_t mid_prev; uint8_t low_prev; @@ -101,52 +101,55 @@ struct ata_cb { uint8_t device; /** Command/status register */ uint8_t cmd_stat; - /** Flags - * - * This field does not correspond to any ATA register. - */ - uint8_t flags; + /** LBA48 addressing flag */ + int lba48; }; -/** LBA48 extended addressing */ -#define ATA_FL_LBA48 0x40 - -/** Device 1 ("slave") */ -#define ATA_FL_SLAVE 0x10 - -/** Write command */ -#define ATA_FL_WRITE 0x01 - /** Obsolete bits in the ATA device register */ #define ATA_DEV_OBSOLETE 0xa0 /** LBA flag in the ATA device register */ #define ATA_DEV_LBA 0x40 +/** Slave ("device 1") flag in the ATA device register */ +#define ATA_DEV_SLAVE 0x10 + +/** Master ("device 0") flag in the ATA device register */ +#define ATA_DEV_MASTER 0x00 + /** "Read sectors" command */ #define ATA_CMD_READ 0x20 +/** "Read sectors (ext)" command */ +#define ATA_CMD_READ_EXT 0x24 + /** "Write sectors" command */ #define ATA_CMD_WRITE 0x30 +/** "Write sectors (ext)" command */ +#define ATA_CMD_WRITE_EXT 0x34 + /** "Identify" command */ #define ATA_CMD_IDENTIFY 0xec -/** "Extended (LBA48)" command modifier - * - * This doesn't apply to all ATA commands, but it does for @c - * ATA_CMD_READ and @c ATA_CMD_WRITE. - */ -#define ATA_CMD_EXT 0x04 - /** An ATA command */ struct ata_command { /** ATA command block */ struct ata_cb cb; - /** Data buffer */ - userptr_t data; - /** Data buffer length */ - size_t data_len; + /** Data-out buffer (may be NULL) */ + userptr_t data_out; + /** Data-out buffer length + * + * Must be zero if @c data_out is NULL + */ + size_t data_out_len; + /** Data-in buffer (may be NULL) */ + userptr_t data_in; + /** Data-in buffer length + * + * Must be zero if @c data_in is NULL + */ + size_t data_in_len; }; /** @@ -175,8 +178,13 @@ struct ata_identity { struct ata_device { /** Block device interface */ struct block_device blockdev; - /** Flags */ - int flags; + /** Device number + * + * Must be ATA_DEV_MASTER or ATA_DEV_SLAVE. + */ + int device; + /** LBA48 extended addressing */ + int lba48; /** * Issue ATA command *