qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 1/6] add ahci support into qemu, only support sata d


From: QiaoChong
Subject: [Qemu-devel] [PATCH 1/6] add ahci support into qemu, only support sata disk.
Date: Tue, 11 May 2010 07:19:02 +0800

use -drive if=sd,file=diskname to add a ahci disk into qemu.

Signed-off-by: QiaoChong <address@hidden>
---
 Makefile.target |    4 +
 hw/ahci.c       |  805 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 809 insertions(+), 0 deletions(-)
 create mode 100644 hw/ahci.c

diff --git a/Makefile.target b/Makefile.target
index c092900..d338af8 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -187,6 +187,10 @@ obj-$(CONFIG_USB_OHCI) += usb-ohci.o
 obj-y += rtl8139.o
 obj-y += e1000.o
 
+# ahci
+#
+obj-$(CONFIG_AHCI) += ahci.o
+
 # Hardware support
 obj-i386-y = pckbd.o dma.o
 obj-i386-y += vga.o
diff --git a/hw/ahci.c b/hw/ahci.c
new file mode 100644
index 0000000..a332a45
--- /dev/null
+++ b/hw/ahci.c
@@ -0,0 +1,805 @@
+/*
+ * QEMU AHCI Emulation
+ * Copyright (c) 2010 address@hidden
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * 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/>.
+ *
+ * TODO:
+ *  o ahci cd support
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysbus.h"
+#include "pci.h"
+#include "dma.h"
+#include "cpu-common.h"
+#include <hw/ide/internal.h>
+#define DPRINTF(...)
+
+
+enum {
+       AHCI_PCI_BAR            = 5,
+       AHCI_MAX_PORTS          = 32,
+       AHCI_MAX_SG             = 168, /* hardware max is 64K */
+       AHCI_DMA_BOUNDARY       = 0xffffffff,
+       AHCI_USE_CLUSTERING     = 0,
+       AHCI_MAX_CMDS           = 32,
+       AHCI_CMD_SZ             = 32,
+       AHCI_CMD_SLOT_SZ        = AHCI_MAX_CMDS * AHCI_CMD_SZ,
+       AHCI_RX_FIS_SZ          = 256,
+       AHCI_CMD_TBL_CDB        = 0x40,
+       AHCI_CMD_TBL_HDR_SZ     = 0x80,
+       AHCI_CMD_TBL_SZ         = AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16),
+       AHCI_CMD_TBL_AR_SZ      = AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS,
+       AHCI_PORT_PRIV_DMA_SZ   = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ +
+               AHCI_RX_FIS_SZ,
+       AHCI_IRQ_ON_SG          = (1 << 31),
+       AHCI_CMD_ATAPI          = (1 << 5),
+       AHCI_CMD_WRITE          = (1 << 6),
+       AHCI_CMD_PREFETCH       = (1 << 7),
+       AHCI_CMD_RESET          = (1 << 8),
+       AHCI_CMD_CLR_BUSY       = (1 << 10),
+
+       RX_FIS_D2H_REG          = 0x40, /* offset of D2H Register FIS data */
+       RX_FIS_SDB              = 0x58, /* offset of SDB FIS data */
+       RX_FIS_UNK              = 0x60, /* offset of Unknown FIS data */
+
+       board_ahci              = 0,
+       board_ahci_pi           = 1,
+       board_ahci_vt8251       = 2,
+       board_ahci_ign_iferr    = 3,
+       board_ahci_sb600        = 4,
+
+       /* global controller registers */
+       HOST_CAP                = 0x00, /* host capabilities */
+       HOST_CTL                = 0x04, /* global host control */
+       HOST_IRQ_STAT           = 0x08, /* interrupt status */
+       HOST_PORTS_IMPL         = 0x0c, /* bitmap of implemented ports */
+       HOST_VERSION            = 0x10, /* AHCI spec. version compliancy */
+
+       /* HOST_CTL bits */
+       HOST_RESET              = (1 << 0),  /* reset controller; self-clear */
+       HOST_IRQ_EN             = (1 << 1),  /* global IRQ enable */
+       HOST_AHCI_EN            = (1 << 31), /* AHCI enabled */
+
+       /* HOST_CAP bits */
+       HOST_CAP_SSC            = (1 << 14), /* Slumber capable */
+       HOST_CAP_CLO            = (1 << 24), /* Command List Override support */
+       HOST_CAP_SSS            = (1 << 27), /* Staggered Spin-up */
+       HOST_CAP_NCQ            = (1 << 30), /* Native Command Queueing */
+       HOST_CAP_64             = (1 << 31), /* PCI DAC (64-bit DMA) support */
+
+       /* registers for each SATA port */
+       PORT_LST_ADDR           = 0x00, /* command list DMA addr */
+       PORT_LST_ADDR_HI        = 0x04, /* command list DMA addr hi */
+       PORT_FIS_ADDR           = 0x08, /* FIS rx buf addr */
+       PORT_FIS_ADDR_HI        = 0x0c, /* FIS rx buf addr hi */
+       PORT_IRQ_STAT           = 0x10, /* interrupt status */
+       PORT_IRQ_MASK           = 0x14, /* interrupt enable/disable mask */
+       PORT_CMD                = 0x18, /* port command */
+       PORT_TFDATA             = 0x20, /* taskfile data */
+       PORT_SIG                = 0x24, /* device TF signature */
+       PORT_CMD_ISSUE          = 0x38, /* command issue */
+       PORT_SCR                = 0x28, /* SATA phy register block */
+       PORT_SCR_STAT           = 0x28, /* SATA phy register: SStatus */
+       PORT_SCR_CTL            = 0x2c, /* SATA phy register: SControl */
+       PORT_SCR_ERR            = 0x30, /* SATA phy register: SError */
+       PORT_SCR_ACT            = 0x34, /* SATA phy register: SActive */
+
+       /* PORT_IRQ_{STAT,MASK} bits */
+       PORT_IRQ_COLD_PRES      = (1 << 31), /* cold presence detect */
+       PORT_IRQ_TF_ERR         = (1 << 30), /* task file error */
+       PORT_IRQ_HBUS_ERR       = (1 << 29), /* host bus fatal error */
+       PORT_IRQ_HBUS_DATA_ERR  = (1 << 28), /* host bus data error */
+       PORT_IRQ_IF_ERR         = (1 << 27), /* interface fatal error */
+       PORT_IRQ_IF_NONFATAL    = (1 << 26), /* interface non-fatal error */
+       PORT_IRQ_OVERFLOW       = (1 << 24), /* xfer exhausted available S/G */
+       PORT_IRQ_BAD_PMP        = (1 << 23), /* incorrect port multiplier */
+
+       PORT_IRQ_PHYRDY         = (1 << 22), /* PhyRdy changed */
+       PORT_IRQ_DEV_ILCK       = (1 << 7), /* device interlock */
+       PORT_IRQ_CONNECT        = (1 << 6), /* port connect change status */
+       PORT_IRQ_SG_DONE        = (1 << 5), /* descriptor processed */
+       PORT_IRQ_UNK_FIS        = (1 << 4), /* unknown FIS rx'd */
+       PORT_IRQ_SDB_FIS        = (1 << 3), /* Set Device Bits FIS rx'd */
+       PORT_IRQ_DMAS_FIS       = (1 << 2), /* DMA Setup FIS rx'd */
+       PORT_IRQ_PIOS_FIS       = (1 << 1), /* PIO Setup FIS rx'd */
+       PORT_IRQ_D2H_REG_FIS    = (1 << 0), /* D2H Register FIS rx'd */
+
+       PORT_IRQ_FREEZE         = PORT_IRQ_HBUS_ERR |
+               PORT_IRQ_IF_ERR |
+               PORT_IRQ_CONNECT |
+               PORT_IRQ_PHYRDY |
+               PORT_IRQ_UNK_FIS,
+       PORT_IRQ_ERROR          = PORT_IRQ_FREEZE |
+               PORT_IRQ_TF_ERR |
+               PORT_IRQ_HBUS_DATA_ERR,
+       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 */
+       PORT_CMD_ATAPI          = (1 << 24), /* Device is ATAPI */
+       PORT_CMD_LIST_ON        = (1 << 15), /* cmd list DMA engine running */
+       PORT_CMD_FIS_ON         = (1 << 14), /* FIS DMA engine running */
+       PORT_CMD_FIS_RX         = (1 << 4), /* Enable FIS receive DMA engine */
+       PORT_CMD_CLO            = (1 << 3), /* Command list override */
+       PORT_CMD_POWER_ON       = (1 << 2), /* Power up device */
+       PORT_CMD_SPIN_UP        = (1 << 1), /* Spin up device */
+       PORT_CMD_START          = (1 << 0), /* Enable port DMA engine */
+
+       PORT_CMD_ICC_MASK       = (0xf << 28), /* i/f ICC state mask */
+       PORT_CMD_ICC_ACTIVE     = (0x1 << 28), /* Put i/f in active state */
+       PORT_CMD_ICC_PARTIAL    = (0x2 << 28), /* Put i/f in partial state */
+       PORT_CMD_ICC_SLUMBER    = (0x6 << 28), /* Put i/f in slumber state */
+
+       /* ap->flags bits */
+       AHCI_FLAG_NO_NCQ                = (1 << 24),
+       AHCI_FLAG_IGN_IRQ_IF_ERR        = (1 << 25), /* ignore IRQ_IF_ERR */
+       AHCI_FLAG_HONOR_PI              = (1 << 26), /* honor PORTS_IMPL */
+       AHCI_FLAG_IGN_SERR_INTERNAL     = (1 << 27), /* ignore SERR_INTERNAL */
+       AHCI_FLAG_32BIT_ONLY            = (1 << 28), /* force 32bit */
+};
+
+/*
+ * ATA Commands (only mandatory commands listed here)
+ */
+#define ATA_CMD_READ   0x20    /* Read Sectors (with retries)  */
+#define ATA_CMD_READN  0x21    /* Read Sectors ( no  retries)  */
+#define ATA_CMD_WRITE  0x30    /* Write Sectores (with retries)*/
+#define ATA_CMD_WRITEN 0x31    /* Write Sectors  ( no  retries)*/
+#define ATA_CMD_VRFY   0x40    /* Read Verify  (with retries)  */
+#define ATA_CMD_VRFYN  0x41    /* Read verify  ( no  retries)  */
+#define ATA_CMD_SEEK   0x70    /* Seek                         */
+#define ATA_CMD_DIAG   0x90    /* Execute Device Diagnostic    */
+#define ATA_CMD_INIT   0x91    /* Initialize Device Parameters */
+#define ATA_CMD_RD_MULT        0xC4    /* Read Multiple                */
+#define ATA_CMD_WR_MULT        0xC5    /* Write Multiple               */
+#define ATA_CMD_SETMULT        0xC6    /* Set Multiple Mode            */
+#define ATA_CMD_RD_DMA 0xC8    /* Read DMA (with retries)      */
+#define ATA_CMD_RD_DMAN        0xC9    /* Read DMS ( no  retries)      */
+#define ATA_CMD_WR_DMA 0xCA    /* Write DMA (with retries)     */
+#define ATA_CMD_WR_DMAN        0xCB    /* Write DMA ( no  retires)     */
+#define ATA_CMD_IDENT  0xEC    /* Identify Device              */
+#define ATA_CMD_SETF   0xEF    /* Set Features                 */
+#define ATA_CMD_CHK_PWR        0xE5    /* Check Power Mode             */
+
+#define ATA_CMD_READ_EXT 0x24  /* Read Sectors (with retries)  with 48bit 
addressing */
+#define ATA_CMD_WRITE_EXT      0x34    /* Write Sectores (with retries) with 
48bit addressing */
+#define ATA_CMD_VRFY_EXT       0x42    /* Read Verify  (with retries)  with 
48bit addressing */
+
+/*
+ * ATAPI Commands
+ */
+#define ATAPI_CMD_IDENT 0xA1 /* Identify AT Atachment Packed Interface Device 
*/
+#define ATAPI_CMD_PACKET 0xA0 /* Packed Command */
+
+
+#define ATAPI_CMD_INQUIRY 0x12
+#define ATAPI_CMD_REQ_SENSE 0x03
+#define ATAPI_CMD_READ_CAP 0x25
+#define ATAPI_CMD_START_STOP 0x1B
+#define ATAPI_CMD_READ_12 0xA8
+
+
+
+typedef struct ahci_control_regs {
+       uint32_t  cap;
+       uint32_t  ghc;
+       uint32_t  irqstatus;
+       uint32_t  impl;
+       uint32_t  version;
+} ahci_control_regs;
+
+typedef struct ahci_port_regs {
+       uint32_t lst_addr;
+       uint32_t lst_addr_hi;
+       uint32_t fis_addr;
+       uint32_t fis_addr_hi;
+       uint32_t irq_stat;
+       uint32_t irq_mask;
+       uint32_t cmd;
+       uint32_t unused0;
+       uint32_t tfdata;
+       uint32_t sig;
+       uint32_t scr_stat;
+       uint32_t scr_ctl;
+       uint32_t scr_err;
+       uint32_t scr_act;
+       uint32_t cmd_issue;
+} ahci_port_regs; 
+
+
+typedef struct ahci_cmd_hdr {
+       uint32_t                        opts;
+       uint32_t                        status;
+       uint32_t                        tbl_addr;
+       uint32_t                        tbl_addr_hi;
+       uint32_t                        reserved[4];
+} ahci_cmd_hdr;
+
+typedef struct ahci_sg {
+       uint32_t                        addr;
+       uint32_t                        addr_hi;
+       uint32_t                        reserved;
+       uint32_t                        flags_size;
+} ahci_sg;
+
+
+typedef struct AHCIState{
+       ahci_control_regs control_regs;
+       ahci_port_regs port_regs[2];
+       int mem;
+       QEMUTimer *timer;
+       IDEBus *ide;
+       ahci_sg *prdt_buf;
+       qemu_irq irq;
+} AHCIState;
+
+typedef struct ahci_pci_state {
+       PCIDevice card;
+       AHCIState *ahci;
+} ahci_pci_state;
+
+typedef struct ahci_sysbus_state {
+       SysBusDevice busdev;
+       AHCIState *ahci;
+} ahci_sysbus_state;
+
+static uint32_t  ahci_port_read(AHCIState *s,int port,int offset)
+{
+       uint32_t val;
+       uint32_t *p;
+       ahci_port_regs *pr;
+       pr=&s->port_regs[port];
+
+       switch(offset)
+       {
+               case PORT_SCR:
+                       if(s->ide && port==0) val=3;
+                       else val=0;
+                       break;
+               case PORT_IRQ_STAT:
+                       val=pr->irq_stat;
+                       break;
+               case PORT_TFDATA:
+
+               case PORT_SIG:
+
+               case PORT_CMD_ISSUE:
+
+
+               case PORT_SCR_CTL:
+
+               case PORT_SCR_ERR:
+
+               case PORT_SCR_ACT:
+               default:
+                       p=(uint32_t *)&s->port_regs[port];
+                       val= p[offset>>2];
+                       break;
+       }
+       return val;
+
+}
+
+static void ahci_check_irq(AHCIState *s)
+{
+       ahci_port_regs *pr;
+       int i;
+       for(i=0;i<2;i++)
+       {
+               pr=&s->port_regs[i];
+
+               if(pr->irq_stat&pr->irq_mask){
+                       s->control_regs.irqstatus |= (1<<i);
+               }
+       }
+
+       if(s->control_regs.irqstatus)
+               qemu_irq_raise(s->irq);
+       else qemu_irq_lower(s->irq);
+}
+
+
+static void  ahci_port_write(AHCIState *s,int port,int offset,uint32_t val)
+{
+       ahci_port_regs *pr=&s->port_regs[port];
+       uint32_t *p;
+
+       switch(offset)
+       {
+               case PORT_LST_ADDR:
+                       pr->lst_addr=val; break;
+
+               case PORT_LST_ADDR_HI:
+                       pr->lst_addr_hi=val; break;
+
+               case PORT_FIS_ADDR:
+                       pr->fis_addr = val; break;
+
+               case PORT_FIS_ADDR_HI:
+                       pr->fis_addr_hi = val; break;
+
+               case PORT_IRQ_STAT:
+                       pr->irq_stat &= ~val; 
+                       ahci_check_irq(s);
+                       break;
+
+               case PORT_IRQ_MASK:
+                       pr->irq_mask = val; 
+                       ahci_check_irq(s);
+                       break;
+
+               case PORT_CMD:
+                       
pr->cmd=val&(PORT_CMD_ATAPI|PORT_CMD_LIST_ON|PORT_CMD_FIS_ON|PORT_CMD_FIS_RX|PORT_CMD_CLO|PORT_CMD_POWER_ON|PORT_CMD_SPIN_UP|PORT_CMD_START);
+                       if(pr->cmd&PORT_CMD_START)
+                               
qemu_mod_timer(s->timer,qemu_get_clock(vm_clock)+muldiv64(1, 
get_ticks_per_sec(), 1000));
+                       break;
+
+
+               case PORT_CMD_ISSUE:
+                       pr->cmd_issue=val;
+                       if(pr->cmd&PORT_CMD_START)
+                               
qemu_mod_timer(s->timer,qemu_get_clock(vm_clock)+muldiv64(1, 
get_ticks_per_sec(), 1000));
+                       break;
+
+               case PORT_TFDATA:
+
+               case PORT_SIG:
+
+
+               case PORT_SCR:
+
+               case PORT_SCR_CTL:
+
+               case PORT_SCR_ERR:
+
+               case PORT_SCR_ACT:
+
+
+               default:
+                       p=(uint32_t *)pr;
+                       p[offset>>2]=val;
+                       break;
+       }
+
+}
+
+static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+       AHCIState *s = ptr;
+       uint32_t val;
+       uint32_t *p;
+       addr=addr&0xfff;
+       if(addr<0x20)
+       {
+               switch(addr)
+               {
+                       case HOST_IRQ_STAT:
+
+                       default:
+                               /* genernal host control */
+                               p=(uint32_t *)&s->control_regs;
+                               val=p[addr>>2];
+               }
+       }
+       else if(addr>=0x100 && addr<0x200)
+       {
+               val=ahci_port_read(s,(addr-0x100)>>7,addr&0x7f);
+       }
+       else val=0;
+
+
+       DPRINTF("ahci_mem_readl:  (addr 0x%08X), val 0x%08X\n", (unsigned) 
addr, val);
+
+       return val;
+}
+
+
+
+static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+       AHCIState *s = ptr;
+       uint32_t *p;
+       addr=addr&0xfff;
+
+       /* Only aligned reads are allowed on OHCI */
+       if (addr & 3) {
+               fprintf(stderr, "ahci: Mis-aligned write to addr 0x"
+                               TARGET_FMT_plx "\n", addr);
+               return;
+       }
+
+       if(addr<0x20)
+       {
+               switch(addr)
+               {
+                       case HOST_IRQ_STAT:
+                               s->control_regs.irqstatus &= ~val; 
+                               ahci_check_irq(s);
+                               break;
+                       default:
+                               /* genernal host control */
+                               p=(uint32_t *)&s->control_regs;
+               }
+       }
+       else if(addr>=0x100 && addr<0x200)
+       {
+               ahci_port_write(s,(addr-0x100)>>7,addr&0x7f,val);
+       }
+
+       DPRINTF("ahci_mem_writel:  (addr 0x%08X), val 0x%08X\n", (unsigned) 
addr, val);
+
+}
+
+static CPUReadMemoryFunc *ahci_readfn[3]={
+       ahci_mem_readl,
+       ahci_mem_readl,
+       ahci_mem_readl
+};
+
+static CPUWriteMemoryFunc *ahci_writefn[3]={
+       ahci_mem_writel,
+       ahci_mem_writel,
+       ahci_mem_writel
+};
+
+static void ahci_reg_init(AHCIState *s)
+{
+       s->control_regs.cap=2|(0x1f<<8); /*2 ports,32 cmd slot*/
+       s->control_regs.ghc=1<<31;
+       s->control_regs.impl=1;/*2 ports*/
+       s->control_regs.version=0x10100;
+}
+
+static void padstr(char *str, const char *src, int len)
+{
+       int i, v;
+       for(i = 0; i < len; i++) {
+               if (*src)
+                       v = *src++;
+               else
+                       v = ' ';
+               str[i^1] = v;
+       }
+}
+
+
+static void put_le16(uint16_t *p, unsigned int v)
+{
+       *p = cpu_to_le16(v);
+}
+
+
+static void ide_identify(IDEState *s)
+{
+       uint16_t *p;
+       unsigned int oldsize;
+
+       if (s->identify_set) {
+               memcpy(s->io_buffer, s->identify_data, 
sizeof(s->identify_data));
+               return;
+       }
+
+       memset(s->io_buffer, 0, 512);
+       p = (uint16_t *)s->io_buffer;
+       put_le16(p + 0, 0x0040);
+       put_le16(p + 1, s->cylinders);
+       put_le16(p + 3, s->heads);
+       put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+       put_le16(p + 5, 512); /* XXX: retired, remove ? */
+       put_le16(p + 6, s->sectors);
+       padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
+       put_le16(p + 20, 3); /* XXX: retired, remove ? */
+       put_le16(p + 21, 512); /* cache size in sectors */
+       put_le16(p + 22, 4); /* ecc bytes */
+       padstr((char *)(p + 23), s->version, 8); /* firmware version */
+       padstr((char *)(p + 27), "QEMU HARDDISK", 40); /* model */
+#if MAX_MULT_SECTORS > 1
+       put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
+#endif
+       put_le16(p + 48, 1); /* dword I/O */
+       put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA 
supported */
+       put_le16(p + 51, 0x200); /* PIO transfer cycle */
+       put_le16(p + 52, 0x200); /* DMA transfer cycle */
+       put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are 
valid */
+       put_le16(p + 54, s->cylinders);
+       put_le16(p + 55, s->heads);
+       put_le16(p + 56, s->sectors);
+       oldsize = s->cylinders * s->heads * s->sectors;
+       put_le16(p + 57, oldsize);
+       put_le16(p + 58, oldsize >> 16);
+       if (s->mult_sectors)
+               put_le16(p + 59, 0x100 | s->mult_sectors);
+       put_le16(p + 60, s->nb_sectors);
+       put_le16(p + 61, s->nb_sectors >> 16);
+       put_le16(p + 62, 0x07); /* single word dma0-2 supported */
+       put_le16(p + 63, 0x07); /* mdma0-2 supported */
+       put_le16(p + 65, 120);
+       put_le16(p + 66, 120);
+       put_le16(p + 67, 120);
+       put_le16(p + 68, 120);
+       put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+       put_le16(p + 81, 0x16); /* conforms to ata5 */
+       /* 14=NOP supported, 0=SMART supported */
+       put_le16(p + 82, (1 << 14) | 1);
+       /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+       put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+       /* 14=set to 1, 1=SMART self test, 0=SMART error logging */
+       put_le16(p + 84, (1 << 14) | 0);
+       /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */
+       if (bdrv_enable_write_cache(s->bs))
+               put_le16(p + 85, (1 << 14) | (1 << 5) | 1);
+       else
+               put_le16(p + 85, (1 << 14) | 1);
+       /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+       put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+       /* 14=set to 1, 1=smart self test, 0=smart error logging */
+       put_le16(p + 87, (1 << 14) | 0);
+       put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+       put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+       put_le16(p + 100, s->nb_sectors);
+       put_le16(p + 101, s->nb_sectors >> 16);
+       put_le16(p + 102, s->nb_sectors >> 32);
+       put_le16(p + 103, s->nb_sectors >> 48);
+
+       memcpy(s->identify_data, p, sizeof(s->identify_data));
+       s->identify_set = 1;
+}
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+static uint32_t write_to_sglist(uint8_t *buffer,uint32_t len,ahci_sg 
*sglist,uint32_t sgcount)
+{
+       uint32_t i=0;
+       uint32_t total=0,once;
+       for(i=0;len&&sgcount;i++)
+       {
+               once=min(sglist->flags_size+1,len);
+               cpu_physical_memory_write(sglist->addr,buffer,once);
+               sglist++;
+               sgcount--;
+               len -= once;
+               buffer += once;
+               total += once;
+       }
+
+       return total;
+}
+
+static uint32_t read_from_sglist(uint8_t *buffer,uint32_t len,ahci_sg 
*sglist,uint32_t sgcount)
+{
+       uint32_t i=0;
+       uint32_t total=0,once;
+       for(i=0;len&&sgcount;i++)
+       {
+               once=min(sglist->flags_size+1,len);
+               cpu_physical_memory_read(sglist->addr,buffer,once);
+               sglist++;
+               sgcount--;
+               len -= once;
+               buffer += once;
+               total += once;
+       }
+
+       return total;
+}
+
+static void handle_cmd(AHCIState *s,int port,int slot)
+{
+       int64_t sector_num;
+       int nb_sectors;
+       IDEState *ide_state;
+       int ret;
+       int cmdaddr;
+       uint8_t fis[0x80];
+       int cmd_len;
+       int prdt_num;
+       int i;
+       ahci_port_regs *pr;
+       ahci_cmd_hdr cmd_hdr;
+       pr=&s->port_regs[port];
+       cmdaddr=pr->lst_addr+slot*32;
+       cpu_physical_memory_read(cmdaddr,(uint8_t *)&cmd_hdr,16);
+       cmd_len=(cmd_hdr.opts&0x1f)*4;
+       cpu_physical_memory_read(cmd_hdr.tbl_addr,fis,cmd_len);
+       prdt_num=cmd_hdr.opts>>16;
+       if(prdt_num) cpu_physical_memory_read(cmd_hdr.tbl_addr+0x80,(uint8_t 
*)s->prdt_buf,prdt_num*32);
+
+
+       for(i=0;i<cmd_len;i++)
+       {
+               if((i&0xf)==0)DPRINTF("\n%02x:",i);
+               DPRINTF("%02x ",fis[i]);
+       }
+
+       switch(fis[0])
+       {
+               case 0x27:
+                       break;
+               default:
+                       hw_error("unkonow command fis[0]=%02x fis[1]=%02x 
fis[2]=%02x\n",fis[0],fis[1],fis[2]);break;
+       }
+
+       switch(fis[1])
+       {
+               case 1<<7: /* cmd fis */
+                       break;
+               case 0:
+                       break;
+               default:
+                       hw_error("unkonow command fis[0]=%02x fis[1]=%02x 
fis[2]=%02x\n",fis[0],fis[1],fis[2]);break;
+       }
+
+       if(fis[1]==0)
+       {
+
+       }
+
+       if(fis[1]==(1<<7))
+       {
+               if(!s->ide)hw_error("no ahci sata disk now\n");
+               ide_state=&s->ide->ifs[0];
+               switch(fis[2])
+               {
+                       case ATA_CMD_IDENT:
+                               ide_identify(ide_state);
+                               write_to_sglist(ide_state->identify_data, 
sizeof(ide_state->identify_data),s->prdt_buf,prdt_num);
+                               pr->irq_stat |= (1<<2);
+                               break;
+                       case WIN_SETFEATURES:
+                               pr->irq_stat |= (1<<2);
+                               break;
+                       case ATA_CMD_RD_DMA:
+                               
sector_num=(((int64_t)fis[10])<<40)|(((int64_t)fis[9])<<32)|(fis[8]<<24)|(fis[6]<<16)|(fis[5]<<8)|fis[4];
+                               nb_sectors=(fis[13]<<8)|fis[12];
+                               if(!nb_sectors)nb_sectors=256;
+                               
printf("nb_sectors=%x,prdt_num=%x\n",nb_sectors,prdt_num);
+                               ret = bdrv_read(ide_state->bs, sector_num, 
ide_state->io_buffer, nb_sectors);
+                               if(ret==0)
+                               {
+                                       
write_to_sglist(ide_state->io_buffer,nb_sectors*512,s->prdt_buf,prdt_num);
+                               }
+                               pr->irq_stat |= (1<<2);
+                               break;
+                       case ATA_CMD_WR_DMA:
+                               
sector_num=(((int64_t)fis[10])<<40)|(((int64_t)fis[9])<<32)|(fis[8]<<24)|(fis[6]<<16)|(fis[5]<<8)|fis[4];
+                               nb_sectors=(fis[13]<<8)|fis[12];
+                               if(!nb_sectors)nb_sectors=256;
+                               
read_from_sglist(ide_state->io_buffer,nb_sectors*512,s->prdt_buf,prdt_num);
+                               ret = bdrv_write(ide_state->bs, sector_num, 
ide_state->io_buffer, nb_sectors);
+                               pr->irq_stat |= (1<<2);
+                               break;
+                       default:
+                               hw_error("unkonow command fis[0]=%02x 
fis[1]=%02x fis[2]=%02x\n",fis[0],fis[1],fis[2]);break;
+               }
+
+       }
+
+       pr->cmd_issue &=~(1<<slot);
+       ahci_check_irq(s);
+}
+
+static void ahci_timer_function(void *opaque)
+{
+       AHCIState *s = opaque;
+       ahci_port_regs *pr;
+       int i,j;
+       for(i=0;i<2;i++)
+       {
+               pr=&s->port_regs[i];
+               for(j=0;j<32 && pr->cmd_issue;j++)
+               {
+                       if(pr->cmd_issue&(1<<j))
+                       {
+                               handle_cmd(s,i,j);
+                       }       
+               }
+       }
+}
+
+static AHCIState *ahci_new(void)
+{
+       DriveInfo *dinfo;
+       IDEBus  *bus = qemu_mallocz(sizeof(IDEBus));
+       AHCIState *s = qemu_mallocz(sizeof(AHCIState));
+       ahci_reg_init(s);
+       s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s);
+       s->timer = qemu_new_timer(vm_clock, ahci_timer_function, s);
+       s->prdt_buf = qemu_malloc(65535*32);
+
+       if ((dinfo = drive_get(IF_SD, 0, 0)) != NULL)
+       {
+               ide_init2(bus, dinfo, NULL,0);
+               s->ide=bus;
+       }
+       return s;
+}
+
+static void ahci_pci_map(PCIDevice *pci_dev, int region_num,
+               pcibus_t addr, pcibus_t size, int type)
+{
+       struct ahci_pci_state *d = (struct ahci_pci_state *)pci_dev;
+       AHCIState *s = d->ahci;
+
+       cpu_register_physical_memory(addr, size, s->mem);
+}
+
+#define PCI_VENDOR_MYDEVICE  0x8086
+#define PCI_PRODUCT_MYDEVICE 0x2652
+
+#define PCI_CLASS_HEADERTYPE_00h       0x00
+
+static int pci_ahci_init(PCIDevice *dev)
+{
+       struct ahci_pci_state *d;
+       d = DO_UPCAST(struct ahci_pci_state, card, dev);
+       pci_config_set_vendor_id(d->card.config,PCI_VENDOR_MYDEVICE);
+       pci_config_set_device_id(d->card.config,PCI_PRODUCT_MYDEVICE);
+       d->card.config[PCI_COMMAND]             = 0x07;         /* I/O + Memory 
*/
+       d->card.config[PCI_CLASS_DEVICE]        = 0;
+       d->card.config[0x0b]            = 1;//storage
+       d->card.config[0x0c]            = 0x08;         /* Cache line size */
+       d->card.config[0x0d]            = 0x40;         /* Latency timer */
+       d->card.config[0x0e]            = PCI_CLASS_HEADERTYPE_00h;
+       d->card.config[0x3d] = 1;    /* interrupt pin 0 */
+
+       pci_register_bar(&d->card, 5, 0x200,
+                       PCI_BASE_ADDRESS_SPACE_MEMORY, ahci_pci_map);
+       d->ahci=ahci_new();
+       d->ahci->irq = d->card.irq[0];
+       return 0;
+}
+
+static int pci_ahci_uninit(PCIDevice *dev)
+{
+       return 0;
+}
+
+static PCIDeviceInfo ahci_info = {
+       .qdev.name  = "ahci",
+       .qdev.size  = sizeof(ahci_pci_state),
+       .init       = pci_ahci_init,
+       .exit       = pci_ahci_uninit,
+       .qdev.props = (Property[]) {
+               DEFINE_PROP_END_OF_LIST(),
+       }
+};
+
+static void ahci_pci_register_devices(void)
+{
+       pci_qdev_register(&ahci_info);
+}
+
+device_init(ahci_pci_register_devices)
+
+
+
+static int ahci_sysbus_init(SysBusDevice *dev)
+{
+    ahci_sysbus_state *d = FROM_SYSBUS(ahci_sysbus_state, dev);
+       d->ahci=ahci_new();
+    sysbus_init_mmio(dev, 0x200, d->ahci->mem);
+    sysbus_init_irq(dev, &d->ahci->irq);
+    return 0;
+}
+
+static void ahci_sysbus_register_devices(void)
+{
+    sysbus_register_dev("ahci", sizeof(ahci_sysbus_state), ahci_sysbus_init);
+}
+
+device_init(ahci_sysbus_register_devices)
-- 
1.7.0.3.254.g4503b.dirty




reply via email to

[Prev in Thread] Current Thread [Next in Thread]