[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] add ahci support into qemu
From: |
乔崇 |
Subject: |
[Qemu-devel] add ahci support into qemu |
Date: |
Fri, 30 Apr 2010 20:48:35 +0800 |
User-agent: |
Mozilla-Thunderbird 2.0.0.22 (X11/20090706) |
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 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 "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];
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] add ahci support into qemu,
乔崇 <=