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 #include <ctype.h>
43 #include <syslinux/zio.h>
44 #include <dprintf.h>
45 
46 #define MAX_LINE 512
47 
48 /* removing any \n found in a string */
remove_eol(char * string)49 static void remove_eol(char *string)
50 {
51     int j = strlen(string);
52     int i = 0;
53     for (i = 0; i < j; i++)
54 	if (string[i] == '\n')
55 	    string[i] = 0;
56 }
57 
58 /* converting a hexa string into its numerical value */
hex_to_int(char * hexa)59 static int hex_to_int(char *hexa)
60 {
61     return strtoul(hexa, NULL, 16);
62 }
63 
64 /* Try to match any pci device to the appropriate kernel module */
65 /* it uses the modules.pcimap from the boot device */
get_module_name_from_pcimap(struct pci_domain * domain,char * modules_pcimap_path)66 int get_module_name_from_pcimap(struct pci_domain *domain,
67 				char *modules_pcimap_path)
68 {
69   char line[MAX_LINE];
70   char module_name[21]; // the module name field is 21 char long
71   char delims[]=" ";    // colums are separated by spaces
72   char vendor_id[16];
73   char product_id[16];
74   char sub_vendor_id[16];
75   char sub_product_id[16];
76   FILE *f;
77   struct pci_device *dev=NULL;
78 
79   /* Intializing the linux_kernel_module for each pci device to "unknown" */
80   /* adding a dev_info member if needed */
81   for_each_pci_func(dev, domain) {
82     /* initialize the dev_info structure if it doesn't exist yet. */
83     if (! dev->dev_info) {
84       dev->dev_info = zalloc(sizeof *dev->dev_info);
85       if (!dev->dev_info)
86 	return -1;
87     }
88     for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
89      if (strlen(dev->dev_info->linux_kernel_module[i])==0)
90        strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
91     }
92   }
93 
94   /* Opening the modules.pcimap (of a linux kernel) from the boot device */
95   f=zfopen(modules_pcimap_path, "r");
96   if (!f)
97     return -ENOMODULESPCIMAP;
98 
99   strcpy(vendor_id,"0000");
100   strcpy(product_id,"0000");
101   strcpy(sub_product_id,"0000");
102   strcpy(sub_vendor_id,"0000");
103 
104   /* for each line we found in the modules.pcimap */
105   while ( fgets(line, sizeof line, f) ) {
106     /* skipping unecessary lines */
107     if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
108         continue;
109 
110     char *result = NULL;
111     int field=0;
112 
113     /* looking for the next field */
114     result = strtok(line, delims);
115     while( result != NULL ) {
116        /* if the column is larger than 1 char */
117        /* multiple spaces generates some empty fields */
118        if (strlen(result)>1) {
119 	 switch (field) {
120 	 /* About case 0, the kernel module name is featuring '_' or '-'
121 	  * in the module name whereas modules.alias is only using '_'.
122 	  * To avoid kernel modules duplication, let's rename all '-' in '_'
123 	  * to match what modules.alias provides */
124 	 case 0:chrreplace(result,'-','_');strcpy(module_name,result); break;
125 	 case 1:strcpy(vendor_id,result); break;
126 	 case 2:strcpy(product_id,result); break;
127 	 case 3:strcpy(sub_vendor_id,result); break;
128 	 case 4:strcpy(sub_product_id,result); break;
129 	 }
130 	 field++;
131        }
132        /* Searching the next field */
133        result = strtok( NULL, delims );
134    }
135     int int_vendor_id=hex_to_int(vendor_id);
136     int int_sub_vendor_id=hex_to_int(sub_vendor_id);
137     int int_product_id=hex_to_int(product_id);
138     int int_sub_product_id=hex_to_int(sub_product_id);
139     /* if a pci_device matches an entry, fill the linux_kernel_module with
140        the appropriate kernel module */
141     for_each_pci_func(dev, domain) {
142       if (int_vendor_id == dev->vendor &&
143 	  int_product_id == dev->product &&
144 	  (int_sub_product_id & dev->sub_product)
145 	  == dev->sub_product &&
146 	  (int_sub_vendor_id & dev->sub_vendor)
147 	  == dev->sub_vendor) {
148 	      bool found=false;
149 
150 	      /* Scan all known kernel modules for this pci device */
151 	      for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
152 
153        	      /* Try to detect if we already knew the same kernel module*/
154 	       if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
155 		      found=true;
156 		      break;
157 	       }
158 	      }
159 	      /* If we don't have this kernel module, let's add it */
160 	      if (!found) {
161 		strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
162 		dev->dev_info->linux_kernel_module_count++;
163 	      }
164       }
165     }
166   }
167   fclose(f);
168   return 0;
169 }
170 
171 /* Try to match any pci device to the appropriate class name */
172 /* it uses the pci.ids from the boot device */
get_class_name_from_pci_ids(struct pci_domain * domain,char * pciids_path)173 int get_class_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
174 {
175     char line[MAX_LINE];
176     char class_name[PCI_CLASS_NAME_SIZE];
177     char sub_class_name[PCI_CLASS_NAME_SIZE];
178     char class_id_str[5];
179     char sub_class_id_str[5];
180     FILE *f;
181     struct pci_device *dev;
182     bool class_mode = false;
183 
184     /* Intializing the vendor/product name for each pci device to "unknown" */
185     /* adding a dev_info member if needed */
186     for_each_pci_func(dev, domain) {
187 	/* initialize the dev_info structure if it doesn't exist yet. */
188 	if (!dev->dev_info) {
189 	    dev->dev_info = zalloc(sizeof *dev->dev_info);
190 	    if (!dev->dev_info)
191 		return -1;
192 	}
193 	strlcpy(dev->dev_info->class_name, "unknown", 7);
194     }
195 
196     /* Opening the pci.ids from the boot device */
197     f = zfopen(pciids_path, "r");
198     if (!f)
199 	return -ENOPCIIDS;
200 
201     /* for each line we found in the pci.ids */
202     while (fgets(line, sizeof line, f)) {
203 	/* Skipping uncessary lines */
204 	if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10))
205 	    continue;
206 
207 	/* Until we found a line starting with a 'C', we are not parsing classes */
208 	if (line[0] == 'C')
209 	    class_mode = true;
210 	if (class_mode == false)
211 	    continue;
212 	strlcpy(class_name, "unknown", 7);
213 	/* If the line doesn't start with a tab, it means that's a class name */
214 	if (line[0] != '\t') {
215 
216 	    /* ignore the two first char and then copy 2 chars (class id) */
217 	    strlcpy(class_id_str, &line[2], 2);
218 	    class_id_str[2] = 0;
219 
220 	    /* the class name is the next field */
221 	    strlcpy(class_name, skipspace(strstr(line, " ")),
222 		    PCI_CLASS_NAME_SIZE - 1);
223 	    remove_eol(class_name);
224 
225 	    int int_class_id_str = hex_to_int(class_id_str);
226 	    /* assign the class_name to any matching pci device */
227 	    for_each_pci_func(dev, domain) {
228 		if (int_class_id_str == dev->class[2]) {
229 		    strlcpy(dev->dev_info->class_name, class_name,
230 			    PCI_CLASS_NAME_SIZE - 1);
231 		    /* This value is usually the main category */
232 		    strlcpy(dev->dev_info->category_name, class_name + 4,
233 			    PCI_CLASS_NAME_SIZE - 1);
234 		}
235 	    }
236 	    /* if we have a tab + a char, it means this is a sub class name */
237 	} else if ((line[0] == '\t') && (line[1] != '\t')) {
238 
239 	    /* the sub class name the second field */
240 	    strlcpy(sub_class_name, skipspace(strstr(line, " ")),
241 		    PCI_CLASS_NAME_SIZE - 1);
242 	    remove_eol(sub_class_name);
243 
244 	    /* the sub class id is first field */
245 	    strlcpy(sub_class_id_str, &line[1], 2);
246 	    sub_class_id_str[2] = 0;
247 
248 	    int int_class_id_str = hex_to_int(class_id_str);
249 	    int int_sub_class_id_str = hex_to_int(sub_class_id_str);
250 	    /* assign the product_name to any matching pci device */
251 	    for_each_pci_func(dev, domain) {
252 		if (int_class_id_str == dev->class[2] &&
253 		    int_sub_class_id_str == dev->class[1])
254 		    strlcpy(dev->dev_info->class_name, sub_class_name,
255 			    PCI_CLASS_NAME_SIZE - 1);
256 	    }
257 
258 	}
259     }
260     fclose(f);
261     return 0;
262 }
263 
264 /* Try to match any pci device to the appropriate vendor and product name */
265 /* it uses the pci.ids from the boot device */
get_name_from_pci_ids(struct pci_domain * domain,char * pciids_path)266 int get_name_from_pci_ids(struct pci_domain *domain, char *pciids_path)
267 {
268     char line[MAX_LINE];
269     char vendor[PCI_VENDOR_NAME_SIZE];
270     char vendor_id[5];
271     char product[PCI_PRODUCT_NAME_SIZE];
272     char product_id[5];
273     char sub_product_id[5];
274     char sub_vendor_id[5];
275     FILE *f;
276     struct pci_device *dev;
277     bool skip_to_next_vendor = false;
278     uint16_t int_vendor_id;
279     uint16_t int_product_id;
280     uint16_t int_sub_product_id;
281     uint16_t int_sub_vendor_id;
282 
283     /* Intializing the vendor/product name for each pci device to "unknown" */
284     /* adding a dev_info member if needed */
285     for_each_pci_func(dev, domain) {
286 	/* initialize the dev_info structure if it doesn't exist yet. */
287 	if (!dev->dev_info) {
288 	    dev->dev_info = zalloc(sizeof *dev->dev_info);
289 	    if (!dev->dev_info)
290 		return -1;
291 	}
292 	strlcpy(dev->dev_info->vendor_name, "unknown", 7);
293 	strlcpy(dev->dev_info->product_name, "unknown", 7);
294     }
295 
296     /* Opening the pci.ids from the boot device */
297     f = zfopen(pciids_path, "r");
298     if (!f)
299 	return -ENOPCIIDS;
300 
301     strlcpy(vendor_id, "0000", 4);
302     strlcpy(product_id, "0000", 4);
303     strlcpy(sub_product_id, "0000", 4);
304     strlcpy(sub_vendor_id, "0000", 4);
305 
306     /* for each line we found in the pci.ids */
307     while (fgets(line, sizeof line, f)) {
308 	/* Skipping uncessary lines */
309 	if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') ||
310 	    (line[0] == 10))
311 	    continue;
312 
313 	/* If the line doesn't start with a tab, it means that's a vendor id */
314 	if (line[0] != '\t') {
315 
316 	    /* the 4 first chars are the vendor_id */
317 	    strlcpy(vendor_id, line, 4);
318 
319 	    /* the vendor name is the next field */
320 	    vendor_id[4] = 0;
321 	    strlcpy(vendor, skipspace(strstr(line, " ")),
322 		    PCI_VENDOR_NAME_SIZE - 1);
323 
324 	    remove_eol(vendor);
325 	    /* init product_id, sub_product and sub_vendor */
326 	    strlcpy(product_id, "0000", 4);
327 	    strlcpy(sub_product_id, "0000", 4);
328 	    strlcpy(sub_vendor_id, "0000", 4);
329 
330 	    /* Unless we found a matching device, we have to skip to the next vendor */
331 	    skip_to_next_vendor = true;
332 
333 	    int_vendor_id = hex_to_int(vendor_id);
334 	    /* Iterate in all pci devices to find a matching vendor */
335 	    for_each_pci_func(dev, domain) {
336 		/* if one device that match this vendor */
337 		if (int_vendor_id == dev->vendor) {
338 		    /* copy the vendor name for this device */
339 		    strlcpy(dev->dev_info->vendor_name, vendor,
340 			    PCI_VENDOR_NAME_SIZE - 1);
341 		    /* Some pci devices match this vendor, so we have to found them */
342 		    skip_to_next_vendor = false;
343 		    /* Let's loop on the other devices as some may have the same vendor */
344 		}
345 	    }
346 	    /* if we have a tab + a char, it means this is a product id
347 	     * but we only look at it if we own some pci devices of the current vendor*/
348 	} else if ((line[0] == '\t') && (line[1] != '\t')
349 		   && (skip_to_next_vendor == false)) {
350 
351 	    /* the product name the second field */
352 	    strlcpy(product, skipspace(strstr(line, " ")),
353 		    PCI_PRODUCT_NAME_SIZE - 1);
354 	    remove_eol(product);
355 
356 	    /* the product id is first field */
357 	    strlcpy(product_id, &line[1], 4);
358 	    product_id[4] = 0;
359 
360 	    /* init sub_product and sub_vendor */
361 	    strlcpy(sub_product_id, "0000", 4);
362 	    strlcpy(sub_vendor_id, "0000", 4);
363 
364 	    int_vendor_id = hex_to_int(vendor_id);
365 	    int_product_id = hex_to_int(product_id);
366 	    /* assign the product_name to any matching pci device */
367 	    for_each_pci_func(dev, domain) {
368 		if (int_vendor_id == dev->vendor &&
369 		    int_product_id == dev->product) {
370 		    strlcpy(dev->dev_info->vendor_name, vendor,
371 			    PCI_VENDOR_NAME_SIZE - 1);
372 		    strlcpy(dev->dev_info->product_name, product,
373 			    PCI_PRODUCT_NAME_SIZE - 1);
374 		}
375 	    }
376 
377 	    /* if we have two tabs, it means this is a sub product
378 	     * but we only look at it if we own some pci devices of the current vendor*/
379 	} else if ((line[0] == '\t') && (line[1] == '\t')
380 		   && (skip_to_next_vendor == false)) {
381 
382 	    /* the product name is last field */
383 	    strlcpy(product, skipspace(strstr(line, " ")),
384 		    PCI_PRODUCT_NAME_SIZE - 1);
385 	    strlcpy(product, skipspace(strstr(product, " ")),
386 		    PCI_PRODUCT_NAME_SIZE - 1);
387 	    remove_eol(product);
388 
389 	    /* the sub_vendor id is first field */
390 	    strlcpy(sub_vendor_id, &line[2], 4);
391 	    sub_vendor_id[4] = 0;
392 
393 	    /* the sub_vendor id is second field */
394 	    strlcpy(sub_product_id, &line[7], 4);
395 	    sub_product_id[4] = 0;
396 
397 	    int_vendor_id = hex_to_int(vendor_id);
398 	    int_sub_vendor_id = hex_to_int(sub_vendor_id);
399 	    int_product_id = hex_to_int(product_id);
400 	    int_sub_product_id = hex_to_int(sub_product_id);
401 	    /* assign the product_name to any matching pci device */
402 	    for_each_pci_func(dev, domain) {
403 		if (int_vendor_id == dev->vendor &&
404 		    int_product_id == dev->product &&
405 		    int_sub_product_id == dev->sub_product &&
406 		    int_sub_vendor_id == dev->sub_vendor) {
407 		    strlcpy(dev->dev_info->vendor_name, vendor,
408 			    PCI_VENDOR_NAME_SIZE - 1);
409 		    strlcpy(dev->dev_info->product_name, product,
410 			    PCI_PRODUCT_NAME_SIZE - 1);
411 		}
412 	    }
413 	}
414     }
415     fclose(f);
416     return 0;
417 }
418 
419 /* searching if any pcidevice match our query */
find_pci_device(const struct pci_domain * domain,struct match * list)420 struct match *find_pci_device(const struct pci_domain *domain,
421 			      struct match *list)
422 {
423     uint32_t did, sid;
424     struct match *m;
425     const struct pci_device *dev;
426 
427     /* for all matches we have to search */
428     for (m = list; m; m = m->next) {
429 	/* for each pci device we know */
430 	for_each_pci_func(dev, domain) {
431 	    /* sid & did are the easiest way to compare devices */
432 	    /* they are made of vendor/product subvendor/subproduct ids */
433 	    sid = dev->svid_sdid;
434 	    did = dev->vid_did;
435 	    /* if the current device match */
436 	    if (((did ^ m->did) & m->did_mask) == 0 &&
437 		((sid ^ m->sid) & m->sid_mask) == 0 &&
438 		dev->revision >= m->rid_min && dev->revision <= m->rid_max) {
439 		dprintf
440 		    ("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n",
441 		     dev->vendor, dev->product, dev->sub_vendor,
442 		     dev->sub_product, dev->revision);
443 		/* returning the matched pci device */
444 		return m;
445 	    }
446 	}
447     }
448     return NULL;
449 }
450 
451 /* scanning the pci bus to find pci devices */
pci_scan(void)452 struct pci_domain *pci_scan(void)
453 {
454     struct pci_domain *domain = NULL;
455     struct pci_bus *bus = NULL;
456     struct pci_slot *slot = NULL;
457     struct pci_device *func = NULL;
458     unsigned int nbus, ndev, nfunc, maxfunc;
459     uint32_t did, sid, rcid;
460     uint8_t hdrtype;
461     pciaddr_t a;
462     int cfgtype;
463 
464     cfgtype = pci_set_config_type(PCI_CFG_AUTO);
465 
466     dprintf("PCI configuration type %d\n", cfgtype);
467 
468     if (cfgtype == PCI_CFG_NONE)
469 	return NULL;
470 
471     dprintf("Scanning PCI Buses\n");
472 
473     for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
474 	dprintf("Probing bus 0x%02x... \n", nbus);
475 	bus = NULL;
476 
477 	for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
478 	    maxfunc = 1;	/* Assume a single-function device */
479 	    slot = NULL;
480 
481 	    for (nfunc = 0; nfunc < maxfunc; nfunc++) {
482 		a = pci_mkaddr(nbus, ndev, nfunc, 0);
483 		did = pci_readl(a);
484 
485 		if (did == 0xffffffff || did == 0xffff0000 ||
486 		    did == 0x0000ffff || did == 0x00000000)
487 		    continue;
488 
489 		hdrtype = pci_readb(a + 0x0e);
490 
491 		if (hdrtype & 0x80)
492 		    maxfunc = MAX_PCI_FUNC;	/* Multifunction device */
493 
494 		rcid = pci_readl(a + 0x08);
495 		sid = pci_readl(a + 0x2c);
496 
497 		if (!domain) {
498 		    domain = zalloc(sizeof *domain);
499 		    if (!domain)
500 			goto bail;
501 		}
502 		if (!bus) {
503 		    bus = zalloc(sizeof *bus);
504 		    if (!bus)
505 			goto bail;
506 		    domain->bus[nbus] = bus;
507 		}
508 		if (!slot) {
509 		    slot = zalloc(sizeof *slot);
510 		    if (!slot)
511 			goto bail;
512 		    bus->slot[ndev] = slot;
513 		}
514 		func = zalloc(sizeof *func);
515 		if (!func)
516 		    goto bail;
517 
518 		slot->func[nfunc] = func;
519 
520 		func->vid_did = did;
521 		func->svid_sdid = sid;
522 		func->rid_class = rcid;
523 
524 		dprintf
525 		    ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
526 		     nbus, did, did >> 16, (did << 16) >> 16, sid, rcid & 0xff);
527 	    }
528 	}
529     }
530 
531     return domain;
532 
533 bail:
534     free_pci_domain(domain);
535     return NULL;
536 }
537 
538 /* gathering additional configuration*/
gather_additional_pci_config(struct pci_domain * domain)539 void gather_additional_pci_config(struct pci_domain *domain)
540 {
541     struct pci_device *dev;
542     pciaddr_t pci_addr;
543     int cfgtype;
544 
545     cfgtype = pci_set_config_type(PCI_CFG_AUTO);
546     if (cfgtype == PCI_CFG_NONE)
547 	return;
548 
549     for_each_pci_func3(dev, domain, pci_addr) {
550 	if (!dev->dev_info) {
551 	    dev->dev_info = zalloc(sizeof *dev->dev_info);
552 	    if (!dev->dev_info) {
553 		return;
554 	    }
555 	}
556 	dev->dev_info->irq = pci_readb(pci_addr + 0x3c);
557 	dev->dev_info->latency = pci_readb(pci_addr + 0x0d);
558     }
559 }
560 
free_pci_domain(struct pci_domain * domain)561 void free_pci_domain(struct pci_domain *domain)
562 {
563     struct pci_bus *bus;
564     struct pci_slot *slot;
565     struct pci_device *func;
566     unsigned int nbus, ndev, nfunc;
567 
568     if (domain) {
569 	for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
570 	    bus = domain->bus[nbus];
571 	    if (bus) {
572 		for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
573 		    slot = bus->slot[ndev];
574 		    if (slot) {
575 			for (nfunc = 0; nfunc < MAX_PCI_FUNC; nfunc++) {
576 			    func = slot->func[nfunc];
577 			    if (func) {
578 				if (func->dev_info)
579 				    free(func->dev_info);
580 				free(func);
581 			    }
582 			}
583 			free(slot);
584 		    }
585 		}
586 		free(bus);
587 	    }
588 	}
589 	free(domain);
590     }
591 }
592 
593 /* Try to match any pci device to the appropriate kernel module */
594 /* it uses the modules.alias from the boot device */
get_module_name_from_alias(struct pci_domain * domain,char * modules_alias_path)595 int get_module_name_from_alias(struct pci_domain *domain, char *modules_alias_path)
596 {
597   char line[MAX_LINE];
598   char module_name[21]; // the module name field is 21 char long
599   char delims[]="*";    // colums are separated by spaces
600   char vendor_id[16];
601   char product_id[16];
602   char sub_vendor_id[16];
603   char sub_product_id[16];
604   FILE *f;
605   struct pci_device *dev=NULL;
606 
607   /* Intializing the linux_kernel_module for each pci device to "unknown" */
608   /* adding a dev_info member if needed */
609   for_each_pci_func(dev, domain) {
610     /* initialize the dev_info structure if it doesn't exist yet. */
611     if (! dev->dev_info) {
612       dev->dev_info = zalloc(sizeof *dev->dev_info);
613       if (!dev->dev_info)
614 	return -1;
615     }
616     for (int i=0;i<MAX_KERNEL_MODULES_PER_PCI_DEVICE;i++) {
617      if (strlen(dev->dev_info->linux_kernel_module[i])==0)
618        strlcpy(dev->dev_info->linux_kernel_module[i], "unknown",7);
619     }
620   }
621 
622   /* Opening the modules.pcimap (of a linux kernel) from the boot device */
623   f=zfopen(modules_alias_path, "r");
624   if (!f)
625     return -ENOMODULESALIAS;
626 
627   /* for each line we found in the modules.pcimap */
628   while ( fgets(line, sizeof line, f) ) {
629     /* skipping unecessary lines */
630     if ((line[0] == '#') || (strstr(line,"alias pci:v")==NULL))
631         continue;
632 
633     /* Resetting temp buffer*/
634     memset(module_name,0,sizeof(module_name));
635     memset(vendor_id,0,sizeof(vendor_id));
636     memset(sub_vendor_id,0,sizeof(sub_vendor_id));
637     memset(product_id,0,sizeof(product_id));
638     memset(sub_product_id,0,sizeof(sub_product_id));
639     strcpy(vendor_id,"0000");
640     strcpy(product_id,"0000");
641     /* ffff will be used to match any device as in modules.alias
642      * a missing subvendor/product have to be considered as  0xFFFF*/
643     strcpy(sub_product_id,"ffff");
644     strcpy(sub_vendor_id,"ffff");
645 
646     char *result = NULL;
647     int field=0;
648 
649     /* looking for the next field */
650     result = strtok(line+strlen("alias pci:v"), delims);
651     while( result != NULL ) {
652 	if (field==0) {
653 
654 		/* Searching for the vendor separator*/
655 		char *temp = strstr(result,"d");
656 		if (temp != NULL) {
657 			strlcpy(vendor_id,result,temp-result);
658 			result+=strlen(vendor_id)+1;
659 		}
660 
661 		/* Searching for the product separator*/
662 		temp = strstr(result,"sv");
663 		if (temp != NULL) {
664 			strlcpy(product_id,result,temp-result);
665 			result+=strlen(product_id)+1;
666 		}
667 
668 		/* Searching for the sub vendor separator*/
669 		temp = strstr(result,"sd");
670 		if (temp != NULL) {
671 			strlcpy(sub_vendor_id,result,temp-result);
672 			result+=strlen(sub_vendor_id)+1;
673 		}
674 
675 		/* Searching for the sub product separator*/
676 		temp = strstr(result,"bc");
677 		if (temp != NULL) {
678 			strlcpy(sub_product_id,result,temp-result);
679 			result+=strlen(sub_product_id)+1;
680 		}
681 	/* That's the module name */
682 	} else if ((strlen(result)>2) &&
683 			(result[0]==0x20))
684 		strcpy(module_name,result+1);
685 		/* We have to replace \n by \0*/
686 		module_name[strlen(module_name)-1]='\0';
687 	field++;
688 
689 	/* Searching the next field */
690         result = strtok( NULL, delims );
691     }
692 
693     /* Now we have extracted informations from the modules.alias
694      * Let's compare it with the devices we know*/
695     int int_vendor_id=hex_to_int(vendor_id);
696     int int_sub_vendor_id=hex_to_int(sub_vendor_id);
697     int int_product_id=hex_to_int(product_id);
698     int int_sub_product_id=hex_to_int(sub_product_id);
699     /* if a pci_device matches an entry, fill the linux_kernel_module with
700        the appropriate kernel module */
701     for_each_pci_func(dev, domain) {
702       if (int_vendor_id == dev->vendor &&
703 	  int_product_id == dev->product &&
704 	  (int_sub_product_id & dev->sub_product)
705 	  == dev->sub_product &&
706 	  (int_sub_vendor_id & dev->sub_vendor)
707 	  == dev->sub_vendor) {
708 	      bool found=false;
709 
710 	      /* Scan all known kernel modules for this pci device */
711 	      for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
712 
713        	      /* Try to detect if we already knew the same kernel module*/
714 	       if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
715 		      found=true;
716 		      break;
717 	       }
718 	      }
719 	      /* If we don't have this kernel module, let's add it */
720 	      if (!found) {
721 		strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name);
722 		dev->dev_info->linux_kernel_module_count++;
723 	      }
724       }
725     }
726   }
727   fclose(f);
728   return 0;
729 }
730