/[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

Annotation of /trunk/pci_dev.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations)
Sat Oct 6 16:23:47 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.7-RC1/pci_dev.c
File MIME type: text/plain
File size: 14980 byte(s)
dynamips-0.2.7-RC1

1 dpavlin 1 /*
2 dpavlin 7 * Cisco router simulation platform.
3 dpavlin 1 * 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 dpavlin 7 #include "cpu.h"
19     #include "vm.h"
20 dpavlin 1 #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 dpavlin 7 void pci_dev_addr_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus,
113 dpavlin 1 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 dpavlin 7 void pci_dev_data_handler(cpu_gen_t *cpu,struct pci_bus *pci_bus,
130 dpavlin 1 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 dpavlin 7 *data = 0x0;
137 dpavlin 1
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 dpavlin 7 cpu_get_pc(cpu), bus, device, function, reg);
162 dpavlin 1 } 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 dpavlin 7 pci_swap(*data,swap), cpu_get_pc(cpu),
166     bus, device, function, reg);
167 dpavlin 1 }
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 dpavlin 7 cpu_get_pc(cpu), bus, device, function, reg);
175 dpavlin 1 } 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 dpavlin 7 pci_swap(*data,swap), cpu_get_pc(cpu),
179     bus, device, function, reg);
180 dpavlin 1 }
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 dpavlin 7 static m_uint32_t pci_bridge_read_reg(cpu_gen_t *cpu,struct pci_device *dev,
431 dpavlin 1 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 dpavlin 7 static void pci_bridge_write_reg(cpu_gen_t *cpu,struct pci_device *dev,
453 dpavlin 1 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