qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [RFC] [PATCH] add ahci support into qemu


From: 乔崇
Subject: [Qemu-devel] [RFC] [PATCH] add ahci support into qemu
Date: Sun, 02 May 2010 20:52:38 +0800
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20090706)

Hi,Alexander Graf.
 
  I am very glad you noticed my patch about ahci.I love qemu just like I love linux.I wish I could do much more work on qemu development.

I had cloned qemu from master branch,add my patch into it,and tested ahci on i386 softmmu.

If anyone is interested on ahci,you can test my patch like this:

git-clone -ls git://git.savannah.nongnu.org/qemu
patch -p1 -i 0001-add-ahci-support-into-qemu-only-support-sata-disk.patch
patch -p1 -i 0002-add-ahci-device-into-i386-pc-just-for-test.patch
./configure --target-list=i386-softmmu
make

dd if=/dev/zero of=/tmp/disk bs=1M count=100
./i386-softmmu/qemu  -cdrom KNOPPIX_V6.0.1CD-2009-02-08-EN.iso -boot d -drive if=sd,file=/tmp/disk

After linux boot,you will find a ahci device named sda.

Now this patch only support sata disk.
Most ahci registers and operations which are not necessary on linux are ignored.
Now this patch support disk identify,dma read,dma write,ignore other opertions.

By the way how you send patch to mail list?


Alexander Graf 写道:
On 30.04.2010, at 14:48, 乔崇 wrote:

  
Hi,everyone!
I just add AHCI Emulation into qemu.
I have tested it on linux kernel,it works well.
run like this:
qemu -hda disk ...

Now only support sata disk.
    


Hi Chong,

While I haven't compiled and tried it out myself, it's amazing to see someone did work on this already. We currently have a GSoC project going on to implement AHCI, so this seems like an amazing kickstart.

Elek, could you please look over this patch, comment on it and evaluate how well it looks? I'll do the same as soon as time permits (on vacation atm).

Chong, at first glace I see some formal issues:

  1) Use the unified diff format for all of the patch / patches (diff -ur or git format-patch)
  2) Add a "Signed-off-by: Chong Qiao <address@hidden>" line to the patch description
  3) Split up the patch for easier review.
  4) Add "[RFC] [PATCH]" to the subject line of the mail


Alex




  


--
乔崇 qiaochong.ac.cn
龙芯技术服务中心
office:010-62600855-108
mobile:13521990614
2009年 11月 16日 星期一 10:31:04 CST
>From 2844d2ea0ead3bcf5382e578a2d658902b0314c8 Mon Sep 17 00:00:00 2001
From: QiaoChong <address@hidden>
Date: Sun, 2 May 2010 20:07:32 +0800
Subject: [PATCH] add ahci support into qemu,only support sata disk.

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 65beed5..189a776 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -186,6 +186,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.5.6.5

>From 446cce5657bf570fc612dea513f5bedcdc3be275 Mon Sep 17 00:00:00 2001
From: QiaoChong <address@hidden>
Date: Sun, 2 May 2010 16:53:43 +0800
Subject: [PATCH] add ahci device into i386 pc just for test.

test like this:

dd if=/dev/zero of=/tmp/disk bs=1M count=100
./i386-softmmu/qemu  -cdrom 
/mnt/hdb1/knoppix-dvd/KNOPPIX_V6.0.1CD-2009-02-08-EN.iso -boot d -drive 
if=sd,file=/tmp/disk

Signed-off-by: QiaoChong <address@hidden>
---
 default-configs/i386-softmmu.mak |    2 ++
 hw/pc.c                          |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 4c1495f..bd72f39 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -20,3 +20,5 @@ CONFIG_NE2000_ISA=y
 CONFIG_PIIX_PCI=y
 CONFIG_SOUND=y
 CONFIG_VIRTIO_PCI=y
+CONFIG_AHCI=y
+
diff --git a/hw/pc.c b/hw/pc.c
index b659344..7ea437f 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1005,6 +1005,7 @@ static void pc_init1(ram_addr_t ram_size,
 
     if (pci_enabled) {
         pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
+               pci_create_simple(pci_bus,-1,"ahci");
     } else {
         for(i = 0; i < MAX_IDE_BUS; i++) {
             isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
-- 
1.5.6.5


reply via email to

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