Dynamips的PCI模擬

/* PCI device */
struct pci_device {
char *name;
u_int vendor_id,product_id;
int device,function,irq;
void *priv_data;

/* Parent bus */
struct pci_bus *pci_bus;

pci_init_t init;
pci_reg_read_t read_register;
pci_reg_write_t write_register;

struct pci_device *next,**pprev;
}
;

/* PCI bus */
struct pci_bus {
char *name;
m_uint32_t pci_addr;

/* Bus number */
int bus;

/* PCI device list on this bus */
struct pci_device *dev_list;

/* PCI bridges to access other busses */
struct pci_bridge *bridge_list;
}
;
PCI總線的數據結構

/* PCI bridge */
struct pci_bridge {
int pri_bus; /* Primary Bus */
int sec_bus; /* Secondary Bus */
int sub_bus; /* Subordinate Bus */

int skip_bus_check;

/* Bus configuration register */
m_uint32_t cfg_reg_bus;

/* PCI bridge device */
struct pci_device *pci_dev;

/* Secondary PCI bus */
struct pci_bus *pci_bus;

/* Fallback handlers to read/write config registers */
pci_reg_read_t fallback_read;
pci_reg_write_t fallback_write;

struct pci_bridge *next,**pprev;
}
;

struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name,
u_int vendor_id,u_int product_id,
int device,int function,
struct pci_bus *sec_bus,
pci_reg_read_t fallback_read,
pci_reg_write_t fallback_write)

pci_bridge_create_dev這個函數的功能是創建PCI橋設備,sec_bus這個入參指向下一級總線.

比如DEC21050橋芯片模擬就調用這個函數,

int dev_dec21050_init(struct pci_bus *pci_bus,int pci_device,
struct pci_bus *sec_bus)
{
struct pci_device *dev;

dev 
= pci_bridge_create_dev(pci_bus,"dec21050",
PCI_VENDOR_DEC,PCI_PRODUCT_DEC_21050,
pci_device,
0,sec_bus,NULL,NULL);
return((dev != NULL) ? 0 : -1);
}



/* Add a PCI device */
struct pci_device *
pci_dev_add(
struct pci_bus *pci_bus,char *name,
u_int vendor_id,u_int product_id,
int device,int function,int irq,
void *priv_data,pci_init_t init,
pci_reg_read_t read_register,
pci_reg_write_t write_register)

pci_dev_add這個函數創建了PCI設備.
比如
struct am79c971_data *
dev_am79c971_init(vm_instance_t 
*vm,char *name,int interface_type,
struct pci_bus *pci_bus,int pci_device,int irq)
{
struct am79c971_data *d;
struct pci_device *pci_dev;
struct vdevice *dev;

/* Allocate the private data structure for AM79C971 */
if (!(d = malloc(sizeof(*d)))) {
fprintf(stderr,
"%s (AM79C971): out of memory ",name);
return NULL;
}


memset(d,
0,sizeof(*d));
memcpy(d
->mii_regs[0],mii_reg_values,sizeof(mii_reg_values));
pthread_mutex_init(
&d->lock,NULL);

/* Add as PCI device */
pci_dev 
= pci_dev_add(pci_bus,name,
AM79C971_PCI_VENDOR_ID,AM79C971_PCI_PRODUCT_ID,
pci_device,
0,irq,
d,NULL,pci_am79c971_read,pci_am79c971_write);

if (!pci_dev) {
fprintf(stderr,
"%s (AM79C971): unable to create PCI device. ",name);
goto err_pci_dev;
}



AM79C971的讀寫函數被註冊爲pci_am79c971_read,pci_am79c971_write
實現如下:
static m_uint32_t pci_am79c971_read(cpu_gen_t *cpu,struct pci_device *dev,
int reg)
{
struct am79c971_data *d = dev->priv_data;

#if DEBUG_PCI_REGS
AM79C971_LOG(d,"read PCI register 0x%x/n",reg);
#endif

switch (reg) {
case 0x00:
return((AM79C971_PCI_PRODUCT_ID << 16) | AM79C971_PCI_VENDOR_ID);
case 0x08:
return(0x02000002);
case PCI_REG_BAR1:
return(d->dev->phys_addr);
default:
return(0);
}
}
再比如
/*
* pci_ap1011_read()
*
* Read a PCI register.
*/
static m_uint32_t pci_ap1011_read(cpu_gen_t *cpu,struct pci_device *dev,
int reg)
{
switch (reg) {
case 0x08:
return(0x06040000);
case 0x34:
return(0x00000040);
case 0x40:
return(0x00210008);
case 0x44:
return(0x00000020);
case 0x48:
return(0x000000C0);
default:
return(0);
}
}
這些實現函數就是模擬寄存器響應的實現.
那麼什麼時候會觸發寄存器的訪問?

是通過pci_dev_data_handler來處理的,這個函數會調用各個設備掛接在數據結構上的
讀寫函數來實現訪問

/*
* Handle the data register access.
*
* The address of requested register is first written at address 0xcf8
* (with pci_dev_addr_handler).
*
* The data is read/written at address 0xcfc.
*/

void pci_dev_data_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus,
u_int op_type,
int swap,m_uint64_t *data)
{
struct pci_device *dev;
int bus,device,function,reg;

if (op_type == MTS_READ)
*data = 0x0;

/*
http://www.mega-tokyo.com/osfaq2/index.php/PciSectionOfPentiumVme
*
* 31 : Enable Bit
* 30 - 24 : Reserved
* 23 - 16 : Bus Number
* 15 - 11 : Device Number
* 10 - 8 : Function Number
* 7 - 2 : Register Number
* 1 - 0 : always 00
*/

bus 
= GET_PCI_ADDR(16,0xff);
device 
= GET_PCI_ADDR(11,0x1f);
function 
= GET_PCI_ADDR(8,0x7);
reg 
= GET_PCI_ADDR(0,0xff);

/* Find the corresponding PCI device */
dev 
= pci_dev_lookup(pci_bus,bus,device,function);

#if DEBUG_PCI
if (op_type == MTS_READ) {
cpu_log(cpu,
"PCI","read request at pc=0x%llx: "
"bus=%d,device=%d,function=%d,reg=0x%2.2x ",
cpu_get_pc(cpu), bus, device, function, reg);
}
 else {
cpu_log(cpu,
"PCI","write request (data=0x%8.8x) at pc=0x%llx: "
"bus=%d,device=%d,function=%d,reg=0x%2.2x ",
pci_swap(
*data,swap), cpu_get_pc(cpu),
bus, device, function, reg);
}

#endif

if (!dev) {
if (op_type == MTS_READ) {
cpu_log(cpu,
"PCI","read request for unknown device at pc=0x%llx "
"(bus=%d,device=%d,function=%d,reg=0x%2.2x). ",
cpu_get_pc(cpu), bus, device, function, reg);
}
 else {
cpu_log(cpu,
"PCI","write request (data=0x%8.8x) for unknown device "
"at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x). ",
pci_swap(
*data,swap), cpu_get_pc(cpu),
bus, device, function, reg);
}


/* Returns an invalid device ID */
if ((op_type == MTS_READ) && (reg == PCI_REG_ID))
*data = 0xffffffff;
}
 else {
if (op_type == MTS_WRITE) {
if (dev->write_register != NULL)
dev
->write_register(cpu,dev,reg,pci_swap(*data,swap));
}
 else {
if (reg == PCI_REG_ID)
*data = pci_swap((dev->product_id << 16| dev->vendor_id,swap);
else {
if (dev->read_register != NULL)
*data = pci_swap(dev->read_register(cpu,dev,reg),swap);
}

}

}

}


dev_c2600_pci_init:
d
->dev.handler = dev_c2600_pci_access;

dev_c2600_pci_access:
switch(offset) {
case 0x500:
pci_dev_addr_handler(cpu,d
->bus,op_type,FALSE,data);
break;

case 0x504:
pci_dev_data_handler(cpu,d
->bus,op_type,FALSE,data);
break;



PCI的處理最後通過vm_bind_device綁定到VM對象上
也就是說vm->dev_array[i].handler(比如dev_c2600_pci_access)可以訪問到PCI處理函數

最後是 dev_access_fast 這個通用訪問函數調用了處理函數
/* device access function */
static forced_inline
void *dev_access_fast(cpu_gen_t *cpu,u_int dev_id,m_uint32_t offset,
u_int op_size,u_int op_type,m_uint64_t *data)
{
struct vdevice *dev = cpu->vm->dev_array[dev_id];

if (unlikely(!dev)) {
cpu_log(cpu,"dev_access_fast","null handler (dev_id=%u,offset=0x%x)/n",
dev_id,offset);
return NULL;
}

#if DEBUG_DEV_PERF_CNT
cpu->dev_access_counter++;
#endif

return(dev->handler(cpu,dev,offset,op_size,op_type,data));
}

mips64_mts64_access
mips64_mts32_access
ppc32_mem_access
這三個函數調用了dev_access_fast

以ppc32_mem_access(訪問DCACHE時的等效宏定義是PPC32_MEM_DACCESS)爲例,看有哪些函數調用了
ppc32_lbz
ppc32_lhz
ppc32_lwz
ppc32_lwbr等內存指令

另外一處調用在ppc32_mem_lookup(虛擬地址查找)
掛接在cpu->mem_op_lookup = ppc32_mem_lookup;

有ppc32_exec_fetch等函數使用了cpu->mem_op_lookup.

還有一些問題:
1.怎麼知道哪些地址對應哪個訪問函數?



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章