/[dynamips]/trunk/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 /trunk/pci_dev.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 12 - (show annotations)
Sat Oct 6 16:45:40 2007 UTC (11 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 14980 byte(s)
make working copy

1 /*
2 * Cisco router 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 "cpu.h"
19 #include "vm.h"
20 #include "dynamips.h"
21 #include "memory.h"
22 #include "device.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_gen_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_gen_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 = 0x0;
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_get_pc(cpu), 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_get_pc(cpu),
166 bus, device, function, reg);
167 }
168 #endif
169
170 if (!dev) {
171 if (op_type == MTS_READ) {
172 cpu_log(cpu,"PCI","read request for unknown device at pc=0x%llx "
173 "(bus=%d,device=%d,function=%d,reg=0x%2.2x).\n",
174 cpu_get_pc(cpu), bus, device, function, reg);
175 } else {
176 cpu_log(cpu,"PCI","write request (data=0x%8.8x) for unknown device "
177 "at pc=0x%llx (bus=%d,device=%d,function=%d,reg=0x%2.2x).\n",
178 pci_swap(*data,swap), cpu_get_pc(cpu),
179 bus, device, function, reg);
180 }
181
182 /* Returns an invalid device ID */
183 if ((op_type == MTS_READ) && (reg == PCI_REG_ID))
184 *data = 0xffffffff;
185 } else {
186 if (op_type == MTS_WRITE) {
187 if (dev->write_register != NULL)
188 dev->write_register(cpu,dev,reg,pci_swap(*data,swap));
189 } else {
190 if (reg == PCI_REG_ID)
191 *data = pci_swap((dev->product_id << 16) | dev->vendor_id,swap);
192 else {
193 if (dev->read_register != NULL)
194 *data = pci_swap(dev->read_register(cpu,dev,reg),swap);
195 }
196 }
197 }
198 }
199
200 /* Add a PCI bridge */
201 struct pci_bridge *pci_bridge_add(struct pci_bus *pci_bus)
202 {
203 struct pci_bridge *bridge;
204
205 if (!pci_bus)
206 return NULL;
207
208 if (!(bridge = malloc(sizeof(*bridge)))) {
209 fprintf(stderr,"pci_bridge_add: unable to create new PCI bridge.\n");
210 return NULL;
211 }
212
213 memset(bridge,0,sizeof(*bridge));
214 bridge->pri_bus = pci_bus->bus;
215 bridge->sec_bus = -1;
216 bridge->sub_bus = -1;
217 bridge->pci_bus = NULL;
218
219 /* Insert the bridge in the double-linked list */
220 bridge->next = pci_bus->bridge_list;
221 bridge->pprev = &pci_bus->bridge_list;
222
223 if (pci_bus->bridge_list != NULL)
224 pci_bus->bridge_list->pprev = &bridge->next;
225
226 pci_bus->bridge_list = bridge;
227 return bridge;
228 }
229
230 /* Remove a PCI bridge from the double-linked list */
231 static inline void pci_bridge_remove_from_list(struct pci_bridge *bridge)
232 {
233 if (bridge->next)
234 bridge->next->pprev = bridge->pprev;
235
236 if (bridge->pprev)
237 *(bridge->pprev) = bridge->next;
238 }
239
240 /* Remove a PCI bridge */
241 void pci_bridge_remove(struct pci_bridge *bridge)
242 {
243 if (bridge != NULL) {
244 pci_bridge_remove_from_list(bridge);
245 free(bridge);
246 }
247 }
248
249 /* Map secondary bus to a PCI bridge */
250 void pci_bridge_map_bus(struct pci_bridge *bridge,struct pci_bus *pci_bus)
251 {
252 if (bridge != NULL) {
253 bridge->pci_bus = pci_bus;
254
255 if (bridge->pci_bus != NULL)
256 bridge->pci_bus->bus = bridge->sec_bus;
257 }
258 }
259
260 /* Set PCI bridge bus info */
261 void pci_bridge_set_bus_info(struct pci_bridge *bridge,
262 int pri_bus,int sec_bus,int sub_bus)
263 {
264 if (bridge != NULL) {
265 bridge->pri_bus = pri_bus;
266 bridge->sec_bus = sec_bus;
267 bridge->sub_bus = sub_bus;
268
269 if (bridge->pci_bus != NULL)
270 bridge->pci_bus->bus = bridge->sec_bus;
271 }
272 }
273
274 /* Add a PCI device */
275 struct pci_device *
276 pci_dev_add(struct pci_bus *pci_bus,char *name,
277 u_int vendor_id,u_int product_id,
278 int device,int function,int irq,
279 void *priv_data,pci_init_t init,
280 pci_reg_read_t read_register,
281 pci_reg_write_t write_register)
282 {
283 struct pci_device *dev;
284
285 if (!pci_bus)
286 return NULL;
287
288 if ((dev = pci_dev_lookup_local(pci_bus,device,function)) != NULL) {
289 fprintf(stderr,"pci_dev_add: bus %s, device %d, function %d already "
290 "registered (device '%s').\n",
291 pci_bus->name,device,function,dev->name);
292 return NULL;
293 }
294
295 /* we can create safely the new device */
296 if (!(dev = malloc(sizeof(*dev)))) {
297 fprintf(stderr,"pci_dev_add: unable to create new PCI device.\n");
298 return NULL;
299 }
300
301 memset(dev,0,sizeof(*dev));
302 dev->name = name;
303 dev->vendor_id = vendor_id;
304 dev->product_id = product_id;
305 dev->pci_bus = pci_bus;
306 dev->device = device;
307 dev->function = function;
308 dev->irq = irq;
309 dev->priv_data = priv_data;
310 dev->init = init;
311 dev->read_register = read_register;
312 dev->write_register = write_register;
313
314 /* Insert the device in the double-linked list */
315 dev->next = pci_bus->dev_list;
316 dev->pprev = &pci_bus->dev_list;
317
318 if (pci_bus->dev_list != NULL)
319 pci_bus->dev_list->pprev = &dev->next;
320
321 pci_bus->dev_list = dev;
322
323 if (init) init(dev);
324 return dev;
325 }
326
327 /* Add a basic PCI device that just returns a Vendor/Product ID */
328 struct pci_device *
329 pci_dev_add_basic(struct pci_bus *pci_bus,
330 char *name,u_int vendor_id,u_int product_id,
331 int device,int function)
332 {
333 return(pci_dev_add(pci_bus,name,vendor_id,product_id,
334 device,function,-1,NULL,
335 NULL,NULL,NULL));
336 }
337
338 /* Remove a device from the double-linked list */
339 static inline void pci_dev_remove_from_list(struct pci_device *dev)
340 {
341 if (dev->next)
342 dev->next->pprev = dev->pprev;
343
344 if (dev->pprev)
345 *(dev->pprev) = dev->next;
346 }
347
348 /* Remove a PCI device */
349 void pci_dev_remove(struct pci_device *dev)
350 {
351 if (dev != NULL) {
352 pci_dev_remove_from_list(dev);
353 free(dev);
354 }
355 }
356
357 /* Remove a PCI device given its ID (bus,device,function) */
358 int pci_dev_remove_by_id(struct pci_bus *pci_bus,
359 int bus,int device,int function)
360 {
361 struct pci_device *dev;
362
363 if (!(dev = pci_dev_lookup(pci_bus,bus,device,function)))
364 return(-1);
365
366 pci_dev_remove(dev);
367 return(0);
368 }
369
370 /* Remove a PCI device given its name */
371 int pci_dev_remove_by_name(struct pci_bus *pci_bus,char *name)
372 {
373 struct pci_device *dev,*next;
374 int count = 0;
375
376 for(dev=pci_bus->dev_list;dev;dev=next) {
377 next = dev->next;
378
379 if (!strcmp(dev->name,name)) {
380 pci_dev_remove(dev);
381 count++;
382 }
383 }
384
385 return(count);
386 }
387
388 /* Create a PCI bus */
389 struct pci_bus *pci_bus_create(char *name,int bus)
390 {
391 struct pci_bus *d;
392
393 if (!(d = malloc(sizeof(*d)))) {
394 fprintf(stderr,"pci_bus_create: unable to create PCI info.\n");
395 return NULL;
396 }
397
398 memset(d,0,sizeof(*d));
399 d->name = strdup(name);
400 d->bus = bus;
401 return d;
402 }
403
404 /* Delete a PCI bus */
405 void pci_bus_remove(struct pci_bus *pci_bus)
406 {
407 struct pci_device *dev,*next;
408 struct pci_bridge *bridge,*next_bridge;
409
410 if (pci_bus) {
411 /* Remove all devices */
412 for(dev=pci_bus->dev_list;dev;dev=next) {
413 next = dev->next;
414 free(dev);
415 }
416
417 /* Remove all bridges */
418 for(bridge=pci_bus->bridge_list;bridge;bridge=next_bridge) {
419 next_bridge = bridge->next;
420 free(bridge);
421 }
422
423 /* Free the structure itself */
424 free(pci_bus->name);
425 free(pci_bus);
426 }
427 }
428
429 /* Read a configuration register of a PCI bridge */
430 static m_uint32_t pci_bridge_read_reg(cpu_gen_t *cpu,struct pci_device *dev,
431 int reg)
432 {
433 struct pci_bridge *bridge = dev->priv_data;
434 m_uint32_t val = 0;
435
436 switch(reg) {
437 case 0x18:
438 return(bridge->cfg_reg_bus);
439 default:
440 if (bridge->fallback_read != NULL)
441 val = bridge->fallback_read(cpu,dev,reg);
442
443 /* Returns appropriate PCI bridge class code if nothing defined */
444 if ((reg == 0x08) && !val)
445 val = 0x06040000;
446
447 return(val);
448 }
449 }
450
451 /* Write a configuration register of a PCI bridge */
452 static void pci_bridge_write_reg(cpu_gen_t *cpu,struct pci_device *dev,
453 int reg,m_uint32_t value)
454 {
455 struct pci_bridge *bridge = dev->priv_data;
456 u_int pri_bus,sec_bus,sub_bus;
457
458 switch(reg) {
459 case 0x18:
460 bridge->cfg_reg_bus = value;
461 sub_bus = (value >> 16) & 0xFF;
462 sec_bus = (value >> 8) & 0xFF;
463 pri_bus = value & 0xFF;
464
465 /* Modify the PCI bridge settings */
466 vm_log(cpu->vm,"PCI",
467 "PCI bridge %d,%d,%d -> pri: %2.2u, sec: %2.2u, sub: %2.2u\n",
468 dev->pci_bus->bus,dev->device,dev->function,
469 pri_bus,sec_bus,sub_bus);
470
471 pci_bridge_set_bus_info(bridge,pri_bus,sec_bus,sub_bus);
472 break;
473
474 default:
475 if (bridge->fallback_write != NULL)
476 bridge->fallback_write(cpu,dev,reg,value);
477 }
478 }
479
480 /* Create a PCI bridge device */
481 struct pci_device *pci_bridge_create_dev(struct pci_bus *pci_bus,char *name,
482 u_int vendor_id,u_int product_id,
483 int device,int function,
484 struct pci_bus *sec_bus,
485 pci_reg_read_t fallback_read,
486 pci_reg_write_t fallback_write)
487 {
488 struct pci_bridge *bridge;
489 struct pci_device *dev;
490
491 /* Create the PCI bridge structure */
492 if (!(bridge = pci_bridge_add(pci_bus)))
493 return NULL;
494
495 /* Create the PCI device corresponding to the bridge */
496 dev = pci_dev_add(pci_bus,name,vendor_id,product_id,device,function,-1,
497 bridge,NULL,pci_bridge_read_reg,pci_bridge_write_reg);
498
499 if (!dev)
500 goto err_pci_dev;
501
502 /* Keep the associated PCI device for this bridge */
503 bridge->pci_dev = dev;
504
505 /* Set the fallback functions */
506 bridge->fallback_read = fallback_read;
507 bridge->fallback_write = fallback_write;
508
509 /* Map the secondary bus (disabled at startup) */
510 pci_bridge_map_bus(bridge,sec_bus);
511 return dev;
512
513 err_pci_dev:
514 pci_bridge_remove(bridge);
515 return NULL;
516 }
517
518 /* Show PCI device list of the specified bus */
519 static void pci_bus_show_dev_list(struct pci_bus *pci_bus)
520 {
521 struct pci_device *dev;
522 struct pci_bridge *bridge;
523 char bus_id[32];
524
525 if (!pci_bus)
526 return;
527
528 if (pci_bus->bus != -1) {
529 snprintf(bus_id,sizeof(bus_id),"%2d",pci_bus->bus);
530 } else {
531 strcpy(bus_id,"XX");
532 }
533
534 for(dev=pci_bus->dev_list;dev;dev=dev->next) {
535 printf(" %-18s: ID %4.4x:%4.4x, Bus %s, Dev. %2d, Func. %2d",
536 dev->name,dev->vendor_id,dev->product_id,
537 bus_id,dev->device,dev->function);
538
539 if (dev->irq != -1)
540 printf(", IRQ: %d\n",dev->irq);
541 else
542 printf("\n");
543 }
544
545 for(bridge=pci_bus->bridge_list;bridge;bridge=bridge->next)
546 pci_bus_show_dev_list(bridge->pci_bus);
547 }
548
549 /* Show PCI device list */
550 void pci_dev_show_list(struct pci_bus *pci_bus)
551 {
552 if (!pci_bus)
553 return;
554
555 printf("PCI Bus \"%s\" Device list:\n",pci_bus->name);
556 pci_bus_show_dev_list(pci_bus);
557 printf("\n");
558 }

  ViewVC Help
Powered by ViewVC 1.1.26