File: dev_c2600_pci.c

package info (click to toggle)
dynamips 0.2.7-0.2.8RC2-2
  • links: PTS
  • area: non-free
  • in suites: lenny
  • size: 3,840 kB
  • ctags: 9,893
  • sloc: ansic: 69,846; makefile: 239; sh: 124; perl: 20
file content (205 lines) | stat: -rw-r--r-- 5,560 bytes parent folder | download | duplicates (5)
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);
}