diff mbox

[1/6] add ahci support into qemu, only support sata disk.

Message ID 1273533547-21789-2-git-send-email-qiaochong@loongson.cn
State New
Headers show

Commit Message

QiaoChong May 10, 2010, 11:19 p.m. UTC
use -drive if=sd,file=diskname to add a ahci disk into qemu.

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

Patch

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 qiaochong@loongson.cn
+ *
+ * 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)