aboutsummaryrefslogtreecommitdiff
path: root/hw/ide
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2011-05-19 11:57:09 +0200
committerKevin Wolf <kwolf@redhat.com>2011-05-19 12:08:28 +0200
commit1fddfba129f5435c80eda14e8bc23fdb888c7187 (patch)
treee179e02b5a2943002a43dbe06334ffdc0d890e49 /hw/ide
parent8d278467ff698de351a136e71f352e611e728579 (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>
Diffstat (limited to 'hw/ide')
-rw-r--r--hw/ide/ahci.c27
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