ahci: Do not ignore memory access read size

The only guidance the AHCI specification gives on memory access is:
"Register accesses shall have a maximum size of 64-bits; 64-bit access
must not cross an 8-byte alignment boundary."

I interpret this to mean that aligned or unaligned 1, 2 and 4 byte
accesses should work, as well as aligned 8 byte accesses.

In practice, a real Q35/ICH9 responds to 1, 2, 4 and 8 byte reads
regardless of alignment. Windows 7 can be observed making 1 byte
reads to the middle of 32 bit registers to fetch error codes.

Introduce a wrapper to support unaligned accesses to AHCI.
This wrapper will support aligned 8 byte reads, but will make
no effort to support unaligned 8 byte reads, which although they
will work on real hardware, are not guaranteed to work and do
not appear to be used by either Windows or Linux.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1434470575-21625-2-git-send-email-jsnow@redhat.com
This commit is contained in:
John Snow 2015-07-04 02:06:02 -04:00
parent 35360642d0
commit e9ebb2f767

View file

@ -331,8 +331,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
}
}
static uint64_t ahci_mem_read(void *opaque, hwaddr addr,
unsigned size)
static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr)
{
AHCIState *s = opaque;
uint32_t val = 0;
@ -368,6 +367,30 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr,
}
/**
* AHCI 1.3 section 3 ("HBA Memory Registers")
* Support unaligned 8/16/32 bit reads, and 64 bit aligned reads.
* Caller is responsible for masking unwanted higher order bytes.
*/
static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size)
{
hwaddr aligned = addr & ~0x3;
int ofst = addr - aligned;
uint64_t lo = ahci_mem_read_32(opaque, aligned);
uint64_t hi;
/* if < 8 byte read does not cross 4 byte boundary */
if (ofst + size <= 4) {
return lo >> (ofst * 8);
}
g_assert_cmpint(size, >, 1);
/* If the 64bit read is unaligned, we will produce undefined
* results. AHCI does not support unaligned 64bit reads. */
hi = ahci_mem_read_32(opaque, aligned + 4);
return (hi << 32 | lo) >> (ofst * 8);
}
static void ahci_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)