diff --git a/src/drivers/ata/aoedev.c b/src/drivers/ata/aoedev.c index 8db484b7..0350bc09 100644 --- a/src/drivers/ata/aoedev.c +++ b/src/drivers/ata/aoedev.c @@ -37,7 +37,7 @@ static int aoe_command ( struct ata_device *ata, struct aoe_device *aoedev = container_of ( ata, struct aoe_device, ata ); - return aoe_issue ( &aoedev->aoe, command ); + return aoe_issue_split ( &aoedev->aoe, command ); } /** diff --git a/src/include/gpxe/aoe.h b/src/include/gpxe/aoe.h index 094feb5c..2e13d7a8 100644 --- a/src/include/gpxe/aoe.h +++ b/src/include/gpxe/aoe.h @@ -110,6 +110,8 @@ struct aoe_session { extern void aoe_open ( struct aoe_session *aoe ); extern void aoe_close ( struct aoe_session *aoe ); extern int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ); +extern int aoe_issue_split ( struct aoe_session *aoe, + struct ata_command *command ); /** An AoE device */ struct aoe_device { diff --git a/src/include/gpxe/ata.h b/src/include/gpxe/ata.h index ecc9c5b4..195e361c 100644 --- a/src/include/gpxe/ata.h +++ b/src/include/gpxe/ata.h @@ -117,6 +117,9 @@ struct ata_cb { /** Master ("device 0") flag in the ATA device register */ #define ATA_DEV_MASTER 0x00 +/** Mask of non-LBA portion of device register */ +#define ATA_DEV_MASK 0xf0 + /** "Read sectors" command */ #define ATA_CMD_READ 0x20 diff --git a/src/net/aoe.c b/src/net/aoe.c index e1c93931..7267cf0e 100644 --- a/src/net/aoe.c +++ b/src/net/aoe.c @@ -84,7 +84,7 @@ static int aoe_send_command ( struct aoe_session *aoe ) { aoecmd->cmd_stat = command->cb.cmd_stat; aoecmd->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); if ( ! command->cb.lba48 ) - aoecmd->lba.bytes[3] |= command->cb.device; + aoecmd->lba.bytes[3] |= ( command->cb.device & ATA_DEV_MASK ); /* Fill data payload */ copy_from_user ( pkb_put ( pkb, command->data_out_len ), @@ -244,8 +244,11 @@ void aoe_kick ( struct aoe_session *aoe ) { * Issue ATA command via an open AoE session * * @v aoe AoE session - * @v command AoE command + * @v command ATA command * @ret rc Return status code + * + * The ATA command must fit within a single AoE frame (i.e. the sector + * count must not exceed AOE_MAX_COUNT). */ int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ) { aoe->command = command; @@ -259,3 +262,46 @@ int aoe_issue ( struct aoe_session *aoe, struct ata_command *command ) { return ( ( aoe->status & AOE_STATUS_ERR_MASK ) ? -EIO : 0 ); } + +/** + * Issue ATA command via an open AoE session + * + * @v aoe AoE session + * @v command ATA command + * @ret rc Return status code + * + * The ATA command will be split into several smaller ATA commands, + * each with a sector count no larger than AOE_MAX_COUNT. + */ +int aoe_issue_split ( struct aoe_session *aoe, struct ata_command *command ) { + struct ata_command subcommand; + unsigned int offset; + unsigned int count; + unsigned int data_len; + unsigned int status = 0; + int rc = 0; + + /* Split ATA command into AoE-sized subcommands */ + for ( offset = 0; offset < command->cb.count.native; offset += count ){ + memcpy ( &subcommand, command, sizeof ( subcommand ) ); + count = ( command->cb.count.native - offset ); + if ( count > AOE_MAX_COUNT ) + count = AOE_MAX_COUNT; + data_len = count * ATA_SECTOR_SIZE; + if ( subcommand.data_in_len ) + subcommand.data_in_len = data_len; + if ( subcommand.data_out_len ) + subcommand.data_out_len = data_len; + aoe->command_offset = ( offset * ATA_SECTOR_SIZE ); + subcommand.cb.lba.native += offset; + subcommand.cb.count.native = count; + if ( ( rc = aoe_issue ( aoe, &subcommand ) ) != 0 ) + goto done; + status |= subcommand.cb.cmd_stat; + } + command->cb.cmd_stat = status; + + done: + aoe->command_offset = 0; + return rc; +}