Home | History | Annotate | Download | only in pci
      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 */
     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 */
     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 */
     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 */
    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 */
    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 */
    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 */
    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*/
    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 
    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 */
    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