All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] add ahci support into qemu
@ 2010-04-30 12:48 乔崇
  2010-05-02  8:05 ` Alexander Graf
  0 siblings, 1 reply; 17+ messages in thread
From: 乔崇 @ 2010-04-30 12:48 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 287 bytes --]

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


[-- Attachment #2: ahci.c --]
[-- Type: text/x-csrc, Size: 21266 bytes --]

/*
 * 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 "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];
}


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-05-10 11:25 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-30 12:48 [Qemu-devel] add ahci support into qemu 乔崇
2010-05-02  8:05 ` Alexander Graf
2010-05-02 12:52   ` [Qemu-devel] [RFC] [PATCH] " 乔崇
2010-05-02 15:36     ` [Qemu-devel] " Sebastian Herbszt
2010-05-02 15:49       ` Avi Kivity
2010-05-02 15:56       ` Elek Roland
2010-05-03 21:08         ` Elek Roland
2010-05-04 20:56           ` Sebastian Herbszt
2010-05-02 22:13     ` Sebastian Herbszt
2010-05-04  1:07       ` 乔崇
2010-05-04 20:51         ` Sebastian Herbszt
2010-05-05 19:37           ` Stuart Brady
2010-05-06 19:10             ` [Qemu-devel] " Sebastian Herbszt
2010-05-04  0:01     ` [Qemu-devel] " Alexander Graf
2010-05-04  0:28       ` 乔崇
2010-05-09 19:16         ` Alexander Graf
2010-05-10 11:24           ` 乔崇

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.