ahci: add port I/O index-data pair
Implement an I/O space index-data register pair as defined by the AHCI spec, including the corresponding SATA PCI capability and BAR. This allows real-mode code to access the AHCI registers; real-mode code cannot address the memory-mapped register space because it is beyond the first megabyte. Signed-off-by: Daniel Verkamp <daniel@drv.nu> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
8e72506e20
commit
465f1ab161
@ -370,6 +370,43 @@ static MemoryRegionOps ahci_mem_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t ahci_idp_read(void *opaque, target_phys_addr_t addr,
|
||||
unsigned size)
|
||||
{
|
||||
AHCIState *s = opaque;
|
||||
|
||||
if (addr == s->idp_offset) {
|
||||
/* index register */
|
||||
return s->idp_index;
|
||||
} else if (addr == s->idp_offset + 4) {
|
||||
/* data register - do memory read at location selected by index */
|
||||
return ahci_mem_read(opaque, s->idp_index, size);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_idp_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
AHCIState *s = opaque;
|
||||
|
||||
if (addr == s->idp_offset) {
|
||||
/* index register - mask off reserved bits */
|
||||
s->idp_index = (uint32_t)val & ((AHCI_MEM_BAR_SIZE - 1) & ~3);
|
||||
} else if (addr == s->idp_offset + 4) {
|
||||
/* data register - do memory write at location selected by index */
|
||||
ahci_mem_write(opaque, s->idp_index, val, size);
|
||||
}
|
||||
}
|
||||
|
||||
static MemoryRegionOps ahci_idp_ops = {
|
||||
.read = ahci_idp_read,
|
||||
.write = ahci_idp_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
|
||||
static void ahci_reg_init(AHCIState *s)
|
||||
{
|
||||
int i;
|
||||
@ -1130,7 +1167,9 @@ void ahci_init(AHCIState *s, DeviceState *qdev, int ports)
|
||||
s->dev = g_malloc0(sizeof(AHCIDevice) * ports);
|
||||
ahci_reg_init(s);
|
||||
/* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
|
||||
memory_region_init_io(&s->mem, &ahci_mem_ops, s, "ahci", 0x1000);
|
||||
memory_region_init_io(&s->mem, &ahci_mem_ops, s, "ahci", AHCI_MEM_BAR_SIZE);
|
||||
memory_region_init_io(&s->idp, &ahci_idp_ops, s, "ahci-idp", 32);
|
||||
|
||||
irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
|
||||
|
||||
for (i = 0; i < s->ports; i++) {
|
||||
@ -1150,6 +1189,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, int ports)
|
||||
void ahci_uninit(AHCIState *s)
|
||||
{
|
||||
memory_region_destroy(&s->mem);
|
||||
memory_region_destroy(&s->idp);
|
||||
g_free(s->dev);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
#ifndef HW_IDE_AHCI_H
|
||||
#define HW_IDE_AHCI_H
|
||||
|
||||
#define AHCI_PCI_BAR 5
|
||||
#define AHCI_MEM_BAR_SIZE 0x1000
|
||||
#define AHCI_MAX_PORTS 32
|
||||
#define AHCI_MAX_SG 168 /* hardware max is 64K */
|
||||
#define AHCI_DMA_BOUNDARY 0xffffffff
|
||||
@ -212,6 +212,10 @@
|
||||
#define RES_FIS_SDBFIS 0x58
|
||||
#define RES_FIS_UFIS 0x60
|
||||
|
||||
#define SATA_CAP_SIZE 0x8
|
||||
#define SATA_CAP_REV 0x2
|
||||
#define SATA_CAP_BAR 0x4
|
||||
|
||||
typedef struct AHCIControlRegs {
|
||||
uint32_t cap;
|
||||
uint32_t ghc;
|
||||
@ -290,6 +294,9 @@ typedef struct AHCIState {
|
||||
AHCIDevice *dev;
|
||||
AHCIControlRegs control_regs;
|
||||
MemoryRegion mem;
|
||||
MemoryRegion idp; /* Index-Data Pair I/O port space */
|
||||
unsigned idp_offset; /* Offset of index in I/O port space */
|
||||
uint32_t idp_index; /* Current IDP index */
|
||||
int ports;
|
||||
qemu_irq irq;
|
||||
} AHCIState;
|
||||
|
27
hw/ide/ich.c
27
hw/ide/ich.c
@ -71,6 +71,14 @@
|
||||
#include <hw/ide/pci.h>
|
||||
#include <hw/ide/ahci.h>
|
||||
|
||||
#define ICH9_SATA_CAP_OFFSET 0xA8
|
||||
|
||||
#define ICH9_IDP_BAR 4
|
||||
#define ICH9_MEM_BAR 5
|
||||
|
||||
#define ICH9_IDP_INDEX 0x10
|
||||
#define ICH9_IDP_INDEX_LOG2 0x04
|
||||
|
||||
static const VMStateDescription vmstate_ahci = {
|
||||
.name = "ahci",
|
||||
.unmigratable = 1,
|
||||
@ -79,6 +87,8 @@ static const VMStateDescription vmstate_ahci = {
|
||||
static int pci_ich9_ahci_init(PCIDevice *dev)
|
||||
{
|
||||
struct AHCIPCIState *d;
|
||||
int sata_cap_offset;
|
||||
uint8_t *sata_cap;
|
||||
d = DO_UPCAST(struct AHCIPCIState, card, dev);
|
||||
|
||||
ahci_init(&d->ahci, &dev->qdev, 6);
|
||||
@ -97,7 +107,22 @@ static int pci_ich9_ahci_init(PCIDevice *dev)
|
||||
msi_init(dev, 0x50, 1, true, false);
|
||||
d->ahci.irq = d->card.irq[0];
|
||||
|
||||
pci_register_bar(&d->card, 5, 0, &d->ahci.mem);
|
||||
pci_register_bar(&d->card, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&d->ahci.idp);
|
||||
pci_register_bar(&d->card, ICH9_MEM_BAR, PCI_BASE_ADDRESS_SPACE_MEMORY,
|
||||
&d->ahci.mem);
|
||||
|
||||
sata_cap_offset = pci_add_capability(&d->card, PCI_CAP_ID_SATA,
|
||||
ICH9_SATA_CAP_OFFSET, SATA_CAP_SIZE);
|
||||
if (sata_cap_offset < 0) {
|
||||
return sata_cap_offset;
|
||||
}
|
||||
|
||||
sata_cap = d->card.config + sata_cap_offset;
|
||||
pci_set_word(sata_cap + SATA_CAP_REV, 0x10);
|
||||
pci_set_long(sata_cap + SATA_CAP_BAR,
|
||||
(ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4));
|
||||
d->ahci.idp_offset = ICH9_IDP_INDEX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -211,6 +211,7 @@
|
||||
#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
|
||||
#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
|
||||
#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
|
||||
#define PCI_CAP_ID_SATA 0x12 /* Serial ATA */
|
||||
#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
|
||||
#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
|
||||
#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
|
||||
|
Loading…
Reference in New Issue
Block a user