1 /* ----------------------------------------------------------------------- *
3 * Copyright 2006-2007 Erwan Velu - All Rights Reserved
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
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
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.
26 * ----------------------------------------------------------------------- */
31 * A module to extract pci informations
43 #include <syslinux/zio.h>
48 /* removing any \n found in a string */
49 static void remove_eol(char *string)
51 int j = strlen(string);
53 for (i = 0; i < j; i++)
54 if (string[i] == '\n')
58 /* converting a hexa string into its numerical value */
59 static int hex_to_int(char *hexa)
61 return strtoul(hexa, NULL, 16);
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)
70 char module_name[21]; // the module name field is 21 char long
71 char delims[]=" "; // colums are separated by spaces
74 char sub_vendor_id[16];
75 char sub_product_id[16];
77 struct pci_device *dev=NULL;
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);
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);
94 /* Opening the modules.pcimap (of a linux kernel) from the boot device */
95 f=zfopen(modules_pcimap_path, "r");
97 return -ENOMODULESPCIMAP;
99 strcpy(vendor_id,"0000");
100 strcpy(product_id,"0000");
101 strcpy(sub_product_id,"0000");
102 strcpy(sub_vendor_id,"0000");
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))
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) {
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;
132 /* Searching the next field */
133 result = strtok( NULL, delims );
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) {
150 /* Scan all known kernel modules for this pci device */
151 for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
153 /* Try to detect if we already knew the same kernel module*/
154 if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
159 /* If we don't have this kernel module, let's add it */
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++;
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)
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];
181 struct pci_device *dev;
182 bool class_mode = false;
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);
193 strlcpy(dev->dev_info->class_name, "unknown", 7);
196 /* Opening the pci.ids from the boot device */
197 f = zfopen(pciids_path, "r");
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))
207 /* Until we found a line starting with a 'C', we are not parsing classes */
210 if (class_mode == false)
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') {
216 /* ignore the two first char and then copy 2 chars (class id) */
217 strlcpy(class_id_str, &line[2], 2);
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);
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);
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')) {
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);
244 /* the sub class id is first field */
245 strlcpy(sub_class_id_str, &line[1], 2);
246 sub_class_id_str[2] = 0;
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);
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)
269 char vendor[PCI_VENDOR_NAME_SIZE];
271 char product[PCI_PRODUCT_NAME_SIZE];
273 char sub_product_id[5];
274 char sub_vendor_id[5];
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;
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);
292 strlcpy(dev->dev_info->vendor_name, "unknown", 7);
293 strlcpy(dev->dev_info->product_name, "unknown", 7);
296 /* Opening the pci.ids from the boot device */
297 f = zfopen(pciids_path, "r");
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);
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') ||
313 /* If the line doesn't start with a tab, it means that's a vendor id */
314 if (line[0] != '\t') {
316 /* the 4 first chars are the vendor_id */
317 strlcpy(vendor_id, line, 4);
319 /* the vendor name is the next field */
321 strlcpy(vendor, skipspace(strstr(line, " ")),
322 PCI_VENDOR_NAME_SIZE - 1);
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);
330 /* Unless we found a matching device, we have to skip to the next vendor */
331 skip_to_next_vendor = true;
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 */
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)) {
351 /* the product name the second field */
352 strlcpy(product, skipspace(strstr(line, " ")),
353 PCI_PRODUCT_NAME_SIZE - 1);
356 /* the product id is first field */
357 strlcpy(product_id, &line[1], 4);
360 /* init sub_product and sub_vendor */
361 strlcpy(sub_product_id, "0000", 4);
362 strlcpy(sub_vendor_id, "0000", 4);
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);
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)) {
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);
389 /* the sub_vendor id is first field */
390 strlcpy(sub_vendor_id, &line[2], 4);
391 sub_vendor_id[4] = 0;
393 /* the sub_vendor id is second field */
394 strlcpy(sub_product_id, &line[7], 4);
395 sub_product_id[4] = 0;
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);
419 /* searching if any pcidevice match our query */
420 struct match *find_pci_device(const struct pci_domain *domain,
425 const struct pci_device *dev;
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;
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) {
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 */
451 /* scanning the pci bus to find pci devices */
452 struct pci_domain *pci_scan(void)
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;
464 cfgtype = pci_set_config_type(PCI_CFG_AUTO);
466 dprintf("PCI configuration type %d\n", cfgtype);
468 if (cfgtype == PCI_CFG_NONE)
471 dprintf("Scanning PCI Buses\n");
473 for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
474 dprintf("Probing bus 0x%02x... \n", nbus);
477 for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
478 maxfunc = 1; /* Assume a single-function device */
481 for (nfunc = 0; nfunc < maxfunc; nfunc++) {
482 a = pci_mkaddr(nbus, ndev, nfunc, 0);
485 if (did == 0xffffffff || did == 0xffff0000 ||
486 did == 0x0000ffff || did == 0x00000000)
489 hdrtype = pci_readb(a + 0x0e);
492 maxfunc = MAX_PCI_FUNC; /* Multifunction device */
494 rcid = pci_readl(a + 0x08);
495 sid = pci_readl(a + 0x2c);
498 domain = zalloc(sizeof *domain);
503 bus = zalloc(sizeof *bus);
506 domain->bus[nbus] = bus;
509 slot = zalloc(sizeof *slot);
512 bus->slot[ndev] = slot;
514 func = zalloc(sizeof *func);
518 slot->func[nfunc] = func;
521 func->svid_sdid = sid;
522 func->rid_class = rcid;
525 ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n",
526 nbus, did, did >> 16, (did << 16) >> 16, sid, rcid & 0xff);
534 free_pci_domain(domain);
538 /* gathering additional configuration*/
539 void gather_additional_pci_config(struct pci_domain *domain)
541 struct pci_device *dev;
545 cfgtype = pci_set_config_type(PCI_CFG_AUTO);
546 if (cfgtype == PCI_CFG_NONE)
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) {
556 dev->dev_info->irq = pci_readb(pci_addr + 0x3c);
557 dev->dev_info->latency = pci_readb(pci_addr + 0x0d);
561 void free_pci_domain(struct pci_domain *domain)
564 struct pci_slot *slot;
565 struct pci_device *func;
566 unsigned int nbus, ndev, nfunc;
569 for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) {
570 bus = domain->bus[nbus];
572 for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) {
573 slot = bus->slot[ndev];
575 for (nfunc = 0; nfunc < MAX_PCI_FUNC; nfunc++) {
576 func = slot->func[nfunc];
579 free(func->dev_info);
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)
598 char module_name[21]; // the module name field is 21 char long
599 char delims[]="*"; // colums are separated by spaces
602 char sub_vendor_id[16];
603 char sub_product_id[16];
605 struct pci_device *dev=NULL;
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);
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);
622 /* Opening the modules.pcimap (of a linux kernel) from the boot device */
623 f=zfopen(modules_alias_path, "r");
625 return -ENOMODULESALIAS;
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))
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");
649 /* looking for the next field */
650 result = strtok(line+strlen("alias pci:v"), delims);
651 while( result != NULL ) {
654 /* Searching for the vendor separator*/
655 char *temp = strstr(result,"d");
657 strlcpy(vendor_id,result,temp-result);
658 result+=strlen(vendor_id)+1;
661 /* Searching for the product separator*/
662 temp = strstr(result,"sv");
664 strlcpy(product_id,result,temp-result);
665 result+=strlen(product_id)+1;
668 /* Searching for the sub vendor separator*/
669 temp = strstr(result,"sd");
671 strlcpy(sub_vendor_id,result,temp-result);
672 result+=strlen(sub_vendor_id)+1;
675 /* Searching for the sub product separator*/
676 temp = strstr(result,"bc");
678 strlcpy(sub_product_id,result,temp-result);
679 result+=strlen(sub_product_id)+1;
681 /* That's the module name */
682 } else if ((strlen(result)>2) &&
684 strcpy(module_name,result+1);
685 /* We have to replace \n by \0*/
686 module_name[strlen(module_name)-1]='\0';
689 /* Searching the next field */
690 result = strtok( NULL, delims );
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) {
710 /* Scan all known kernel modules for this pci device */
711 for (int i=0; i<dev->dev_info->linux_kernel_module_count; i++) {
713 /* Try to detect if we already knew the same kernel module*/
714 if (strstr(dev->dev_info->linux_kernel_module[i], module_name)) {
719 /* If we don't have this kernel module, let's add it */
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++;