1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
/*
* Cisco router simulation platform.
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
*
* Cisco 2600 PCI controller.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "net.h"
#include "cpu.h"
#include "vm.h"
#include "dynamips.h"
#include "memory.h"
#include "device.h"
#include "net_io.h"
/* Debugging flags */
#define DEBUG_ACCESS 0
#define DEBUG_UNKNOWN 1
#define DEBUG_PCI 1
#define C2600_PCI_BRIDGE_VENDOR_ID 0x10ee
#define C2600_PCI_BRIDGE_PRODUCT_ID 0x4013
/* C2600 PCI controller */
struct c2600_pci_data {
char *name;
vm_obj_t vm_obj;
struct vdevice dev;
struct pci_device *pci_dev;
vm_instance_t *vm;
struct pci_bus *bus;
m_uint32_t bridge_bar0,bridge_bar1;
};
/*
* dev_c2600_pci_access()
*/
void *dev_c2600_pci_access(cpu_gen_t *cpu,struct vdevice *dev,
m_uint32_t offset,u_int op_size,u_int op_type,
m_uint64_t *data)
{
struct c2600_pci_data *d = dev->priv_data;
struct pci_device *pci_dev;
u_int bus,device,function,reg;
if (op_type == MTS_READ)
*data = 0x0;
bus = 0;
device = (offset >> 12) & 0x0F;
function = (offset >> 8) & 0x07;
reg = offset & 0xFF;
/* Find the corresponding PCI device */
pci_dev = pci_dev_lookup(d->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\n",
cpu_get_pc(cpu), bus, device, function, reg);
} else {
cpu_log(cpu,"PCI","write request (data=0x%8.8llx) at pc=0x%llx: "
"bus=%d,device=%d,function=%d,reg=0x%2.2x\n",
*data, cpu_get_pc(cpu), bus, device, function, reg);
}
#endif
if (!pci_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).\n",
cpu_get_pc(cpu), bus, device, function, reg);
} else {
cpu_log(cpu,"PCI","write request (data=0x%8.8llx) for unknown device "
"at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n",
*data, 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 (pci_dev->write_register != NULL)
pci_dev->write_register(cpu,pci_dev,reg,*data);
} else {
if (reg == PCI_REG_ID)
*data = (pci_dev->product_id << 16) | pci_dev->vendor_id;
else {
if (pci_dev->read_register != NULL)
*data = pci_dev->read_register(cpu,pci_dev,reg);
}
}
}
return NULL;
}
/* Shutdown the c2600 PCI controller device */
void dev_c2600_pci_shutdown(vm_instance_t *vm,struct c2600_pci_data *d)
{
if (d != NULL) {
/* Remove the device */
dev_remove(vm,&d->dev);
/* Free the structure itself */
free(d);
}
}
/* PCI bridge read access */
static m_uint32_t dev_c2600_pci_bridge_read(cpu_gen_t *cpu,
struct pci_device *dev,
int reg)
{
struct c2600_pci_data *d = dev->priv_data;
switch(reg) {
case 0x10:
return(d->bridge_bar0);
case 0x14:
return(d->bridge_bar1);
default:
return(0);
}
}
/* PCI bridge read access */
static void dev_c2600_pci_bridge_write(cpu_gen_t *cpu,struct pci_device *dev,
int reg,m_uint32_t value)
{
struct c2600_pci_data *d = dev->priv_data;
switch(reg) {
case 0x10:
/* BAR0 must be at 0x00000000 for correct RAM access */
if (value != 0x00000000) {
vm_error(d->vm,"C2600_PCI",
"Trying to set bridge BAR0 at 0x%8.8x!\n",
value);
}
d->bridge_bar0 = value;
break;
case 0x14:
/* BAR1 = byte swapped zone */
if (!d->bridge_bar1) {
d->bridge_bar1 = value;
/* XXX */
dev_bswap_init(d->vm,"pci_bswap",d->bridge_bar1,0x10000000,
0x00000000);
}
break;
}
}
/* Create the c2600 PCI controller device */
int dev_c2600_pci_init(vm_instance_t *vm,char *name,
m_uint64_t paddr,m_uint32_t len,
struct pci_bus *bus)
{
struct c2600_pci_data *d;
if (!(d = malloc(sizeof(*d)))) {
fprintf(stderr,"c2600_pci: unable to create device data.\n");
return(-1);
}
memset(d,0,sizeof(*d));
d->name = name;
d->vm = vm;
d->bus = bus;
vm_object_init(&d->vm_obj);
d->vm_obj.name = name;
d->vm_obj.data = d;
d->vm_obj.shutdown = (vm_shutdown_t)dev_c2600_pci_shutdown;
dev_init(&d->dev);
d->dev.name = name;
d->dev.priv_data = d;
d->dev.phys_addr = paddr;
d->dev.phys_len = len;
d->dev.handler = dev_c2600_pci_access;
pci_dev_add(d->bus,"pci_bridge",
C2600_PCI_BRIDGE_VENDOR_ID,C2600_PCI_BRIDGE_PRODUCT_ID,
15,0,-1,d,
NULL,
dev_c2600_pci_bridge_read,
dev_c2600_pci_bridge_write);
/* Map this device to the VM */
vm_bind_device(vm,&d->dev);
vm_object_add(vm,&d->vm_obj);
return(0);
}
|