|
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:135219906142009年 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
[Prev in Thread] | Current Thread | [Next in Thread] |