From patchwork Mon May 10 23:19:02 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: QiaoChong X-Patchwork-Id: 52250 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 34964B7D61 for ; Tue, 11 May 2010 09:30:34 +1000 (EST) Received: from localhost ([127.0.0.1]:35081 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OBcQ1-0001vZ-Jh for incoming@patchwork.ozlabs.org; Mon, 10 May 2010 19:29:49 -0400 Received: from [140.186.70.92] (port=42295 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OBcGF-00030e-5P for qemu-devel@nongnu.org; Mon, 10 May 2010 19:19:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OBcGC-0007Zw-U7 for qemu-devel@nongnu.org; Mon, 10 May 2010 19:19:43 -0400 Received: from [159.226.40.154] (port=42994 helo=mail.loongson.cn) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OBcGB-0007XP-Ns for qemu-devel@nongnu.org; Mon, 10 May 2010 19:19:40 -0400 Received: from localhost.localdomain (unknown [10.2.1.99]) by mail.loongson.cn (Postfix) with ESMTPA id 811B6C6231; Tue, 11 May 2010 07:19:23 +0800 (CST) From: QiaoChong To: qemu-devel@nongnu.org Date: Tue, 11 May 2010 07:19:02 +0800 Message-Id: <1273533547-21789-2-git-send-email-qiaochong@loongson.cn> X-Mailer: git-send-email 1.7.0.3.254.g4503b.dirty In-Reply-To: <1273533547-21789-1-git-send-email-qiaochong@loongson.cn> References: <1273533547-21789-1-git-send-email-qiaochong@loongson.cn> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 1) Cc: QiaoChong , joro@8bytes.org, herbszt@gmx.de, agraf@suse.de, elek.roland@gmail.com Subject: [Qemu-devel] [PATCH 1/6] add ahci support into qemu, only support sata disk. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org use -drive if=sd,file=diskname to add a ahci disk into qemu. Signed-off-by: QiaoChong --- 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 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 . + * + * 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 +#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<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;iide)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<port_regs[i]; + for(j=0;j<32 && pr->cmd_issue;j++) + { + if(pr->cmd_issue&(1<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)