-Tinkered with formatting
[pintos-anon] / src / devices / pci.c
1 #include <debug.h>
2 #include <list.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include "devices/pci.h"
6 #include "threads/io.h"
7 #include "threads/malloc.h"
8
9 struct list pci_dev_list;
10
11 static void scan_bus(uint8_t bus);
12
13 static uint32_t
14 pci_read_config (unsigned bus, unsigned dev, unsigned func, unsigned reg) 
15 {
16   ASSERT (bus < 256);
17   ASSERT (dev < 32);
18   ASSERT (func < 8);
19   ASSERT (reg < 64);
20
21   outl (0xcf8,
22         0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg << 2));
23   return inl (0xcfc);
24 }
25
26 static void
27 pci_write_config (unsigned bus, unsigned dev, unsigned func, unsigned reg, 
28                   unsigned data)
29 {
30   ASSERT (bus < 256);
31   ASSERT (dev < 32);
32   ASSERT (func < 8);
33   ASSERT (reg < 64);
34
35   outl (0xcf8,
36         0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg << 2));
37   outl (0xcfc, data);
38 }
39
40 static void
41 pci_dump_dev (struct pci_dev *dev) 
42 {
43   int bar;
44
45   printf ("PCI: Device %04x:%02x:%02x.%x %04x:%04x\n", 
46           0, dev->bus_id, dev->devfn >> 4, dev->devfn & 0xf, 
47           dev->ven_id, dev->dev_id);
48   printf ("PCI: Class %02x:%02x:%02x\n", 
49           dev->base_class, dev->sub_class, dev->interface);
50
51   for (bar = 0; bar < PCI_NUM_BARS; bar++)
52     {
53       if (dev->resources[bar].start != 0 ||
54           dev->resources[bar].end != 0)
55         {
56           printf ("PCI: %s ", 
57                   dev->resources[bar].type == PCI_BAR_TYPE_MEM 
58                   ? "Memory" : "IO");
59           printf ("range %d: %08x-%08x\n", 
60                   bar, dev->resources[bar].start, dev->resources[bar].end);
61         }
62     }
63 }
64
65 static void
66 pci_setup_bases (struct pci_dev *dev) 
67 {
68   int bar;
69   unsigned bar_reg;
70   uint32_t base, size;
71   struct resource *res;
72
73   for (bar = 0; bar < PCI_NUM_BARS; bar++)
74     {
75       bar_reg = PCI_REGNUM_BASE_ADDRESS + bar;
76
77       res = &dev->resources[bar];
78       res->start = 0;
79       res->end = 0;
80       
81       /* Get the base address out of the BAR */
82       base = pci_read_config(dev->bus_id,
83                              dev->devfn >> 4, 
84                              dev->devfn & 0xf, 
85                              bar_reg);
86
87       /* Write 1's and get the size from the BAR */
88       pci_write_config(dev->bus_id, 
89                        dev->devfn >> 4, 
90                        dev->devfn & 0xf, 
91                        bar_reg,
92                        ~0);
93       size = pci_read_config(dev->bus_id, 
94                              dev->devfn >> 4, 
95                              dev->devfn & 0xf, 
96                              bar_reg);
97
98       /* Put the base address back in the BAR */
99       pci_write_config(dev->bus_id, 
100                        dev->devfn >> 4, 
101                        dev->devfn & 0xf, 
102                        bar_reg,
103                        base);
104
105       /* If the size is unreasonable, skip this BAR.
106          Is this redundant for Pintos? */
107       if (!size || size == 0xffffffff) {
108         continue;
109       }
110
111       /* Broken hardware that requests a base at the end? 
112          Maybe redundant for Pintos? */
113       if (base == 0xffffffff) {
114         base = 0;
115       }
116
117       /* Make a new resource, tag it for I/O vs. memory */
118       res->type = base & PCI_BAR_TYPE_MASK;
119       if (res->type == PCI_BAR_TYPE_MEM) 
120         {
121           res->start = base & PCI_BAR_MASK_MEM;
122           size = size & PCI_BAR_MASK_MEM;
123         }
124       else
125         {
126           res->start = base & PCI_BAR_MASK_IO;
127           size = size & PCI_BAR_MASK_IO;
128         }
129       res->end = res->start + size;
130     }
131 }
132
133 static bool
134 scan_device (uint8_t bus, uint8_t dev, uint8_t func) 
135 {
136   uint32_t cfg[16];
137   uint8_t *byte_cfg;
138   int dev_id, vendor_id;
139   int line;
140   int reg;
141   struct pci_dev *new_pci_dev;
142
143   byte_cfg = (uint8_t *) cfg;
144
145   /* Read configuration space header */
146   for (reg = 0; reg < 16; reg++)
147     cfg[reg] = pci_read_config (bus, dev, func, reg);
148   
149   /* Get vendor and device ID */
150   dev_id = cfg[0] >> 16;
151   vendor_id = cfg[0] & 0xffff;
152
153   if (dev_id == 0 || dev_id == PCI_BAD_DEVICE || 
154       vendor_id == 0 || vendor_id == PCI_BAD_DEVICE)
155     return 0;
156
157   /* We have a valid PCI device, set it up */
158   new_pci_dev = malloc (sizeof *new_pci_dev);
159   if (!new_pci_dev)
160     PANIC ("couldn't allocate memory for PCI device");
161
162   new_pci_dev->bus_id = bus;
163   new_pci_dev->devfn = (dev << 4) | func;
164   new_pci_dev->ven_id = vendor_id;
165   new_pci_dev->dev_id = dev_id;
166   new_pci_dev->base_class = byte_cfg[PCI_REG_CLASS_BASE];
167   new_pci_dev->sub_class = byte_cfg[PCI_REG_CLASS_SUB];
168   new_pci_dev->interface = byte_cfg[PCI_REG_CLASS_INTERFACE];
169   list_push_front (&pci_dev_list, &new_pci_dev->elem);
170     
171   /* If device is PCI-to-PCI bridge, scan the bus behind it */
172   if (new_pci_dev->base_class == PCI_BRIDGE_BASE_CLASS &&
173       new_pci_dev->sub_class == PCI_BRIDGE_SUB_CLASS &&
174       (byte_cfg[PCI_REG_HEADER_TYPE] & 0x3f) == PCI_BRIDGE_HEADER_TYPE)
175     {
176       scan_bus(byte_cfg[PCI_BRIDGE_REG_SBUS]);
177     }
178
179   /* For normal devices, set up the BARs */
180   else 
181     {
182       pci_setup_bases (new_pci_dev);
183     }
184
185   /* Debugging output */
186   pci_dump_dev(new_pci_dev);
187   /*for (line = 0; line < 16; line++) 
188     {
189       int byte;
190       
191       printf ("PCI: %02x:", line * 4);
192       for (byte = 3; byte >= 0; byte--)
193         printf (" %02x", byte_cfg[line * 4 + byte]);
194       printf ("\n");
195       }*/
196   printf ("\n");
197
198   
199   /* Return if we're a multifunction device */
200   return byte_cfg[PCI_REG_HEADER_TYPE] & 0x80;
201 }
202
203 static void
204 scan_bus(uint8_t bus)
205 {
206   uint8_t dev;
207
208   printf ("PCI: Scanning bus (%u)\n", bus);
209   printf ("\n");
210
211   for (dev = 0; dev < 32; dev++) 
212     {
213       /* Returns true if device is multi-function */
214       if (scan_device (bus, dev, 0)) 
215         {
216           int func;
217
218           for (func = 1; func < 8; func++)
219             scan_device (bus, dev, func);
220         }
221     }
222   printf ("PCI: Done (%u)\n", bus);
223   printf ("\n");
224 }
225
226 void
227 pci_init (void) 
228 {
229   printf ("\n");
230   printf ("PCI: Initializating\n");
231   list_init (&pci_dev_list);
232   scan_bus(0);  
233   printf ("PCI: Initialization done\n");
234 }
235
236 void
237 pci_dump (void)
238 {
239   struct list_elem *e;
240
241   for (e = list_begin (&pci_dev_list); e != list_end (&pci_dev_list);
242        e = list_next (e))
243     {
244       struct pci_dev *dev = list_entry (e, struct pci_dev, elem);
245       pci_dump_dev(dev);
246     }
247 }