diff options
author | Alexander Graf <agraf@suse.de> | 2011-05-19 11:57:09 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2011-05-19 12:08:28 +0200 |
commit | 1fddfba129f5435c80eda14e8bc23fdb888c7187 (patch) | |
tree | e179e02b5a2943002a43dbe06334ffdc0d890e49 | |
parent | 8d278467ff698de351a136e71f352e611e728579 (diff) |
ahci: Fix non-NCQ accesses for LBA > 16bits
AHCI provides two ways of reading/writing data:
1) NCQ
2) ATA commands with the LBA in the command FIS
In the second code path, we didn't handle any LBAs that were bigger than
16 bits, so whenever a guest that used high LBA numbers wanted to access
data, the LBA got truncated down to 16 bits, giving the guest garbage.
This patch adds support for LBAs higher than 16 bits. I've tested that it
works just fine with SeaBIOS and Linux guests. This patch also unbreaks
the often reported grub errors people have seen with AHCI.
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r-- | hw/ide/ahci.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 744d19d6db..1f008a3dda 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -884,8 +884,31 @@ static int handle_cmd(AHCIState *s, int port, int slot) } if (ide_state->drive_kind != IDE_CD) { - ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) | - cmd_fis[4]); + /* + * We set the sector depending on the sector defined in the FIS. + * Unfortunately, the spec isn't exactly obvious on this one. + * + * Apparently LBA48 commands set fis bytes 10,9,8,6,5,4 to the + * 48 bit sector number. ATA_CMD_READ_DMA_EXT is an example for + * such a command. + * + * Non-LBA48 commands however use 7[lower 4 bits],6,5,4 to define a + * 28-bit sector number. ATA_CMD_READ_DMA is an example for such + * a command. + * + * Since the spec doesn't explicitly state what each field should + * do, I simply assume non-used fields as reserved and OR everything + * together, independent of the command. + */ + ide_set_sector(ide_state, ((uint64_t)cmd_fis[10] << 40) + | ((uint64_t)cmd_fis[9] << 32) + /* This is used for LBA48 commands */ + | ((uint64_t)cmd_fis[8] << 24) + /* This is used for non-LBA48 commands */ + | ((uint64_t)(cmd_fis[7] & 0xf) << 24) + | ((uint64_t)cmd_fis[6] << 16) + | ((uint64_t)cmd_fis[5] << 8) + | cmd_fis[4]); } /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command |