* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
- *
- * lspci dump of a ICH-9 real device in IDE mode (hopefully close enough):
- *
- * 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0])
- * Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922]
- * Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
- * Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
- * Latency: 0
- * Interrupt: pin B routed to IRQ 222
- * Region 0: I/O ports at d000 [size=8]
- * Region 1: I/O ports at cc00 [size=4]
- * Region 2: I/O ports at c880 [size=8]
- * Region 3: I/O ports at c800 [size=4]
- * Region 4: I/O ports at c480 [size=32]
- * Region 5: Memory at febf9000 (32-bit, non-prefetchable) [size=2K]
- * Capabilities: [80] Message Signalled Interrupts: Mask- 64bit- Count=1/16 Enable+
- * Address: fee0f00c Data: 41d9
- * Capabilities: [70] Power Management version 3
- * Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot+,D3cold-)
- * Status: D0 PME-Enable- DSel=0 DScale=0 PME-
- * Capabilities: [a8] SATA HBA <?>
- * Capabilities: [b0] Vendor Specific Information <?>
- * Kernel driver in use: ahci
- * Kernel modules: ahci
- * 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00
- * 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00
- * 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29
- * 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00
- * 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00
- * 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00
- * 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00
- * 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00
- * a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00
- * b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00
- * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- * f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00
- *
*/
#include <hw/hw.h>
static int handle_cmd(AHCIState *s,int port,int slot);
static void ahci_reset_port(AHCIState *s, int port);
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
+static void ahci_init_d2h(AHCIDevice *ad);
static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
{
DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus);
- for (i = 0; i < SATA_PORTS; i++) {
+ for (i = 0; i < s->ports; i++) {
AHCIPortRegs *pr = &s->dev[i].port_regs;
if (pr->irq_stat & pr->irq_mask) {
s->control_regs.irqstatus |= (1 << i);
pr->cmd |= PORT_CMD_FIS_ON;
}
+ /* XXX usually the FIS would be pending on the bus here and
+ issuing deferred until the OS enables FIS receival.
+ Instead, we only submit it once - which works in most
+ cases, but is a hack. */
+ if ((pr->cmd & PORT_CMD_FIS_ON) &&
+ !s->dev[port].init_d2h_sent) {
+ ahci_init_d2h(&s->dev[port]);
+ s->dev[port].init_d2h_sent = 1;
+ }
+
check_cmd(s, port);
break;
case PORT_TFDATA:
}
}
-static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr)
+static uint64_t ahci_mem_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
{
- AHCIState *s = ptr;
+ AHCIState *s = opaque;
uint32_t val = 0;
- addr = addr & 0xfff;
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
switch (addr) {
case HOST_CAP:
DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val);
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
- (addr < AHCI_PORT_REGS_END_ADDR)) {
+ (addr < (AHCI_PORT_REGS_START_ADDR +
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK);
}
-static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+static void ahci_mem_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
{
- AHCIState *s = ptr;
- addr = addr & 0xfff;
+ AHCIState *s = opaque;
/* Only aligned reads are allowed on AHCI */
if (addr & 3) {
case HOST_CTL: /* R/W */
if (val & HOST_CTL_RESET) {
DPRINTF(-1, "HBA Reset\n");
- /* FIXME reset? */
+ ahci_reset(container_of(s, AHCIPCIState, ahci));
} else {
s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
ahci_check_irq(s);
DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr);
}
} else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
- (addr < AHCI_PORT_REGS_END_ADDR)) {
+ (addr < (AHCI_PORT_REGS_START_ADDR +
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
}
}
-static CPUReadMemoryFunc * const ahci_readfn[3]={
- ahci_mem_readl,
- ahci_mem_readl,
- ahci_mem_readl
-};
-
-static CPUWriteMemoryFunc * const ahci_writefn[3]={
- ahci_mem_writel,
- ahci_mem_writel,
- ahci_mem_writel
+static MemoryRegionOps ahci_mem_ops = {
+ .read = ahci_mem_read,
+ .write = ahci_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void ahci_reg_init(AHCIState *s)
{
int i;
- s->control_regs.cap = (SATA_PORTS - 1) |
+ s->control_regs.cap = (s->ports - 1) |
(AHCI_NUM_COMMAND_SLOTS << 8) |
(AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) |
HOST_CAP_NCQ | HOST_CAP_AHCI;
- s->control_regs.impl = (1 << SATA_PORTS) - 1;
+ s->control_regs.impl = (1 << s->ports) - 1;
s->control_regs.version = AHCI_VERSION_1_0;
- for (i = 0; i < SATA_PORTS; i++) {
+ for (i = 0; i < s->ports; i++) {
s->dev[i].port_state = STATE_RUN;
}
}
check_cmd(ad->hba, ad->port_no);
}
+static void ahci_init_d2h(AHCIDevice *ad)
+{
+ uint8_t init_fis[0x20];
+ IDEState *ide_state = &ad->port.ifs[0];
+
+ memset(init_fis, 0, sizeof(init_fis));
+
+ init_fis[4] = 1;
+ init_fis[12] = 1;
+
+ if (ide_state->drive_kind == IDE_CD) {
+ init_fis[5] = ide_state->lcyl;
+ init_fis[6] = ide_state->hcyl;
+ }
+
+ ahci_write_fis_d2h(ad, init_fis);
+}
+
static void ahci_reset_port(AHCIState *s, int port)
{
AHCIDevice *d = &s->dev[port];
AHCIPortRegs *pr = &d->port_regs;
IDEState *ide_state = &d->port.ifs[0];
- uint8_t init_fis[0x20];
int i;
DPRINTF(port, "reset port\n");
pr->scr_err = 0;
pr->scr_act = 0;
d->busy_slot = -1;
+ d->init_d2h_sent = 0;
ide_state = &s->dev[port].port.ifs[0];
if (!ide_state->bs) {
ncq_tfs->used = 0;
}
- memset(init_fis, 0, sizeof(init_fis));
s->dev[port].port_state = STATE_RUN;
if (!ide_state->bs) {
s->dev[port].port_regs.sig = 0;
ide_state->lcyl = 0x14;
ide_state->hcyl = 0xeb;
DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
- init_fis[5] = ide_state->lcyl;
- init_fis[6] = ide_state->hcyl;
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
} else {
s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK;
}
ide_state->error = 1;
- init_fis[4] = 1;
- init_fis[12] = 1;
- ahci_write_fis_d2h(d, init_fis);
+ ahci_init_d2h(d);
}
static void debug_print_fis(uint8_t *fis, int cmd_len)
}
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
ad->dma_cb = NULL;
- /* maybe we still have something to process, check later */
- ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
- qemu_bh_schedule(ad->check_bh);
+ if (!ad->check_bh) {
+ /* maybe we still have something to process, check later */
+ ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
+ qemu_bh_schedule(ad->check_bh);
+ }
return 0;
}
.reset = ahci_dma_reset,
};
-void ahci_init(AHCIState *s, DeviceState *qdev)
+void ahci_init(AHCIState *s, DeviceState *qdev, int ports)
{
qemu_irq *irqs;
int i;
+ s->ports = ports;
+ s->dev = g_malloc0(sizeof(AHCIDevice) * ports);
ahci_reg_init(s);
- s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s,
- DEVICE_LITTLE_ENDIAN);
- irqs = qemu_allocate_irqs(ahci_irq_set, s, SATA_PORTS);
+ /* 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);
+ irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
- for (i = 0; i < SATA_PORTS; i++) {
+ for (i = 0; i < s->ports; i++) {
AHCIDevice *ad = &s->dev[i];
ide_bus_new(&ad->port, qdev, i);
}
}
-void ahci_pci_map(PCIDevice *pci_dev, int region_num,
- pcibus_t addr, pcibus_t size, int type)
+void ahci_uninit(AHCIState *s)
{
- struct AHCIPCIState *d = (struct AHCIPCIState *)pci_dev;
- AHCIState *s = &d->ahci;
-
- cpu_register_physical_memory(addr, size, s->mem);
+ memory_region_destroy(&s->mem);
+ g_free(s->dev);
}
void ahci_reset(void *opaque)
struct AHCIPCIState *d = opaque;
int i;
- for (i = 0; i < SATA_PORTS; i++) {
- ahci_reset_port(&d->ahci, i);
- }
-}
-
-static int pci_ahci_init(PCIDevice *dev)
-{
- struct AHCIPCIState *d;
- d = DO_UPCAST(struct AHCIPCIState, card, dev);
-
- pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL);
- pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR);
-
- pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA);
- pci_config_set_revision(d->card.config, 0x02);
- pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1);
-
- d->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
- d->card.config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */
- pci_config_set_interrupt_pin(d->card.config, 1);
-
- /* XXX Software should program this register */
- d->card.config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */
-
- qemu_register_reset(ahci_reset, d);
-
- /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
- pci_register_bar(&d->card, 5, 0x1000, PCI_BASE_ADDRESS_SPACE_MEMORY,
- ahci_pci_map);
-
- msi_init(dev, 0x50, 1, true, false);
-
- ahci_init(&d->ahci, &dev->qdev);
- d->ahci.irq = d->card.irq[0];
-
- return 0;
-}
+ d->ahci.control_regs.irqstatus = 0;
+ d->ahci.control_regs.ghc = 0;
-static int pci_ahci_uninit(PCIDevice *dev)
-{
- struct AHCIPCIState *d;
- d = DO_UPCAST(struct AHCIPCIState, card, dev);
-
- if (msi_enabled(dev)) {
- msi_uninit(dev);
+ for (i = 0; i < d->ahci.ports; i++) {
+ ahci_reset_port(&d->ahci, i);
}
-
- qemu_unregister_reset(ahci_reset, d);
-
- return 0;
}
-
-static void pci_ahci_write_config(PCIDevice *pci, uint32_t addr,
- uint32_t val, int len)
-{
- pci_default_write_config(pci, addr, val, len);
- msi_write_config(pci, addr, val, len);
-}
-
-static PCIDeviceInfo ahci_info = {
- .qdev.name = "ahci",
- .qdev.size = sizeof(AHCIPCIState),
- .init = pci_ahci_init,
- .exit = pci_ahci_uninit,
- .config_write = pci_ahci_write_config,
-};
-
-static void ahci_pci_register_devices(void)
-{
- pci_qdev_register(&ahci_info);
-}
-
-device_init(ahci_pci_register_devices)