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. -- 乔崇 qiaochong.ac.cn 龙芯技术服务中心 office:010-62600855-108 mobile:13521990614 2009年 11月 16日 星期一 10:31:04 CST
/* * QEMU AHCI Emulation * Copyright (c) 2010 qiaoch...@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 "pci.h" #include "dma.h" #include "cpu-common.h" #include <hw/ide/internal.h> #define DPRINTF(...) //printf 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 AHCIState{ ahci_control_regs control_regs; ahci_port_regs port_regs[2]; int mem; QEMUTimer *timer; IDEBus *ide; qemu_irq irq; } AHCIState; 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_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; struct ahci_pci_state { PCIDevice card; AHCIState *ahci; }; 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=&s->port_regs[port]; val= p[offset>>2]; break; } return val; } 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 uint32_t ahci_port_write(AHCIState *s,int port,int offset,uint32_t val) { ahci_port_regs *pr=&s->port_regs[port]; uint32_t *p; static int64_t time; 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=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,offset; uint32_t *p; addr=addr&0xfff; if(addr<0x20) { switch(addr) { case HOST_IRQ_STAT: default: /* genernal host control */ p=&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 offset; uint32_t *p; addr=addr&0xfff; int i; /* 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=&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 }; 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 padstr8(uint8_t *buf, int buf_size, const char *src) { int i; for(i = 0; i < buf_size; i++) { if (*src) buf[i] = *src++; else buf[i] = ' '; } } 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)) uint32_t write_to_sglist(char *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,len); cpu_physical_memory_write(sglist->addr,buffer,once); sglist++; sgcount--; len -=once; buffer += once; total += once; } return total; } uint32_t read_from_sglist(char *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,len); cpu_physical_memory_read(sglist->addr,buffer,once); sglist++; sgcount--; len -=once; buffer += once; total += once; } return total; } 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; ahci_sg *prdt_buf; 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,&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; prdt_buf=qemu_malloc(prdt_num*32); cpu_physical_memory_read(cmd_hdr.tbl_addr+0x80,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; defult: 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),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=(fis[6]<<16)|(fis[5]<<8)|fis[4]; nb_sectors=(fis[13]<<8)|fis[12]; if(!nb_sectors)nb_sectors=256; 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,prdt_buf,prdt_num); } pr->irq_stat |= (1<<2); break; case ATA_CMD_WR_DMA: sector_num=(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,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); qemu_free(prdt_buf); } 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); } } } } AHCIState *ahci_new() { int mem; 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); if ((dinfo = drive_get(IF_IDE, 0, 0)) != NULL) { ide_init2(bus, dinfo, NULL,0); s->ide=bus; } return s; } int ahci_platform(unsigned long addr,unsigned long size,qemu_irq irq) { AHCIState *s; s=ahci_new(); s->irq =irq; cpu_register_physical_memory(addr, size, s->mem); } static void ahci_pci_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { int iomemtype; 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 void pci_ahci_init(PCIBus *bus) { struct ahci_pci_state *d; d = (struct ahci_pci_state *) pci_register_device(bus, "QEMUware AHCI", sizeof(struct ahci_pci_state), -1, 0, 0); d->card.config[PCI_VENDOR_ID] = PCI_VENDOR_MYDEVICE & 0xff; d->card.config[PCI_VENDOR_ID + 1] = PCI_VENDOR_MYDEVICE >> 8; d->card.config[PCI_DEVICE_ID] = PCI_PRODUCT_MYDEVICE & 0xff; d->card.config[PCI_DEVICE_ID + 1] = PCI_PRODUCT_MYDEVICE >> 8; 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]; }