/[dynamips]/upstream/dynamips-0.2.5/pci_dev.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/dynamips-0.2.5/pci_dev.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Sat Oct 6 16:01:44 2007 UTC (12 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 14929 byte(s)
import 0.2.5 from upstream

1 /*
2 * Cisco 7200 (Predator) simulation platform.
3 * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4 *
5 * PCI devices.
6 *
7 * Very interesting docs:
8 * http://www.science.unitn.it/~fiorella/guidelinux/tlk/node72.html
9 * http://www.science.unitn.it/~fiorella/guidelinux/tlk/node76.html
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17
18 #include "mips64.h"
19 #include "dynamips.h"
20 #include "memory.h"
21 #include "device.h"
22 #include "vm.h"
23
24 #define DEBUG_PCI 1
25
26 #define GET_PCI_ADDR(offset,mask) ((pci_bus->pci_addr >> offset) & mask)
27
28 /* Trigger a PCI device IRQ */
29 void pci_dev_trigger_irq(vm_instance_t *vm,struct pci_device *dev)
30 {
31 if (dev->irq != -1)
32 vm_set_irq(vm,dev->irq);
33 }
34
35 /* Clear a PCI device IRQ */
36 void pci_dev_clear_irq(vm_instance_t *vm,struct pci_device *dev)
37 {
38 if (dev->irq != -1)
39 vm_clear_irq(vm,dev->irq);
40 }
41
42 /* Swapping function */
43 static inline m_uint32_t pci_swap(m_uint32_t val,int swap)
44 {
45 return((swap) ? swap32(val) : val);
46 }
47
48 /* PCI bus lookup */
49 struct pci_bus *pci_bus_lookup(struct pci_bus *pci_bus_root,int bus)
50 {
51 struct pci_bus *next_bus,*cur_bus = pci_bus_root;
52 struct pci_bridge *bridge;
53
54 while(cur_bus != NULL) {
55 if (cur_bus->bus == bus)
56 return cur_bus;
57
58 /* Try busses behind PCI bridges */
59 next_bus = NULL;
60
61 for(bridge=cur_bus->bridge_list;bridge;bridge=bridge->next) {
62 /*
63 * Specific case: final bridge with no checking of secondary
64 * bus number. Dynamically programming.
65 */
66 if (bridge->skip_bus_check) {
67 pci_bridge_set_bus_info(bridge,cur_bus->bus,bus,bus);
68 bridge->skip_bus_check = FALSE;
69 return bridge->pci_bus;
70 }
71
72 if ((bus >= bridge->sec_bus) && (bus <= bridge->sub_bus)) {
73 next_bus = bridge->pci_bus;
74 break;
75 }
76 }
77
78 cur_bus = next_bus;
79 }
80
81 return NULL;
82 }
83
84 /* PCI device local lookup */
85 struct pci_device *pci_dev_lookup_local(struct pci_bus *pci_bus,
86 int device,int function)
87 {
88 struct pci_device *dev;
89
90 for(dev=pci_bus->dev_list;dev;dev=dev->next)
91 if ((dev->device == device) && (dev->function == function))
92 return dev;
93
94 return NULL;
95 }
96
97 /* PCI Device lookup */
98 struct pci_device *pci_dev_lookup(struct pci_bus *pci_bus_root,
99 int bus,int device,int function)
100 {
101 struct pci_bus *req_bus;
102
103 /* Find, try to find the request bus */
104 if (!(req_bus = pci_bus_lookup(pci_bus_root,bus)))
105 return NULL;
106
107 /* Walk through devices present on this bus */
108 return pci_dev_lookup_local(req_bus,device,function);
109 }
110
111 /* Handle the address register access */
112 void pci_dev_addr_handler(cpu_mips_t *cpu,struct pci_bus *pci_bus,
113 u_int op_type,int swap,m_uint64_t *data)
114 {
115 if (op_type == MTS_WRITE)
116 pci_bus->pci_addr = pci_swap(*data,swap);
117 else
118 *data = pci_swap(pci_bus->pci_addr,swap);
119 }
120
121 /*
122 * Handle the data register access.
123 *
124 * The address of requested register is first written at address 0xcf8
125 * (with pci_dev_addr_handler).
126 *
127 * The data is read/written at address 0xcfc.
128 */
129 void pci_dev_data_handler(cpu_mips_t *cpu,struct pci_bus *pci_bus,
130 u_int op_type,int swap,m_uint64_t *data)
131 {
132 struct pci_device *dev;
133 int bus,device,function,reg;
134
135 if (op_type == MTS_READ)
136 *data = 0;
137
138 /*
139 * http://www.mega-tokyo.com/osfaq2/index.php/PciSectionOfPentiumVme
140 *
141 * 31 : Enable Bit
142 * 30 - 24 : Reserved
143 * 23 - 16 : Bus Number
144 * 15 - 11 : Device Number
145 * 10 - 8 : Function Number
146 * 7 - 2 : Register Number
147 * 1 - 0 : always 00
148 */
149 bus = GET_PCI_ADDR(16,0xff);
150 device = GET_PCI_ADDR(11,0x1f);
151 function = GET_PCI_ADDR(8,0x7);
152 reg = GET_PCI_ADDR(0,0xff);
153
154 /* Find the corresponding PCI device */
155 dev = pci_dev_lookup(pci_bus,bus,device,function);
156
157 #if DEBUG_PCI
158 if (op_type == MTS_READ) {
159 cpu_log(cpu,"PCI","read request at pc=0x%llx: "
160 "bus=%d,device=%d,function=%d,reg=0x%2.2x\n",
161 cpu->pc, bus, device, function, reg);
162 } else {
163 cpu_log(cpu,"PCI","write request (data=0x%8.8x) at pc=0x%llx: "
164 "bus=%d,device=%d,function=%d,reg=0x%2.2x\n",
165 pci_swap(*data,swap), cpu->pc, bus, device, function, reg);
166 }
167 #endif
168
169 if (!dev) {
170 if (op_type == MTS_READ) {
171 cpu_log(cpu,"PCI","read request for unknown device at pc=0x%llx "
172 "(bus=%d,device=%d,function=%d,reg=0x%2.2x).\n",
173 cpu->pc, bus, device, function, reg);
174 } else {
175 cpu_log(cpu,"PCI","write request (data=0x%8.8x) for unknown device "
176 "at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n",
177 pci_swap(*data,swap), cpu->pc, bus, device, function, reg);
178 }
179
180 /* Returns an invalid device ID */
181 if ((op_type == MTS_READ) && (reg == PCI_REG_ID))
182 *data = 0xffffffff;
183 } else {
184 if (op_type == MTS_WRITE) {
185 if (dev->write_register != NULL)
186 dev->write_register(cpu,dev,reg,pci_swap(*data,swap));
187 } else {
188 if (reg == PCI_REG_ID)
189 *data = pci_swap((dev->product_id << 16) | dev->vendor_id,swap);
190 else {
191 if (dev->read_register != NULL)
192 *data = pci_swap(dev->read_register(cpu,dev,reg),swap);
193 }
194 }
195 }
196 }
197
198 /* Add a PCI bridge */
199 struct pci_bridge *pci_bridge_add(struct pci_bus *pci_bus)
200 {
201 struct pci_bridge *bridge;
202
203 if (!pci_bus)
204 return NULL;
205
206 if (!(bridge = malloc(sizeof(*bridge)))) {
207 fprintf(stderr,"pci_bridge_add: unable to create new PCI bridge.\n");
208 return NULL;
209 }
210
211 memset(bridge,0,sizeof(*bridge));
212 bridge->pri_bus = pci_bus->bus;
213 bridge->sec_bus = -1;
214 bridge->sub_bus = -1;
215 bridge->pci_bus = NULL;
216
217 /* Insert the bridge in the double-linked list */
218 bridge->next = pci_bus->bridge_list;
219 bridge->pprev = &pci_bus->bridge_list;
220
221 if (pci_bus->bridge_list != NULL)
222 pci_bus->bridge_list->pprev = &bridge->next;
223
224 pci_bus->bridge_list = bridge;
225 return bridge;
226 }
227
228 /* Remove a PCI bridge from the double-linked list */
229 static inline void pci_bridge_remove_from_list(struct pci_bridge *bridge)
230 {
231 if (bridge->next)
232 bridge->next->pprev = bridge->pprev;
233
234 if (bridge->pprev)
235 *(bridge->pprev) = bridge->next;
236 }
237
238 /* Remove a PCI bridge */
239 void pci_bridge_remove(struct pci_bridge *bridge)
240 {
241 if (bridge != NULL) {
242 pci_bridge_remove_from_list(bridge);
243 free(bridge);
244 }
245 }
246
247 /* Map secondary bus to a PCI bridge */
248 void pci_bridge_map_bus(struct pci_bridge *bridge,struct pci_bus *pci_bus)
249 {
250 if (bridge != NULL) {
251 bridge->pci_bus = pci_bus;
252
253 if (bridge->pci_bus != NULL)
254 bridge->pci_bus->bus = bridge->sec_bus;
255 }
256 }
257
258 /* Set PCI bridge bus info */
259 void pci_bridge_set_bus_info(struct pci_bridge *bridge,
260 int pri_bus,int sec_bus,int sub_bus)
261 {
262 if (bridge != NULL) {
263 bridge->pri_bus = pri_bus;
264 bridge->sec_bus = sec_bus;
265 bridge->sub_bus = sub_bus;
266
267 if (bridge->pci_bus != NULL)
268 bridge->pci_bus->bus = bridge->sec_bus;
269 }
270 }
271
272 /* Add a PCI device */
273 struct pci_device *
274 pci_dev_add(struct pci_bus *pci_bus,char *name,
275 u_int vendor_id,u_int product_id,
276 int device,int function,int irq,
277 void *priv_data,pci_init_t init,
278 pci_reg_read_t read_register,
279 pci_reg_write_t write_register)
280 {
281 struct pci_device *dev;
282
283 if (!pci_bus)
284 return NULL;
285
286 if ((dev = pci_dev_lookup_local(pci_bus,device,function)) != NULL) {
287 fprintf(stderr,"pci_dev_add: bus %s, device %d, function %d already "
288 "registered (device '%s').\n",
289 pci_bus->name,device,function,dev->name);
290 return NULL;
291 }
292
293 /* we can create safely the new device */
294 if (!(dev = malloc(sizeof(*dev)))) {
295 fprintf(stderr,"pci_dev_add: unable to create new PCI device.\n");
296 return NULL;
297 }
298
299 memset(dev,0,sizeof(*dev));
300 dev->name = name;
301 dev->vendor_id = vendor_id;
302 dev->product_id = product_id;
303 dev->pci_bus = pci_bus;
304 dev->device = device;
305 dev->function = function;
306 dev->irq = irq;
307 dev->priv_data = priv_data;
308 dev->init = init;
309 dev->read_register = read_register;
310 dev->write_register = write_register;
311
312 /* Insert the device in the double-linked list */
313 dev->next = pci_bus->dev_list;
314 dev->pprev = &pci_bus->dev_list;
315
316 if (pci_bus->dev_list != NULL)
317 pci_bus->dev_list->pprev = &dev->next;
318
319 pci_bus->dev_list = dev;
320
321 if (init) init(dev);
322 return dev;
323 }
324
325 /* Add a basic PCI device that just returns a Vendor/Product ID */
326 struct pci_device *
327 pci_dev_add_basic(struct pci_bus *pci_bus,
328 char *name,u_int vendor_id,u_int product_id,
329 int device,int function)
330 {
331 return(pci_dev_add(pci_bus,name,vendor_id,product_id,
332 device,function,-1,NULL,
333 NULL,NULL,NULL));
334 }
335
336 /* Remove a device from the double-linked list */
337 static inline void pci_dev_remove_from_list(struct pci_device *dev)
338 {
339 if (dev->next)
340 dev->next->pprev = dev->pprev;
341
342 if (dev->pprev)
343 *(dev->pprev) = dev->next;
344 }
345
346 /* Remove a PCI device */
347 void pci_dev_remove(struct pci_device *dev)
348 {
349 if (dev != NULL) {
350 pci_dev_remove_from_list(dev);
351 free(dev);
352 }
353 }
354
355 /* Remove a PCI device given its ID (bus,device,function) */
356 int pci_dev_remove_by_id(struct pci_bus *pci_bus,
357 int bus,int device,int function)
358 {
359 struct pci_device *dev;
360
361 if (!(dev = pci_dev_lookup(pci_bus,bus,device,function)))
362 return(-1);
363
364 pci_dev_remove(dev);
365 return(0);
366 }
367
368 /* Remove a PCI device given its name */
369 int pci_dev_remove_by_name(struct pci_bus *pci_bus,char *name)
370 {
371 struct pci_device *dev,*next;
372 int count = 0;
373
374 for(dev=pci_bus->dev_list;dev;dev=next) {
375 next = dev->next;
376
377 if (!strcmp(dev->name,name)) {
378 pci_dev_remove(dev);
379 count++;
380 }
381 }
382
383 return(count);
384 }
385
386 /* Create a PCI bus */
387 struct pci_bus *pci_bus_create(char *name,int bus)
388 {
389 struct pci_bus *d;
390
391 if (!(d = malloc(sizeof(*d)))) {
392 fprintf(stderr,"pci_bus_create: unable to create PCI info.\n");
393 return NULL;
394 }
395
396 memset(d,0,sizeof(*d));
397 d->name = strdup(name);
398 d->bus = bus;
399 return d;
400 }
401
402 /* Delete a PCI bus */
403 void pci_bus_remove(struct pci_bus *pci_bus)
404 {
405 struct pci_device *dev,*next;
406 struct pci_bridge *bridge,*next_bridge;
407
408 if (pci_bus) {
409 /* Remove all devices */
410 for(dev=pci_bus->dev_list;dev;dev=next) {
411 next = dev->next;
412 free(dev);
413 }
414
415 /* Remove all bridges */
416 for(bridge=pci_bus->bridge_list;bridge;bridge=next_bridge) {
417 next_bridge = bridge->next;
418 free(bridge);
419 }
420
421 /* Free the structure itself */
422 free(pci_bus->name);
423 free(pci_bus);
424 }
425 }
426
427 /* Read a configuration register of a PCI bridge */
428 static m_uint32_t pci_bridge_read_reg(cpu_mips_t *cpu,struct pci_device *dev,
429 int reg)
430 {
431 struct pci_bridge *bridge = dev->priv_data;
432 m_uint32_t val = 0;
433
434 switch(reg) {
435 case 0x18:
436 return(bridge->cfg_reg_bus);
437 default:
438 if (bridge->fallback_read != NULL)
439 val = bridge->fallback_read(cpu,dev,reg);
440
441 /* Returns appropriate PCI bridge class code if nothing defined */
442 if ((reg == 0x08) && !val)
443 val = 0x06040000;
444
445 return(val);
446 }
447 }
448
449 /* Write a configuration register of a PCI bridge */
450 static void pci_bridge_write_reg(cpu_mips_t *cpu,struct pci_device *dev,
451 int reg,m_uint32_t value)
452 {
453 struct pci_bridge *bridge = dev->priv_data;
454 u_int pri_bus,sec_bus,sub_bus;
455
456 switch(reg) {
457 case 0x18:
458 bridge->cfg_reg_bus = value;
459 sub_bus = (value >> 16) & 0xFF;
460 sec_bus = (value >> 8) & 0xFF;
461 pri_bus = value & 0xFF;
462
463 /* Modify the PCI bridge settings */
464 vm_log(cpu->vm,"PCI",
465 "PCI bridge %d,%d,%d -> pri: %2.2u, sec: %2.2u, sub: %2.2u\n",
466 dev->pci_bus->bus,dev->device,dev->function,
467 pri_bus,sec_bus,sub_bus);
468
469 pci_bridge_set_bus_info(bridge,pri_bus,sec_bus,sub_bus);
470 break;
471
472 default:
473 if (bridge->fallback_write != NULL)
474 bridge->fallback_write(cpu,dev,reg,value);
475 }
476 }
477
478 /* Create a PCI bridge device */
479 struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name,
480 u_int vendor_id,u_int product_id,
481 int device,int function,
482 struct pci_bus *sec_bus,
483 pci_reg_read_t fallback_read,
484 pci_reg_write_t fallback_write)
485 {
486 struct pci_bridge *bridge;
487 struct pci_device *dev;
488
489 /* Create the PCI bridge structure */
490 if (!(bridge = pci_bridge_add(pci_bus)))
491 return NULL;
492
493 /* Create the PCI device corresponding to the bridge */
494 dev = pci_dev_add(pci_bus,name,vendor_id,product_id,device,function,-1,
495 bridge,NULL,pci_bridge_read_reg,pci_bridge_write_reg);
496
497 if (!dev)
498 goto err_pci_dev;
499
500 /* Keep the associated PCI device for this bridge */
501 bridge->pci_dev = dev;
502
503 /* Set the fallback functions */
504 bridge->fallback_read = fallback_read;
505 bridge->fallback_write = fallback_write;
506
507 /* Map the secondary bus (disabled at startup) */
508 pci_bridge_map_bus(bridge,sec_bus);
509 return dev;
510
511 err_pci_dev:
512 pci_bridge_remove(bridge);
513 return NULL;
514 }
515
516 /* Show PCI device list of the specified bus */
517 static void pci_bus_show_dev_list(struct pci_bus *pci_bus)
518 {
519 struct pci_device *dev;
520 struct pci_bridge *bridge;
521 char bus_id[32];
522
523 if (!pci_bus)
524 return;
525
526 if (pci_bus->bus != -1) {
527 snprintf(bus_id,sizeof(bus_id),"%2d",pci_bus->bus);
528 } else {
529 strcpy(bus_id,"XX");
530 }
531
532 for(dev=pci_bus->dev_list;dev;dev=dev->next) {
533 printf(" %-18s: ID %4.4x:%4.4x, Bus %s, Dev. %2d, Func. %2d",
534 dev->name,dev->vendor_id,dev->product_id,
535 bus_id,dev->device,dev->function);
536
537 if (dev->irq != -1)
538 printf(", IRQ: %d\n",dev->irq);
539 else
540 printf("\n");
541 }
542
543 for(bridge=pci_bus->bridge_list;bridge;bridge=bridge->next)
544 pci_bus_show_dev_list(bridge->pci_bus);
545 }
546
547 /* Show PCI device list */
548 void pci_dev_show_list(struct pci_bus *pci_bus)
549 {
550 if (!pci_bus)
551 return;
552
553 printf("PCI Bus \"%s\" Device list:\n",pci_bus->name);
554 pci_bus_show_dev_list(pci_bus);
555 printf("\n");
556 }

  ViewVC Help
Powered by ViewVC 1.1.26