[SeaBIOS] [PATCH 3/3] add ahci support

Gerd Hoffmann kraxel at redhat.com
Thu Nov 25 16:42:31 CET 2010


This patch adds AHCI support to seabios.  Tested with virtual hardware
only (upcoming ahci support in qemu).  Coded by looking at the
recommandations in the intel ahci specs, so I don't expect much trouble
on real hardware.  Tested booting fedora install from hard disk and a
opensuse live iso from cdrom.

Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
 Makefile       |    2 +-
 src/ahci.c     |  484 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ahci.h     |  196 +++++++++++++++++++++++
 src/block.c    |    3 +
 src/blockcmd.c |    3 +
 src/config.h   |    2 +
 src/disk.h     |    1 +
 src/post.c     |    2 +
 8 files changed, 692 insertions(+), 1 deletions(-)
 create mode 100644 src/ahci.c
 create mode 100644 src/ahci.h

diff --git a/Makefile b/Makefile
index 1663a5d..acc2385 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ SRCBOTH=misc.c pmm.c stacks.c output.c util.c block.c floppy.c ata.c mouse.c \
         kbd.c pci.c serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
         pnpbios.c pirtable.c vgahooks.c ramdisk.c pcibios.c blockcmd.c \
         usb.c usb-uhci.c usb-ohci.c usb-ehci.c usb-hid.c usb-msc.c \
-        virtio-ring.c virtio-pci.c virtio-blk.c apm.c
+        virtio-ring.c virtio-pci.c virtio-blk.c apm.c ahci.c
 SRC16=$(SRCBOTH) system.c disk.c font.c
 SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
       acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
diff --git a/src/ahci.c b/src/ahci.c
new file mode 100644
index 0000000..9633951
--- /dev/null
+++ b/src/ahci.c
@@ -0,0 +1,484 @@
+// Low level AHCI disk access
+//
+// Copyright (C) 2010 Gerd Hoffmann <kraxel at redhat.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "types.h" // u8
+#include "ioport.h" // inb
+#include "util.h" // dprintf
+#include "biosvar.h" // GET_EBDA
+#include "pci.h" // foreachpci
+#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
+#include "pci_regs.h" // PCI_INTERRUPT_LINE
+#include "boot.h" // add_bcv_hd
+#include "disk.h" // struct ata_s
+#include "ata.h" // ATA_CB_STAT
+#include "ahci.h" // CDB_CMD_READ_10
+#include "blockcmd.h" // CDB_CMD_READ_10
+
+#define AHCI_MAX_RETRIES 5
+
+/****************************************************************
+ * these bits must run in both 16bit and 32bit modes
+ ****************************************************************/
+
+// prepare sata command fis
+static void memset_fl(void *ptr, u8 val, size_t size)
+{
+#if MODESEGMENT == 1
+    memset_far(FLATPTR_TO_SEG(ptr), (void*)(FLATPTR_TO_OFFSET(ptr)),
+               val, size);
+#else
+    memset(ptr, val, size);
+#endif
+}
+
+static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command)
+{
+    memset_fl(fis, 0, sizeof(*fis));
+    SET_FLATPTR(fis->command, command);
+}
+
+static void sata_prep_readwrite(struct sata_cmd_fis *fis,
+                                struct disk_op_s *op, int iswrite)
+{
+    u64 lba = op->lba;
+    u8 command;
+
+    memset_fl(fis, 0, sizeof(*fis));
+
+    if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
+        SET_FLATPTR(fis->sector_count2, op->count >> 8);
+        SET_FLATPTR(fis->lba_low2,      lba >> 24);
+        SET_FLATPTR(fis->lba_mid2,      lba >> 32);
+        SET_FLATPTR(fis->lba_high2,     lba >> 40);
+        lba &= 0xffffff;
+        command = (iswrite ? ATA_CMD_WRITE_DMA_EXT
+                   : ATA_CMD_READ_DMA_EXT);
+    } else {
+        command = (iswrite ? ATA_CMD_WRITE_DMA
+                   : ATA_CMD_READ_DMA);
+    }
+    SET_FLATPTR(fis->command,      command);
+    SET_FLATPTR(fis->sector_count, op->count);
+    SET_FLATPTR(fis->lba_low,      lba);
+    SET_FLATPTR(fis->lba_mid,      lba >> 8);
+    SET_FLATPTR(fis->lba_high,     lba >> 16);
+    SET_FLATPTR(fis->device,       ((lba >> 24) & 0xf) | ATA_CB_DH_LBA);
+}
+
+static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize)
+{
+    memset_fl(fis, 0, sizeof(*fis));
+    SET_FLATPTR(fis->command,  ATA_CMD_PACKET);
+    SET_FLATPTR(fis->lba_mid,  blocksize);
+    SET_FLATPTR(fis->lba_high, blocksize >> 8);
+}
+
+// ahci register access helpers
+static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg)
+{
+    u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
+    return pci_readl(addr);
+}
+
+static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val)
+{
+    u32 addr = GET_GLOBALFLAT(ctrl->iobase) + reg;
+    pci_writel(addr, val);
+}
+
+static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg)
+{
+    u32 ctrl_reg = 0x100;
+    ctrl_reg += pnr * 0x80;
+    ctrl_reg += port_reg;
+    return ctrl_reg;
+}
+
+static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg)
+{
+    u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
+    return ahci_ctrl_readl(ctrl, ctrl_reg);
+}
+
+static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val)
+{
+    u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg);
+    ahci_ctrl_writel(ctrl, ctrl_reg, val);
+}
+
+// submit ahci command + wait for result
+static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
+                        void *buffer, u32 bsize)
+{
+    u32 val, status, success, flags;
+    struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl);
+    struct ahci_cmd_s  *cmd  = GET_GLOBAL(port->cmd);
+    struct ahci_fis_s  *fis  = GET_GLOBAL(port->fis);
+    struct ahci_list_s *list = GET_GLOBAL(port->list);
+    u32 pnr                  = GET_GLOBAL(port->pnr);
+
+    SET_FLATPTR(cmd->fis.reg,       0x27);
+    SET_FLATPTR(cmd->fis.pmp_type,  (1 << 7)); /* cmd fis */
+    SET_FLATPTR(cmd->prdt[0].base,  ((u32)buffer));
+    SET_FLATPTR(cmd->prdt[0].baseu, 0);
+    SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
+
+    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
+
+    if (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE))
+        return -1;
+
+    flags = ((1 << 16) | /* one prd entry */
+             (1 << 10) | /* clear busy on ok */
+             (iswrite ? (1 << 6) : 0) |
+             (isatapi ? (1 << 5) : 0) |
+             (4 << 0)); /* fis length (dwords) */
+    SET_FLATPTR(list[0].flags, flags);
+    SET_FLATPTR(list[0].bytes,  bsize);
+    SET_FLATPTR(list[0].base,   ((u32)(cmd)));
+    SET_FLATPTR(list[0].baseu,  0);
+
+    dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
+    SET_FLATPTR(fis->rfis[2], 0);
+    ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
+    ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
+    while (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE)) {
+        yield();
+    }
+    while ((status = GET_FLATPTR(fis->rfis[2])) == 0) {
+        yield();
+    }
+
+    success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
+                                  ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) &&
+               ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY)));
+    dprintf(2, "AHCI/%d: ... finished, status 0x%x, %s\n", pnr,
+            status, success ? "OK" : "ERROR");
+    return success ? 0 : -1;
+}
+
+#define CDROM_CDB_SIZE 12
+
+int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
+{
+    struct ahci_port_s *port = container_of(
+        op->drive_g, struct ahci_port_s, drive);
+    struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
+    u8 *atapi = cdbcmd;
+    int i, rc;
+
+    sata_prep_atapi(&cmd->fis, blocksize);
+    for (i = 0; i < CDROM_CDB_SIZE; i++) {
+        SET_FLATPTR(cmd->atapi[i], atapi[i]);
+    }
+    rc = ahci_command(port, 0, 1, op->buf_fl,
+                      op->count * blocksize);
+    if (rc < 0)
+        return DISK_RET_EBADTRACK;
+    return DISK_RET_SUCCESS;
+}
+
+// read/write count blocks from a harddrive.
+static int
+ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
+{
+    struct ahci_port_s *port = container_of(
+        op->drive_g, struct ahci_port_s, drive);
+    struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd);
+    int rc;
+
+    sata_prep_readwrite(&cmd->fis, op, iswrite);
+    rc = ahci_command(port, iswrite, 0, op->buf_fl,
+                      op->count * DISK_SECTOR_SIZE);
+    dprintf(2, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n",
+            iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc);
+    if (rc < 0)
+        return DISK_RET_EBADTRACK;
+    return DISK_RET_SUCCESS;
+}
+
+// command demuxer
+int process_ahci_op(struct disk_op_s *op)
+{
+    struct ahci_port_s *port;
+    u32 atapi;
+
+    if (!CONFIG_AHCI)
+        return 0;
+
+    port = container_of(op->drive_g, struct ahci_port_s, drive);
+    atapi = GET_GLOBAL(port->atapi);
+
+    if (atapi) {
+        switch (op->command) {
+        case CMD_READ:
+            return cdb_read(op);
+        case CMD_WRITE:
+        case CMD_FORMAT:
+            return DISK_RET_EWRITEPROTECT;
+        case CMD_RESET:
+            /* FIXME: what should we do here? */
+        case CMD_VERIFY:
+        case CMD_SEEK:
+            return DISK_RET_SUCCESS;
+        default:
+            dprintf(1, "AHCI: unknown cdrom command %d\n", op->command);
+            op->count = 0;
+            return DISK_RET_EPARAM;
+        }
+    } else {
+        switch (op->command) {
+        case CMD_READ:
+            return ahci_disk_readwrite(op, 0);
+        case CMD_RESET:
+            /* FIXME: what should we do here? */
+        case CMD_FORMAT:
+        case CMD_VERIFY:
+        case CMD_SEEK:
+            return DISK_RET_SUCCESS;
+        default:
+            dprintf(1, "AHCI: unknown disk command %d\n", op->command);
+            op->count = 0;
+            return DISK_RET_EPARAM;
+        }
+    }
+}
+
+/****************************************************************
+ * everything below is pure 32bit code
+ ****************************************************************/
+
+static void
+ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
+{
+    u32 val, count = 0;
+
+    /* disable FIS + CMD */
+    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    while (val & (PORT_CMD_FIS_RX | PORT_CMD_START |
+                  PORT_CMD_FIS_ON | PORT_CMD_LIST_ON) &&
+           count < AHCI_MAX_RETRIES) {
+        val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START);
+        ahci_port_writel(ctrl, pnr, PORT_CMD, val);
+        ndelay(500);
+        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+        count++;
+    }
+
+    /* clear status */
+    val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
+    if (val)
+        ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
+
+    /* disable + clear IRQs */
+    ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, val);
+    val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+    if (val)
+        ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
+}
+
+static int
+ahci_port_probe(struct ahci_ctrl_s *ctrl, u32 pnr)
+{
+    u32 val, count = 0;
+
+    val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
+    while (val & ((1 << 7) /* BSY */ |
+                  (1 << 3) /* DRQ */)) {
+        ndelay(500);
+        val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
+        count++;
+        if (count >= AHCI_MAX_RETRIES)
+            return -1;
+    }
+
+    val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
+    if ((val & 0x07) != 0x03)
+        return -1;
+    return 0;
+}
+
+#define MAXMODEL 40
+
+static struct ahci_port_s*
+ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
+{
+    struct ahci_port_s *port = malloc_fseg(sizeof(*port));
+    char model[MAXMODEL+1];
+    u16 buffer[256];
+    u32 val;
+    int rc;
+
+    if (!port) {
+        warn_noalloc();
+        return NULL;
+    }
+    port->pnr = pnr;
+    port->ctrl = ctrl;
+    port->list = memalign_low(1024, 1024);
+    port->fis = memalign_low(256, 256);
+    port->cmd = memalign_low(256, 256);
+    if (port->list == NULL || port->fis == NULL || port->cmd == NULL) {
+        warn_noalloc();
+        return NULL;
+    }
+    memset(port->list, 0, 1024);
+    memset(port->fis, 0, 256);
+    memset(port->cmd, 0, 256);
+
+    ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
+    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_FIS_RX);
+
+    sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
+    rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
+    if (rc == 0) {
+        port->atapi = 1;
+    } else {
+        port->atapi = 0;
+        sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE);
+        rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
+        if (rc < 0)
+            goto err;
+    }
+
+    port->drive.type = DTYPE_AHCI;
+    port->drive.desc = malloc_tmp(MAXDESCSIZE);
+    port->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
+
+    if (!port->atapi) {
+        // found disk (ata)
+        port->drive.blksize = DISK_SECTOR_SIZE;
+        port->drive.pchs.cylinders = buffer[1];
+        port->drive.pchs.heads = buffer[3];
+        port->drive.pchs.spt = buffer[6];
+
+        u64 sectors;
+        if (buffer[83] & (1 << 10)) // word 83 - lba48 support
+            sectors = *(u64*)&buffer[100]; // word 100-103
+        else
+            sectors = *(u32*)&buffer[60]; // word 60 and word 61
+        port->drive.sectors = sectors;
+        u64 adjsize = sectors >> 11;
+        char adjprefix = 'M';
+        if (adjsize >= (1 << 16)) {
+            adjsize >>= 10;
+            adjprefix = 'G';
+        }
+        snprintf(port->drive.desc, MAXDESCSIZE
+                 , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
+                 , port->pnr
+                 , ata_extract_model(model, MAXMODEL, buffer)
+                 , ata_extract_version(buffer)
+                 , (u32)adjsize, adjprefix);
+
+        // Setup disk geometry translation.
+        setup_translation(&port->drive);
+        // Register with bcv system.
+        add_bcv_internal(&port->drive);
+    } else {
+        // found cdrom (atapi)
+        port->drive.blksize = CDROM_SECTOR_SIZE;
+        port->drive.sectors = (u64)-1;
+        u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
+        snprintf(port->drive.desc, MAXDESCSIZE, "AHCI/%d: %s ATAPI-%d %s"
+                 , port->pnr
+                 , ata_extract_model(model, MAXMODEL, buffer)
+                 , ata_extract_version(buffer)
+                 , (iscd ? "DVD/CD" : "Device"));
+
+        // fill cdidmap
+        if (iscd)
+            map_cd_drive(&port->drive);
+    }
+    dprintf(1, "%s\n", port->drive.desc);
+
+    return port;
+
+err:
+    dprintf(1, "AHCI/%d: init failure, reset\n", port->pnr);
+    ahci_port_reset(ctrl, pnr);
+    return NULL;
+}
+
+// Detect any drives attached to a given controller.
+static void
+ahci_detect(void *data)
+{
+    struct ahci_ctrl_s *ctrl = data;
+    struct ahci_port_s *port;
+    u32 pnr, max;
+    int rc;
+
+    max = ctrl->caps & 0x1f;
+    for (pnr = 0; pnr < max; pnr++) {
+        if (!(ctrl->ports & (1 << pnr)))
+            continue;
+        dprintf(2, "AHCI/%d: probing\n", pnr);
+        ahci_port_reset(ctrl, pnr);
+        rc = ahci_port_probe(ctrl, pnr);
+        dprintf(1, "AHCI/%d: link %s\n", pnr, rc == 0 ? "up" : "down");
+        if (rc != 0)
+            continue;
+        port = ahci_port_init(ctrl, pnr);
+    }
+}
+
+// Initialize an ata controller and detect its drives.
+static void
+ahci_init_controller(int bdf)
+{
+    struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl));
+    u32 val;
+
+    if (!ctrl) {
+        warn_noalloc();
+        return;
+    }
+    ctrl->pci_bdf = bdf;
+    ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5);
+    ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
+    dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n",
+            bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq);
+
+    val = ahci_ctrl_readl(ctrl, HOST_CTL);
+    ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN);
+
+    ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP);
+    ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL);
+    dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n",
+            ctrl->caps, ctrl->ports);
+
+    run_thread(ahci_detect, ctrl);
+}
+
+// Locate and init ahci controllers.
+static void
+ahci_init(void)
+{
+    // Scan PCI bus for ATA adapters
+    int bdf, max;
+    foreachpci(bdf, max) {
+        if (pci_config_readw(bdf, PCI_CLASS_DEVICE) != PCI_CLASS_STORAGE_SATA)
+            continue;
+        if (pci_config_readb(bdf, PCI_CLASS_PROG) != 1 /* AHCI rev 1 */)
+            continue;
+        ahci_init_controller(bdf);
+    }
+}
+
+void
+ahci_setup(void)
+{
+    ASSERT32FLAT();
+    if (!CONFIG_AHCI)
+        return;
+
+    dprintf(3, "init ahci\n");
+    ahci_init();
+}
diff --git a/src/ahci.h b/src/ahci.h
new file mode 100644
index 0000000..0e13e00
--- /dev/null
+++ b/src/ahci.h
@@ -0,0 +1,196 @@
+#ifndef __AHCI_H
+#define __AHCI_H
+
+struct sata_cmd_fis {
+    u8 reg;
+    u8 pmp_type;
+    u8 command;
+    u8 feature;
+
+    u8 lba_low;
+    u8 lba_mid;
+    u8 lba_high;
+    u8 device;
+
+    u8 lba_low2;
+    u8 lba_mid2;
+    u8 lba_high2;
+    u8 feature2;
+
+    u8 sector_count;
+    u8 sector_count2;
+    u8 res_1;
+    u8 control;
+
+    u8 res_2[64 - 16];
+};
+
+struct ahci_ctrl_s {
+    int pci_bdf;
+    u8  irq;
+    u32 iobase;
+    u32 caps;
+    u32 ports;
+};
+
+struct ahci_cmd_s {
+    struct sata_cmd_fis fis;
+    u8 atapi[0x20];
+    u8 res[0x20];
+    struct {
+        u32 base;
+        u32 baseu;
+        u32 res;
+        u32 flags;
+    } prdt[];
+};
+
+/* command list */
+struct ahci_list_s {
+    u32 flags;
+    u32 bytes;
+    u32 base;
+    u32 baseu;
+    u32 res[4];
+};
+
+struct ahci_fis_s {
+    u8 dsfis[0x1c];  /* dma setup */
+    u8 res_1[0x04];
+    u8 psfis[0x14];  /* pio setup */
+    u8 res_2[0x0c];
+    u8 rfis[0x14];   /* d2h register */
+    u8 res_3[0x04];
+    u8 sdbfis[0x08]; /* set device bits */
+    u8 ufis[0x40];   /* unknown */
+    u8 res_4[0x60];
+};
+
+struct ahci_port_s {
+    struct drive_s     drive;
+    struct ahci_ctrl_s *ctrl;
+    struct ahci_list_s *list;
+    struct ahci_fis_s  *fis;
+    struct ahci_cmd_s  *cmd;
+    u32                pnr;
+    u32                atapi;
+};
+
+void ahci_setup(void);
+int process_ahci_op(struct disk_op_s *op);
+int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize);
+
+#define AHCI_IRQ_ON_SG            (1 << 31)
+#define AHCI_CMD_ATAPI            (1 << 5)
+#define AHCI_CMD_WRITE            (1 << 6)
+#define AHCI_CMD_PREFETCH         (1 << 7)
+#define AHCI_CMD_RESET            (1 << 8)
+#define AHCI_CMD_CLR_BUSY         (1 << 10)
+
+#define RX_FIS_D2H_REG            0x40 /* offset of D2H Register FIS data */
+#define RX_FIS_SDB                0x58 /* offset of SDB FIS data */
+#define RX_FIS_UNK                0x60 /* offset of Unknown FIS data */
+
+/* global controller registers */
+#define HOST_CAP                  0x00 /* host capabilities */
+#define HOST_CTL                  0x04 /* global host control */
+#define HOST_IRQ_STAT             0x08 /* interrupt status */
+#define HOST_PORTS_IMPL           0x0c /* bitmap of implemented ports */
+#define HOST_VERSION              0x10 /* AHCI spec. version compliancy */
+
+/* HOST_CTL bits */
+#define HOST_CTL_RESET            (1 << 0)  /* reset controller; self-clear */
+#define HOST_CTL_IRQ_EN           (1 << 1)  /* global IRQ enable */
+#define HOST_CTL_AHCI_EN          (1 << 31) /* AHCI enabled */
+
+/* HOST_CAP bits */
+#define HOST_CAP_SSC              (1 << 14) /* Slumber capable */
+#define HOST_CAP_AHCI             (1 << 18) /* AHCI only */
+#define HOST_CAP_CLO              (1 << 24) /* Command List Override support */
+#define HOST_CAP_SSS              (1 << 27) /* Staggered Spin-up */
+#define HOST_CAP_NCQ              (1 << 30) /* Native Command Queueing */
+#define HOST_CAP_64               (1 << 31) /* PCI DAC (64-bit DMA) support */
+
+/* registers for each SATA port */
+#define PORT_LST_ADDR             0x00 /* command list DMA addr */
+#define PORT_LST_ADDR_HI          0x04 /* command list DMA addr hi */
+#define PORT_FIS_ADDR             0x08 /* FIS rx buf addr */
+#define PORT_FIS_ADDR_HI          0x0c /* FIS rx buf addr hi */
+#define PORT_IRQ_STAT             0x10 /* interrupt status */
+#define PORT_IRQ_MASK             0x14 /* interrupt enable/disable mask */
+#define PORT_CMD                  0x18 /* port command */
+#define PORT_TFDATA               0x20 /* taskfile data */
+#define PORT_SIG                  0x24 /* device TF signature */
+#define PORT_SCR_STAT             0x28 /* SATA phy register: SStatus */
+#define PORT_SCR_CTL              0x2c /* SATA phy register: SControl */
+#define PORT_SCR_ERR              0x30 /* SATA phy register: SError */
+#define PORT_SCR_ACT              0x34 /* SATA phy register: SActive */
+#define PORT_CMD_ISSUE            0x38 /* command issue */
+#define PORT_RESERVED             0x3c /* reserved */
+
+/* PORT_IRQ_{STAT,MASK} bits */
+#define PORT_IRQ_COLD_PRES        (1 << 31) /* cold presence detect */
+#define PORT_IRQ_TF_ERR           (1 << 30) /* task file error */
+#define PORT_IRQ_HBUS_ERR         (1 << 29) /* host bus fatal error */
+#define PORT_IRQ_HBUS_DATA_ERR    (1 << 28) /* host bus data error */
+#define PORT_IRQ_IF_ERR           (1 << 27) /* interface fatal error */
+#define PORT_IRQ_IF_NONFATAL      (1 << 26) /* interface non-fatal error */
+#define PORT_IRQ_OVERFLOW         (1 << 24) /* xfer exhausted available S/G */
+#define PORT_IRQ_BAD_PMP          (1 << 23) /* incorrect port multiplier */
+
+#define PORT_IRQ_PHYRDY           (1 << 22) /* PhyRdy changed */
+#define PORT_IRQ_DEV_ILCK         (1 << 7) /* device interlock */
+#define PORT_IRQ_CONNECT          (1 << 6) /* port connect change status */
+#define PORT_IRQ_SG_DONE          (1 << 5) /* descriptor processed */
+#define PORT_IRQ_UNK_FIS          (1 << 4) /* unknown FIS rx'd */
+#define PORT_IRQ_SDB_FIS          (1 << 3) /* Set Device Bits FIS rx'd */
+#define PORT_IRQ_DMAS_FIS         (1 << 2) /* DMA Setup FIS rx'd */
+#define PORT_IRQ_PIOS_FIS         (1 << 1) /* PIO Setup FIS rx'd */
+#define PORT_IRQ_D2H_REG_FIS      (1 << 0) /* D2H Register FIS rx'd */
+
+#define PORT_IRQ_FREEZE           (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR |   \
+                                   PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY |    \
+                                   PORT_IRQ_UNK_FIS)
+#define PORT_IRQ_ERROR            (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR |     \
+                                   PORT_IRQ_HBUS_DATA_ERR)
+#define DEF_PORT_IRQ              (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE |     \
+                                   PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS |  \
+                                   PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
+
+/* PORT_CMD bits */
+#define PORT_CMD_ATAPI            (1 << 24) /* Device is ATAPI */
+#define PORT_CMD_LIST_ON          (1 << 15) /* cmd list DMA engine running */
+#define PORT_CMD_FIS_ON           (1 << 14) /* FIS DMA engine running */
+#define PORT_CMD_FIS_RX           (1 << 4) /* Enable FIS receive DMA engine */
+#define PORT_CMD_CLO              (1 << 3) /* Command list override */
+#define PORT_CMD_POWER_ON         (1 << 2) /* Power up device */
+#define PORT_CMD_SPIN_UP          (1 << 1) /* Spin up device */
+#define PORT_CMD_START            (1 << 0) /* Enable port DMA engine */
+
+#define PORT_CMD_ICC_MASK         (0xf << 28) /* i/f ICC state mask */
+#define PORT_CMD_ICC_ACTIVE       (0x1 << 28) /* Put i/f in active state */
+#define PORT_CMD_ICC_PARTIAL      (0x2 << 28) /* Put i/f in partial state */
+#define PORT_CMD_ICC_SLUMBER      (0x6 << 28) /* Put i/f in slumber state */
+
+#define PORT_IRQ_STAT_DHRS        (1 << 0) /* Device to Host Register FIS */
+#define PORT_IRQ_STAT_PSS         (1 << 1) /* PIO Setup FIS */
+#define PORT_IRQ_STAT_DSS         (1 << 2) /* DMA Setup FIS */
+#define PORT_IRQ_STAT_SDBS        (1 << 3) /* Set Device Bits */
+#define PORT_IRQ_STAT_UFS         (1 << 4) /* Unknown FIS */
+#define PORT_IRQ_STAT_DPS         (1 << 5) /* Descriptor Processed */
+#define PORT_IRQ_STAT_PCS         (1 << 6) /* Port Connect Change Status */
+#define PORT_IRQ_STAT_DMPS        (1 << 7) /* Device Mechanical Presence
+                                              Status */
+#define PORT_IRQ_STAT_PRCS        (1 << 22) /* File Ready Status */
+#define PORT_IRQ_STAT_IPMS        (1 << 23) /* Incorrect Port Multiplier
+                                               Status */
+#define PORT_IRQ_STAT_OFS         (1 << 24) /* Overflow Status */
+#define PORT_IRQ_STAT_INFS        (1 << 26) /* Interface Non-Fatal Error
+                                               Status */
+#define PORT_IRQ_STAT_IFS         (1 << 27) /* Interface Fatal Error */
+#define PORT_IRQ_STAT_HBDS        (1 << 28) /* Host Bus Data Error Status */
+#define PORT_IRQ_STAT_HBFS        (1 << 29) /* Host Bus Fatal Error Status */
+#define PORT_IRQ_STAT_TFES        (1 << 30) /* Task File Error Status */
+#define PORT_IRQ_STAT_CPDS        (1 << 31) /* Code Port Detect Status */
+
+#endif // ahci.h
diff --git a/src/block.c b/src/block.c
index 3f4b13f..818c9f9 100644
--- a/src/block.c
+++ b/src/block.c
@@ -10,6 +10,7 @@
 #include "cmos.h" // inb_cmos
 #include "util.h" // dprintf
 #include "ata.h" // process_ata_op
+#include "ahci.h" // process_ahci_op
 #include "usb-msc.h" // process_usb_op
 #include "virtio-blk.h" // process_virtio_op
 
@@ -292,6 +293,8 @@ process_op(struct disk_op_s *op)
         return process_usb_op(op);
     case DTYPE_VIRTIO:
 	return process_virtio_op(op);
+    case DTYPE_AHCI:
+	return process_ahci_op(op);
     default:
         op->count = 0;
         return DISK_RET_EPARAM;
diff --git a/src/blockcmd.c b/src/blockcmd.c
index 48568e6..c9c6845 100644
--- a/src/blockcmd.c
+++ b/src/blockcmd.c
@@ -10,6 +10,7 @@
 #include "disk.h" // struct disk_op_s
 #include "blockcmd.h" // struct cdb_request_sense
 #include "ata.h" // atapi_cmd_data
+#include "ahci.h" // atapi_cmd_data
 #include "usb-msc.h" // usb_cmd_data
 
 // Route command to low-level handler.
@@ -22,6 +23,8 @@ cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
         return atapi_cmd_data(op, cdbcmd, blocksize);
     case DTYPE_USB:
         return usb_cmd_data(op, cdbcmd, blocksize);
+    case DTYPE_AHCI:
+        return ahci_cmd_data(op, cdbcmd, blocksize);
     default:
         op->count = 0;
         return DISK_RET_EPARAM;
diff --git a/src/config.h b/src/config.h
index f9bf3b8..ea525b1 100644
--- a/src/config.h
+++ b/src/config.h
@@ -52,6 +52,8 @@
 #define CONFIG_PS2PORT 1
 // Support for IDE disk code
 #define CONFIG_ATA 1
+// Support for AHCI disk code
+#define CONFIG_AHCI 1
 // Detect and try to use ATA bus mastering DMA controllers.
 #define CONFIG_ATA_DMA 0
 // Use 32bit PIO accesses on ATA (minor optimization on PCI transfers)
diff --git a/src/disk.h b/src/disk.h
index 55a5da3..c1ba6ae 100644
--- a/src/disk.h
+++ b/src/disk.h
@@ -198,6 +198,7 @@ struct drive_s {
 #define DTYPE_CDEMU    0x05
 #define DTYPE_USB      0x06
 #define DTYPE_VIRTIO   0x07
+#define DTYPE_AHCI     0x08
 
 #define MAXDESCSIZE 80
 
diff --git a/src/post.c b/src/post.c
index 1e35142..ff6813c 100644
--- a/src/post.c
+++ b/src/post.c
@@ -12,6 +12,7 @@
 #include "biosvar.h" // struct bios_data_area_s
 #include "disk.h" // floppy_drive_setup
 #include "ata.h" // ata_setup
+#include "ahci.h" // ahci_setup
 #include "memmap.h" // add_e820
 #include "pic.h" // pic_setup
 #include "pci.h" // create_pirtable
@@ -178,6 +179,7 @@ init_hw(void)
 
     floppy_setup();
     ata_setup();
+    ahci_setup();
     ramdisk_setup();
     virtio_blk_setup();
 }
-- 
1.7.1




More information about the SeaBIOS mailing list