PCI detection code doesn't corrupt memory anymore (2nd try)
[people/xl0/syslinux-lua.git] / com32 / lib / pci / scan.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2006-2007 Erwan Velu - All Rights Reserved
4  *
5  *   Permission is hereby granted, free of charge, to any person
6  *   obtaining a copy of this software and associated documentation
7  *   files (the "Software"), to deal in the Software without
8  *   restriction, including without limitation the rights to use,
9  *   copy, modify, merge, publish, distribute, sublicense, and/or
10  *   sell copies of the Software, and to permit persons to whom
11  *   the Software is furnished to do so, subject to the following
12  *   conditions:
13  *
14  *   The above copyright notice and this permission notice shall
15  *   be included in all copies or substantial portions of the Software.
16  *
17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  *   OTHER DEALINGS IN THE SOFTWARE.
25  *
26  * ----------------------------------------------------------------------- */
27
28 /*
29  * pci.c
30  *
31  * A module to extract pci informations
32  */
33
34 #include <inttypes.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <console.h>
39 #include <sys/pci.h>
40 #include <com32.h>
41 #include <stdbool.h>
42
43 #ifdef DEBUG
44 # define dprintf printf
45 #else
46 # define dprintf(...) ((void)0)
47 #endif
48
49 #define MAX_LINE 512
50
51 /* searching the next char that is not a space */
52 static char *skipspace(char *p)
53 {
54   while (*p && *p <= ' ')
55     p++;
56
57   return p;
58 }
59
60 /* removing any \n found in a string */
61 static void remove_eol(char *string)
62 {
63  int j = strlen(string);
64  int i = 0;
65  for(i = 0; i < j; i++) if(string[i] == '\n') string[i] = 0;
66 }
67
68 /* converting a hexa string into its numerical value*/
69 static int hex_to_int(char *hexa)
70 {
71   return strtoul(hexa, NULL, 16);
72 }
73
74 /* Try to match any pci device to the appropriate kernel module */
75 /* it uses the modules.pcimap from the boot device*/
76 void get_module_name_from_pci_ids(struct pci_device_list *pci_device_list)
77 {
78   char line[MAX_LINE];
79   char module_name[21]; // the module name field is 21 char long
80   char delims[]=" ";    // colums are separated by spaces
81   char vendor_id[16];
82   char product_id[16];
83   char sub_vendor_id[16];
84   char sub_product_id[16];
85   FILE *f;
86   int pci_dev;
87
88   /* Intializing the linux_kernel_module for each pci device to "unknow" */
89   /* adding a pci_dev_info member if needed*/
90   for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
91     struct pci_device *pci_device = &(pci_device_list->pci_device[pci_dev]);
92
93     /* initialize the pci_dev_info structure if it doesn't exist yet. */
94     if (! pci_device->pci_dev_info) {
95       pci_device->pci_dev_info = calloc(1,sizeof *pci_device->pci_dev_info);
96
97       if (!pci_device->pci_dev_info) {
98         printf("Can't allocate memory\n");
99         return;
100       }
101     }
102     strlcpy(pci_device->pci_dev_info->linux_kernel_module,"unknown",7);
103   }
104
105   /* Opening the modules.pcimap (ofa linux kernel) from the boot device*/
106   f=fopen("modules.pcimap","r");
107   if (!f)
108     return;
109
110   strcpy(vendor_id,"0000");
111   strcpy(product_id,"0000");
112   strcpy(sub_product_id,"0000");
113   strcpy(sub_vendor_id,"0000");
114
115   /* for each line we found in the modules.pcimap*/
116   while ( fgets(line, sizeof line, f) ) {
117     /*skipping unecessary lines */
118     if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
119         continue;
120
121     char *result = NULL;
122     int field=0;
123
124     /* looking for the next field */
125     result = strtok(line, delims);
126     while( result != NULL ) {
127        /* if the column is larger than 1 char */
128        /* multiple spaces generates some empty fields*/
129        if (strlen(result)>1) {
130          switch (field) {
131          case 0:strcpy(module_name,result); break;
132          case 1:strcpy(vendor_id,result); break;
133          case 2:strcpy(product_id,result); break;
134          case 3:strcpy(sub_vendor_id,result); break;
135          case 4:strcpy(sub_product_id,result); break;
136          }
137          field++;
138        }
139        /* Searching the next field*/
140        result = strtok( NULL, delims );
141    }
142     /* if a pci_device match an entry, fill the linux_kernel_module with
143        the appropriate kernel module */
144     for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
145       struct pci_device *pci_device =
146         &pci_device_list->pci_device[pci_dev];
147
148       if (hex_to_int(vendor_id) == pci_device->vendor &&
149           hex_to_int(product_id) == pci_device->product &&
150           (hex_to_int(sub_product_id) & pci_device->sub_product)
151           == pci_device->sub_product &&
152           (hex_to_int(sub_vendor_id) & pci_device->sub_vendor)
153           == pci_device->sub_vendor)
154         strcpy(pci_device->pci_dev_info->linux_kernel_module,
155                module_name);
156     }
157   }
158  fclose(f);
159 }
160
161 /* Try to match any pci device to the appropriate vendor and product name */
162 /* it uses the pci.ids from the boot device*/
163 void get_name_from_pci_ids(struct pci_device_list *pci_device_list)
164 {
165   char line[MAX_LINE];
166   char vendor[255];
167   char vendor_id[5];
168   char product[255];
169   char product_id[5];
170   char sub_product_id[5];
171   char sub_vendor_id[5];
172   FILE *f;
173   int pci_dev;
174
175  /* Intializing the vendor/product name for each pci device to "unknow" */
176  /* adding a pci_dev_info member if needed*/
177  for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
178     struct pci_device *pci_device = &pci_device_list->pci_device[pci_dev];
179
180     /* initialize the pci_dev_info structure if it doesn't exist yet. */
181     if (! pci_device->pci_dev_info) {
182       pci_device->pci_dev_info = calloc(1,sizeof *pci_device->pci_dev_info);
183
184       if (!pci_device->pci_dev_info) {
185         printf("Can't allocate memory\n");
186         return;
187       }
188     }
189
190     strlcpy(pci_device->pci_dev_info->vendor_name,"unknown",7);
191     strlcpy(pci_device->pci_dev_info->product_name,"unknown",7);
192   }
193
194   /* Opening the pci.ids from the boot device*/
195   f=fopen("pci.ids","r");
196   if (!f)
197         return;
198   strcpy(vendor_id,"0000");
199   strcpy(product_id,"0000");
200   strcpy(sub_product_id,"0000");
201   strcpy(sub_vendor_id,"0000");
202
203
204   /* for each line we found in the pci.ids*/
205   while ( fgets(line, sizeof line, f) ) {
206     /* Skipping uncessary lines */
207     if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') ||
208         (line[0] == 10))
209         continue;
210     /* If the line doesn't start with a tab, it means that's a vendor id */
211     if (line[0] != '\t') {
212
213         /* the 4th first chars are the vendor_id */
214         strlcpy(vendor_id,line,4);
215
216         /* the vendor name is the next field*/
217         vendor_id[4]=0;
218         strlcpy(vendor,skipspace(strstr(line," ")),255);
219
220         remove_eol(vendor);
221         /* init product_id, sub_product and sub_vendor */
222         strcpy(product_id,"0000");
223         strcpy(sub_product_id,"0000");
224         strcpy(sub_vendor_id,"0000");
225
226         /* ffff is an invalid vendor id */
227         if (strstr(vendor_id,"ffff")) break;
228         /* assign the vendor_name to any matching pci device*/
229         for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
230           struct pci_device *pci_device =
231             &pci_device_list->pci_device[pci_dev];
232
233           if (hex_to_int(vendor_id) == pci_device->vendor)
234             strlcpy(pci_device->pci_dev_info->vendor_name,vendor,255);
235         }
236     /* if we have a tab + a char, it means this is a product id */
237     } else if ((line[0] == '\t') && (line[1] != '\t')) {
238
239         /* the product name the second field */
240         strlcpy(product,skipspace(strstr(line," ")),255);
241         remove_eol(product);
242
243         /* the product id is first field */
244         strlcpy(product_id,&line[1],4);
245         product_id[4]=0;
246
247         /* init sub_product and sub_vendor */
248         strcpy(sub_product_id,"0000");
249         strcpy(sub_vendor_id,"0000");
250
251         /* assign the product_name to any matching pci device*/
252         for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
253           struct pci_device *pci_device =
254             &pci_device_list->pci_device[pci_dev];
255           if (hex_to_int(vendor_id) == pci_device->vendor &&
256               hex_to_int(product_id) == pci_device->product)
257             strlcpy(pci_device->pci_dev_info->product_name,product,255);
258         }
259
260     /* if we have two tabs, it means this is a sub product */
261     } else if ((line[0] == '\t') && (line[1] == '\t')) {
262
263       /* the product name is last field */
264       strlcpy(product,skipspace(strstr(line," ")),255);
265       strlcpy(product,skipspace(strstr(product," ")),255);
266       remove_eol(product);
267
268       /* the sub_vendor id is first field */
269       strlcpy(sub_vendor_id,&line[2],4);
270       sub_vendor_id[4]=0;
271
272       /* the sub_vendor id is second field */
273       strlcpy(sub_product_id,&line[7],4);
274       sub_product_id[4]=0;
275
276       /* assign the product_name to any matching pci device*/
277       for (pci_dev=0; pci_dev < pci_device_list->count; pci_dev++) {
278         struct pci_device *pci_device =
279           &pci_device_list->pci_device[pci_dev];
280
281         if (hex_to_int(vendor_id) == pci_device->vendor &&
282             hex_to_int(product_id) == pci_device->product &&
283             hex_to_int(sub_product_id) == pci_device->sub_product &&
284             hex_to_int(sub_vendor_id) == pci_device->sub_vendor)
285           strlcpy(pci_device->pci_dev_info->product_name,product,255);
286       }
287     }
288   }
289   fclose(f);
290 }
291
292 /* searching if any pcidevice match our query */
293 struct match *find_pci_device(struct pci_device_list * pci_device_list,
294                               struct match *list)
295 {
296   int pci_dev;
297   uint32_t did, sid;
298   struct match *m;
299   /* for all matches we have to search */
300   for (m = list; m; m = m->next) {
301           /* for each pci device we know */
302     for (pci_dev = 0; pci_dev < pci_device_list->count; pci_dev++) {
303       struct pci_device *pci_device =
304         &pci_device_list->pci_device[pci_dev];
305
306       /* sid & did are the easiest way to compare devices */
307       /* they are made of vendor/product subvendor/subproduct ids */
308       sid =
309         ((pci_device->sub_product) << 16 | (pci_device->
310                                             sub_vendor));
311       did = ((pci_device->product << 16) | (pci_device->vendor));
312
313       /*if the current device match */
314       if (((did ^ m->did) & m->did_mask) == 0 &&
315           ((sid ^ m->sid) & m->sid_mask) == 0 &&
316           pci_device->revision >= m->rid_min
317           && pci_device->revision <= m->rid_max) {
318         dprintf("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n",
319                 pci_device->vendor, pci_device->product,
320                 pci_device->sub_vendor,
321                 pci_device->sub_product,
322                 pci_device->revision);
323         /* returning the matched pci device */
324         return m;
325       }
326     }
327   }
328   return NULL;
329 }
330
331 /* scanning the pci bus to find pci devices */
332 int pci_scan(struct pci_bus_list * pci_bus_list, struct pci_device_list * pci_device_list)
333 {
334   unsigned int bus, dev, func, maxfunc;
335   uint32_t did, sid;
336   uint8_t hdrtype, rid;
337   pciaddr_t a;
338   int cfgtype;
339
340   pci_device_list->count = 0;
341
342 #ifdef DEBUG
343   outl(~0, 0xcf8);
344   printf("Poking at port CF8 = %#08x\n", inl(0xcf8));
345   outl(0, 0xcf8);
346 #endif
347
348   cfgtype = pci_set_config_type(PCI_CFG_AUTO);
349   (void)cfgtype;
350
351   dprintf("PCI configuration type %d\n", cfgtype);
352   dprintf("Scanning PCI Buses\n");
353
354   /* We try to detect 255 buses */
355   for (bus = 0; bus <= MAX_PCI_BUSES; bus++) {
356
357     dprintf("Probing bus 0x%02x... \n", bus);
358
359     pci_bus_list->pci_bus[bus].id = bus;
360     pci_bus_list->pci_bus[bus].pci_device_count = 0;
361     pci_bus_list->count = 0;;
362
363     for (dev = 0; dev <= 0x1f; dev++) {
364       maxfunc = 0;
365       for (func = 0; func <= maxfunc; func++) {
366         a = pci_mkaddr(bus, dev, func, 0);
367
368         did = pci_readl(a);
369
370         if (did == 0xffffffff || did == 0xffff0000 ||
371             did == 0x0000ffff || did == 0x00000000)
372           continue;
373
374         hdrtype = pci_readb(a + 0x0e);
375
376         if (hdrtype & 0x80)
377           maxfunc = 7;  /* Multifunction device */
378
379         rid = pci_readb(a + 0x08);
380         sid = pci_readl(a + 0x2c);
381         struct pci_device *pci_device =
382           &pci_device_list->
383           pci_device[pci_device_list->count];
384         pci_device->product = did >> 16;
385         pci_device->sub_product = sid >> 16;
386         pci_device->vendor = (did << 16) >> 16;
387         pci_device->sub_vendor = (sid << 16) >> 16;
388         pci_device->revision = rid;
389         pci_device_list->count++;
390         dprintf
391           ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
392            bus, did, did >> 16, (did << 16) >> 16,
393            sid, rid);
394         /* Adding the detected pci device to the bus */
395         pci_bus_list->pci_bus[bus].
396           pci_device[pci_bus_list->pci_bus[bus].
397                      pci_device_count] = pci_device;
398         pci_bus_list->pci_bus[bus].pci_device_count++;
399       }
400     }
401   }
402
403   /* Detecting pci buses that have pci devices connected */
404   for (bus = 0; bus <= 0xff; bus++) {
405     if (pci_bus_list->pci_bus[bus].pci_device_count > 0) {
406       pci_bus_list->count++;
407     }
408   }
409   return 0;
410 }