static void platform_find_pci_info(struct xf86_platform_device *pd, char *busid) { struct pci_slot_match devmatch; struct pci_device *info; struct pci_device_iterator *iter; int ret; ret = sscanf(busid, "pci:%04x:%02x:%02x.%u", &devmatch.domain, &devmatch.bus, &devmatch.dev, &devmatch.func); if (ret != 4) return; iter = pci_slot_match_iterator_create(&devmatch); info = pci_device_next(iter); if (info) { pd->pdev = info; pci_device_probe(info); if (pci_device_is_boot_vga(info)) { primaryBus.type = BUS_PLATFORM; primaryBus.id.plat = pd; } } pci_iterator_destroy(iter); }
static struct pci_device* ati_device_get_primary(void) { struct pci_device *device = NULL; struct pci_device_iterator *device_iter; device_iter = pci_slot_match_iterator_create(NULL); while ((device = pci_device_next(device_iter)) != NULL) { if (xf86IsPrimaryPci(device)) break; } pci_iterator_destroy(device_iter); return device; }
static struct pci_device * ati_device_get_indexed(int index) { struct pci_device *device = NULL; struct pci_device_iterator *device_iter; int count = 0; device_iter = pci_slot_match_iterator_create(NULL); while ((device = pci_device_next(device_iter)) != NULL) { if (device->vendor_id == PCI_VENDOR_ATI) { if (count == index) return device; count++; } } return NULL; }
static void pci_userspace_init(void) { /* FIXME: add a hook to make rump call this, once and only once */ static int is_init = 0; if (is_init) return; is_init = 1; if (get_privileged_ports (&master_host, &master_device)) err(1, "get_privileged_ports"); pci_system_init (); struct pci_device_iterator *dev_iter; struct pci_device *pci_dev; dev_iter = pci_slot_match_iterator_create (NULL); int i = 0; while ((pci_dev = pci_device_next (dev_iter)) != NULL) { pci_devices[i++] = pci_dev; } }
static int hwloc_look_pci(struct hwloc_backend *backend) { struct hwloc_topology *topology = backend->topology; struct hwloc_obj *first_obj = NULL, *last_obj = NULL; int ret; struct pci_device_iterator *iter; struct pci_device *pcidev; #ifdef HWLOC_LINUX_SYS DIR *dir; #endif if (!(hwloc_topology_get_flags(topology) & (HWLOC_TOPOLOGY_FLAG_IO_DEVICES|HWLOC_TOPOLOGY_FLAG_WHOLE_IO))) return 0; if (hwloc_get_next_pcidev(topology, NULL)) { hwloc_debug("%s", "PCI objects already added, ignoring pci backend.\n"); return 0; } if (!hwloc_topology_is_thissystem(topology)) { hwloc_debug("%s", "\nno PCI detection (not thissystem)\n"); return 0; } hwloc_debug("%s", "\nScanning PCI buses...\n"); /* initialize PCI scanning */ ret = pci_system_init(); if (ret) { hwloc_debug("%s", "Can not initialize libpciaccess\n"); return -1; } iter = pci_slot_match_iterator_create(NULL); /* iterate over devices */ for (pcidev = pci_device_next(iter); pcidev; pcidev = pci_device_next(iter)) { const char *vendorname, *devicename, *fullname; unsigned char config_space_cache[CONFIG_SPACE_CACHESIZE]; struct hwloc_obj *obj; unsigned os_index; unsigned domain; unsigned device_class; unsigned short tmp16; char name[128]; unsigned offset; /* initialize the config space in case we fail to read it (missing permissions, etc). */ memset(config_space_cache, 0xff, CONFIG_SPACE_CACHESIZE); pci_device_probe(pcidev); pci_device_cfg_read(pcidev, config_space_cache, 0, CONFIG_SPACE_CACHESIZE, NULL); /* try to read the domain */ domain = pcidev->domain; /* try to read the device_class */ device_class = pcidev->device_class >> 8; /* fixup SR-IOV buggy VF device/vendor IDs */ if (0xffff == pcidev->vendor_id && 0xffff == pcidev->device_id) { /* SR-IOV puts ffff:ffff in Virtual Function config space. * The actual VF device ID is stored at a special (dynamic) location in the Physical Function config space. * VF and PF have the same vendor ID. * * libpciaccess just returns ffff:ffff, needs to be fixed. * linuxpci is OK because sysfs files are already fixed the kernel. * (pciutils is OK when it uses those Linux sysfs files.) * * Reading these files is an easy way to work around the libpciaccess issue on Linux, * but we have no way to know if this is caused by SR-IOV or not. * * TODO: * If PF has CAP_ID_PCIX or CAP_ID_EXP (offset>0), * look for extended capability PCI_EXT_CAP_ID_SRIOV (need extended config space (more than 256 bytes)), * then read the VF device ID after it (PCI_IOV_DID bytes later). * Needs access to extended config space (needs root on Linux). * TODO: * Add string info attributes in VF and PF objects? */ #ifdef HWLOC_LINUX_SYS /* Workaround for Linux (the kernel returns the VF device/vendor IDs). */ char path[64]; char value[16]; FILE *file; size_t read; snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vendor", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { read = fread(value, 1, sizeof(value), file); fclose(file); if (read) /* fixup the pciaccess struct so that pci_device_get_vendor_name() is correct later. */ pcidev->vendor_id = strtoul(value, NULL, 16); } snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/device", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { read = fread(value, 1, sizeof(value), file); fclose(file); if (read) /* fixup the pciaccess struct so that pci_device_get_device_name() is correct later. */ pcidev->device_id = strtoul(value, NULL, 16); } #endif } /* might be useful for debugging (note that domain might be truncated) */ os_index = (domain << 20) + (pcidev->bus << 12) + (pcidev->dev << 4) + pcidev->func; obj = hwloc_alloc_setup_object(HWLOC_OBJ_PCI_DEVICE, os_index); obj->attr->pcidev.domain = domain; obj->attr->pcidev.bus = pcidev->bus; obj->attr->pcidev.dev = pcidev->dev; obj->attr->pcidev.func = pcidev->func; obj->attr->pcidev.vendor_id = pcidev->vendor_id; obj->attr->pcidev.device_id = pcidev->device_id; obj->attr->pcidev.class_id = device_class; obj->attr->pcidev.revision = config_space_cache[PCI_REVISION_ID]; obj->attr->pcidev.linkspeed = 0; /* unknown */ offset = hwloc_pci_find_cap(config_space_cache, PCI_CAP_ID_EXP); if (offset > 0 && offset + 20 /* size of PCI express block up to link status */ <= CONFIG_SPACE_CACHESIZE) hwloc_pci_find_linkspeed(config_space_cache, offset, &obj->attr->pcidev.linkspeed); hwloc_pci_prepare_bridge(obj, config_space_cache); if (obj->type == HWLOC_OBJ_PCI_DEVICE) { memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_VENDOR_ID], sizeof(tmp16)); obj->attr->pcidev.subvendor_id = tmp16; memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_ID], sizeof(tmp16)); obj->attr->pcidev.subdevice_id = tmp16; } else { /* TODO: * bridge must lookup PCI_CAP_ID_SSVID and then look at offset+PCI_SSVID_VENDOR/DEVICE_ID * cardbus must look at PCI_CB_SUBSYSTEM_VENDOR_ID and PCI_CB_SUBSYSTEM_ID */ } /* get the vendor name */ vendorname = pci_device_get_vendor_name(pcidev); if (vendorname && *vendorname) hwloc_obj_add_info(obj, "PCIVendor", vendorname); /* get the device name */ devicename = pci_device_get_device_name(pcidev); if (devicename && *devicename) hwloc_obj_add_info(obj, "PCIDevice", devicename); /* generate or get the fullname */ snprintf(name, sizeof(name), "%s%s%s", vendorname ? vendorname : "", vendorname && devicename ? " " : "", devicename ? devicename : ""); fullname = name; if (*name) obj->name = strdup(name); hwloc_debug(" %04x:%02x:%02x.%01x %04x %04x:%04x %s\n", domain, pcidev->bus, pcidev->dev, pcidev->func, device_class, pcidev->vendor_id, pcidev->device_id, fullname && *fullname ? fullname : "??"); /* queue the object for now */ if (first_obj) last_obj->next_sibling = obj; else first_obj = obj; last_obj = obj; } /* finalize device scanning */ pci_iterator_destroy(iter); pci_system_cleanup(); #ifdef HWLOC_LINUX_SYS dir = opendir("/sys/bus/pci/slots/"); if (dir) { struct dirent *dirent; while ((dirent = readdir(dir)) != NULL) { char path[64]; FILE *file; if (dirent->d_name[0] == '.') continue; snprintf(path, sizeof(path), "/sys/bus/pci/slots/%s/address", dirent->d_name); file = fopen(path, "r"); if (file) { unsigned domain, bus, dev; if (fscanf(file, "%x:%x:%x", &domain, &bus, &dev) == 3) { hwloc_obj_t obj = first_obj; while (obj) { if (obj->attr->pcidev.domain == domain && obj->attr->pcidev.bus == bus && obj->attr->pcidev.dev == dev && obj->attr->pcidev.func == 0) { hwloc_obj_add_info(obj, "PCISlot", dirent->d_name); break; } obj = obj->next_sibling; } } fclose(file); } } closedir(dir); } #endif return hwloc_insert_pci_device_list(backend, first_obj); }
void xf86PciProbe(void) { int i = 0, k; int num = 0; struct pci_device *info; struct pci_device_iterator *iter; struct pci_device **xf86PciVideoInfo = NULL; if (!xf86scanpci()) { xf86PciVideoInfo = NULL; return; } iter = pci_slot_match_iterator_create(&xf86IsolateDevice); while ((info = pci_device_next(iter)) != NULL) { if (PCIINFOCLASSES(info->device_class)) { num++; xf86PciVideoInfo = xnfreallocarray(xf86PciVideoInfo, num + 1, sizeof(struct pci_device *)); xf86PciVideoInfo[num] = NULL; xf86PciVideoInfo[num - 1] = info; pci_device_probe(info); if (primaryBus.type == BUS_NONE && pci_device_is_boot_vga(info)) { primaryBus.type = BUS_PCI; primaryBus.id.pci = info; } info->user_data = 0; } } free(iter); /* If we haven't found a primary device try a different heuristic */ if (primaryBus.type == BUS_NONE && num) { for (i = 0; i < num; i++) { uint16_t command; info = xf86PciVideoInfo[i]; pci_device_cfg_read_u16(info, &command, 4); if ((command & PCI_CMD_MEM_ENABLE) && ((num == 1) || IS_VGA(info->device_class))) { if (primaryBus.type == BUS_NONE) { primaryBus.type = BUS_PCI; primaryBus.id.pci = info; } else { xf86Msg(X_NOTICE, "More than one possible primary device found\n"); primaryBus.type ^= (BusType) (-1); } } } } /* Print a summary of the video devices found */ for (k = 0; k < num; k++) { const char *prim = " "; Bool memdone = FALSE, iodone = FALSE; info = xf86PciVideoInfo[k]; if (!PCIALWAYSPRINTCLASSES(info->device_class)) continue; if (xf86IsPrimaryPci(info)) prim = "*"; xf86Msg(X_PROBED, "PCI:%s(%u:%u:%u:%u) %04x:%04x:%04x:%04x ", prim, info->domain, info->bus, info->dev, info->func, info->vendor_id, info->device_id, info->subvendor_id, info->subdevice_id); xf86ErrorF("rev %d", info->revision); for (i = 0; i < 6; i++) { struct pci_mem_region *r = &info->regions[i]; if (r->size && !r->is_IO) { if (!memdone) { xf86ErrorF(", Mem @ "); memdone = TRUE; } else xf86ErrorF(", "); xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); } } for (i = 0; i < 6; i++) { struct pci_mem_region *r = &info->regions[i]; if (r->size && r->is_IO) { if (!iodone) { xf86ErrorF(", I/O @ "); iodone = TRUE; } else xf86ErrorF(", "); xf86ErrorF("0x%08lx/%ld", (long) r->base_addr, (long) r->size); } } if (info->rom_size) { xf86ErrorF(", BIOS @ 0x\?\?\?\?\?\?\?\?/%ld", (long) info->rom_size); } xf86ErrorF("\n"); } free(xf86PciVideoInfo); }
/* This function is used to provide a workaround for binary drivers that * don't export their PCI ID's properly. If distros don't end up using this * feature it can and should be removed because the symbol-based resolution * scheme should be the primary one */ static void matchDriverFromFiles (char** matches, uint16_t match_vendor, uint16_t match_chip) { DIR *idsdir; FILE *fp; struct dirent *direntry; char *line = NULL; size_t len; ssize_t read; char path_name[256], vendor_str[5], chip_str[5]; uint16_t vendor, chip; int i, j; idsdir = opendir(PCI_TXT_IDS_PATH); if (!idsdir) return; xf86Msg(X_INFO, "Scanning %s directory for additional PCI ID's supported by the drivers\n", PCI_TXT_IDS_PATH); direntry = readdir(idsdir); /* Read the directory */ while (direntry) { if (direntry->d_name[0] == '.') { direntry = readdir(idsdir); continue; } len = strlen(direntry->d_name); /* A tiny bit of sanity checking. We should probably do better */ if (strncmp(&(direntry->d_name[len-4]), ".ids", 4) == 0) { /* We need the full path name to open the file */ strncpy(path_name, PCI_TXT_IDS_PATH, 256); strncat(path_name, "/", 1); strncat(path_name, direntry->d_name, (256 - strlen(path_name) - 1)); fp = fopen(path_name, "r"); if (fp == NULL) { xf86Msg(X_ERROR, "Could not open %s for reading. Exiting.\n", path_name); goto end; } /* Read the file */ #ifdef __GLIBC__ while ((read = getline(&line, &len, fp)) != -1) { #else while ((line = fgetln(fp, &len)) != (char *)NULL) { #endif /* __GLIBC __ */ xchomp(line); if (isdigit(line[0])) { strncpy(vendor_str, line, 4); vendor_str[4] = '\0'; vendor = (int)strtol(vendor_str, NULL, 16); if ((strlen(&line[4])) == 0) { chip_str[0] = '\0'; chip = -1; } else { /* Handle trailing whitespace */ if (isspace(line[4])) { chip_str[0] = '\0'; chip = -1; } else { /* Ok, it's a real ID */ strncpy(chip_str, &line[4], 4); chip_str[4] = '\0'; chip = (int)strtol(chip_str, NULL, 16); } } if (vendor == match_vendor && chip == match_chip ) { i = 0; while (matches[i]) { i++; } matches[i] = (char*)xalloc(sizeof(char) * strlen(direntry->d_name) - 3); if (!matches[i]) { xf86Msg(X_ERROR, "Could not allocate space for the module name. Exiting.\n"); goto end; } /* hack off the .ids suffix. This should guard * against other problems, but it will end up * taking off anything after the first '.' */ for (j = 0; j < (strlen(direntry->d_name) - 3) ; j++) { if (direntry->d_name[j] == '.') { matches[i][j] = '\0'; break; } else { matches[i][j] = direntry->d_name[j]; } } xf86Msg(X_INFO, "Matched %s from file name %s\n", matches[i], direntry->d_name); } } else { /* TODO Handle driver overrides here */ } } fclose(fp); } direntry = readdir(idsdir); } end: xfree(line); closedir(idsdir); } #endif /* __linux__ */ static void listPossibleVideoDrivers(char *matches[], int nmatches) { struct pci_device * info = NULL; struct pci_device_iterator *iter; int i; for (i = 0 ; i < nmatches ; i++) { matches[i] = NULL; } i = 0; #ifdef sun /* Check for driver type based on /dev/fb type and if valid, use it instead of PCI bus probe results */ if (xf86Info.consoleFd >= 0) { struct vis_identifier visid; const char *cp; extern char xf86SolarisFbDev[PATH_MAX]; int iret; SYSCALL(iret = ioctl(xf86Info.consoleFd, VIS_GETIDENTIFIER, &visid)); if (iret < 0) { int fbfd; fbfd = open(xf86SolarisFbDev, O_RDONLY); if (fbfd >= 0) { SYSCALL(iret = ioctl(fbfd, VIS_GETIDENTIFIER, &visid)); close(fbfd); } } if (iret < 0) { xf86Msg(X_WARNING, "could not get frame buffer identifier from %s\n", xf86SolarisFbDev); } else { xf86Msg(X_PROBED, "console driver: %s\n", visid.name); /* Special case from before the general case was set */ if (strcmp(visid.name, "NVDAnvda") == 0) { matches[i++] = xnfstrdup("nvidia"); } /* General case - split into vendor name (initial all-caps prefix) & driver name (rest of the string). */ if (strcmp(visid.name, "SUNWtext") != 0) { for (cp = visid.name; (*cp != '\0') && isupper(*cp); cp++) { /* find end of all uppercase vendor section */ } if ((cp != visid.name) && (*cp != '\0')) { char *driverName = xnfstrdup(cp); char *vendorName = xnfstrdup(visid.name); vendorName[cp - visid.name] = '\0'; matches[i++] = vendorName; matches[i++] = driverName; } } } } #endif #ifdef __sparc__ { char *sbusDriver = sparcDriverName(); if (sbusDriver) matches[i++] = xnfstrdup(sbusDriver); } #endif /* Find the primary device, and get some information about it. */ iter = pci_slot_match_iterator_create(NULL); while ((info = pci_device_next(iter)) != NULL) { if (xf86IsPrimaryPci(info)) { break; } } pci_iterator_destroy(iter); if (!info) { ErrorF("Primary device is not PCI\n"); } #ifdef __linux__ else { matchDriverFromFiles(matches, info->vendor_id, info->device_id); } #endif /* __linux__ */ for (i = 0; (i < nmatches) && (matches[i]); i++) { /* find end of matches list */ } if ((info != NULL) && (i < nmatches)) { i += videoPtrToDriverList(info, &(matches[i]), nmatches - i); } /* Fallback to platform default hardware */ if (i < (nmatches - 1)) { #if defined(__i386__) || defined(__amd64__) || defined(__hurd__) matches[i++] = xnfstrdup("vesa"); #elif defined(__sparc__) && !defined(sun) matches[i++] = xnfstrdup("sunffb"); #endif } /* Fallback to platform default frame buffer driver */ if (i < (nmatches - 1)) { #if !defined(__linux__) && defined(__sparc__) matches[i++] = xnfstrdup("wsfb"); #else matches[i++] = xnfstrdup("fbdev"); #endif } } /* copy a screen section and enter the desired driver * and insert it at i in the list of screens */ static Bool copyScreen(confScreenPtr oscreen, GDevPtr odev, int i, char *driver) { GDevPtr cptr = NULL; xf86ConfigLayout.screens[i].screen = xnfcalloc(1, sizeof(confScreenRec)); if(!xf86ConfigLayout.screens[i].screen) return FALSE; memcpy(xf86ConfigLayout.screens[i].screen, oscreen, sizeof(confScreenRec)); cptr = xcalloc(1, sizeof(GDevRec)); if (!cptr) return FALSE; memcpy(cptr, odev, sizeof(GDevRec)); cptr->identifier = Xprintf("Autoconfigured Video Device %s", driver); cptr->driver = driver; /* now associate the new driver entry with the new screen entry */ xf86ConfigLayout.screens[i].screen->device = cptr; cptr->myScreenSection = xf86ConfigLayout.screens[i].screen; return TRUE; }
static int hwloc_look_pci(struct hwloc_backend *backend) { struct hwloc_topology *topology = backend->topology; struct hwloc_obj *first_obj = NULL, *last_obj = NULL; #ifdef HWLOC_HAVE_LIBPCIACCESS int ret; struct pci_device_iterator *iter; struct pci_device *pcidev; #else /* HWLOC_HAVE_PCIUTILS */ struct pci_access *pciaccess; struct pci_dev *pcidev; #endif if (!(hwloc_topology_get_flags(topology) & (HWLOC_TOPOLOGY_FLAG_IO_DEVICES|HWLOC_TOPOLOGY_FLAG_WHOLE_IO))) return 0; if (hwloc_get_next_pcidev(topology, NULL)) { hwloc_debug("%s", "PCI objects already added, ignoring pci backend.\n"); return 0; } if (!hwloc_topology_is_thissystem(topology)) { hwloc_debug("%s", "\nno PCI detection (not thissystem)\n"); return 0; } hwloc_debug("%s", "\nScanning PCI buses...\n"); /* initialize PCI scanning */ #ifdef HWLOC_HAVE_LIBPCIACCESS ret = pci_system_init(); if (ret) { hwloc_debug("%s", "Can not initialize libpciaccess\n"); return -1; } iter = pci_slot_match_iterator_create(NULL); #else /* HWLOC_HAVE_PCIUTILS */ pciaccess = pci_alloc(); pciaccess->error = hwloc_pci_error; pciaccess->warning = hwloc_pci_warning; if (setjmp(err_buf)) { pci_cleanup(pciaccess); return -1; } pci_init(pciaccess); pci_scan_bus(pciaccess); #endif /* iterate over devices */ #ifdef HWLOC_HAVE_LIBPCIACCESS for (pcidev = pci_device_next(iter); pcidev; pcidev = pci_device_next(iter)) #else /* HWLOC_HAVE_PCIUTILS */ for (pcidev = pciaccess->devices; pcidev; pcidev = pcidev->next) #endif { const char *vendorname, *devicename, *fullname; unsigned char config_space_cache[CONFIG_SPACE_CACHESIZE]; struct hwloc_obj *obj; unsigned os_index; unsigned domain; unsigned device_class; unsigned short tmp16; char name[128]; unsigned offset; #ifdef HWLOC_HAVE_PCI_FIND_CAP struct pci_cap *cap; #endif /* initialize the config space in case we fail to read it (missing permissions, etc). */ memset(config_space_cache, 0xff, CONFIG_SPACE_CACHESIZE); #ifdef HWLOC_HAVE_LIBPCIACCESS pci_device_probe(pcidev); pci_device_cfg_read(pcidev, config_space_cache, 0, CONFIG_SPACE_CACHESIZE, NULL); #else /* HWLOC_HAVE_PCIUTILS */ pci_read_block(pcidev, 0, config_space_cache, CONFIG_SPACE_CACHESIZE); /* doesn't even tell how much it actually reads */ #endif /* try to read the domain */ #if (defined HWLOC_HAVE_LIBPCIACCESS) || (defined HWLOC_HAVE_PCIDEV_DOMAIN) domain = pcidev->domain; #else domain = 0; /* default domain number */ #endif /* try to read the device_class */ #ifdef HWLOC_HAVE_LIBPCIACCESS device_class = pcidev->device_class >> 8; #else /* HWLOC_HAVE_PCIUTILS */ #ifdef HWLOC_HAVE_PCIDEV_DEVICE_CLASS device_class = pcidev->device_class; #else device_class = config_space_cache[PCI_CLASS_DEVICE] | (config_space_cache[PCI_CLASS_DEVICE+1] << 8); #endif #endif /* might be useful for debugging (note that domain might be truncated) */ os_index = (domain << 20) + (pcidev->bus << 12) + (pcidev->dev << 4) + pcidev->func; obj = hwloc_alloc_setup_object(HWLOC_OBJ_PCI_DEVICE, os_index); obj->attr->pcidev.domain = domain; obj->attr->pcidev.bus = pcidev->bus; obj->attr->pcidev.dev = pcidev->dev; obj->attr->pcidev.func = pcidev->func; obj->attr->pcidev.vendor_id = pcidev->vendor_id; obj->attr->pcidev.device_id = pcidev->device_id; obj->attr->pcidev.class_id = device_class; obj->attr->pcidev.revision = config_space_cache[PCI_REVISION_ID]; obj->attr->pcidev.linkspeed = 0; /* unknown */ #ifdef HWLOC_HAVE_PCI_FIND_CAP cap = pci_find_cap(pcidev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL); offset = cap ? cap->addr : 0; #else offset = hwloc_pci_find_cap(config_space_cache, PCI_CAP_ID_EXP); #endif /* HWLOC_HAVE_PCI_FIND_CAP */ if (0xffff == pcidev->vendor_id && 0xffff == pcidev->device_id) { /* SR-IOV puts ffff:ffff in Virtual Function config space. * The actual VF device ID is stored at a special (dynamic) location in the Physical Function config space. * VF and PF have the same vendor ID. * * libpciaccess just returns ffff:ffff, needs to be fixed. * linuxpci is OK because sysfs files are already fixed the kernel. * pciutils is OK when it uses those Linux sysfs files. * * Reading these files is an easy way to work around the libpciaccess issue on Linux, * but we have no way to know if this is caused by SR-IOV or not. * * TODO: * If PF has CAP_ID_PCIX or CAP_ID_EXP (offset>0), * look for extended capability PCI_EXT_CAP_ID_SRIOV (need extended config space (more than 256 bytes)), * then read the VF device ID after it (PCI_IOV_DID bytes later). * Needs access to extended config space (needs root on Linux). * TODO: * Add string info attributes in VF and PF objects? */ #ifdef HWLOC_LINUX_SYS /* Workaround for Linux (the kernel returns the VF device/vendor IDs). */ char path[64]; char value[16]; FILE *file; snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vendor", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { fread(value, sizeof(value), 1, file); fclose(file); obj->attr->pcidev.vendor_id = strtoul(value, NULL, 16); } snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/device", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { fread(value, sizeof(value), 1, file); fclose(file); obj->attr->pcidev.device_id = strtoul(value, NULL, 16); } #endif } if (offset > 0 && offset + 20 /* size of PCI express block up to link status */ <= CONFIG_SPACE_CACHESIZE) hwloc_pci_find_linkspeed(config_space_cache, offset, &obj->attr->pcidev.linkspeed); hwloc_pci_prepare_bridge(obj, config_space_cache); if (obj->type == HWLOC_OBJ_PCI_DEVICE) { memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_VENDOR_ID], sizeof(tmp16)); obj->attr->pcidev.subvendor_id = tmp16; memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_ID], sizeof(tmp16)); obj->attr->pcidev.subdevice_id = tmp16; } else { /* TODO: * bridge must lookup PCI_CAP_ID_SSVID and then look at offset+PCI_SSVID_VENDOR/DEVICE_ID * cardbus must look at PCI_CB_SUBSYSTEM_VENDOR_ID and PCI_CB_SUBSYSTEM_ID */ } /* starting from pciutils 2.2, pci_lookup_name() takes a variable number * of arguments, and supports the PCI_LOOKUP_NO_NUMBERS flag. */ /* get the vendor name */ #ifdef HWLOC_HAVE_LIBPCIACCESS vendorname = pci_device_get_vendor_name(pcidev); #else /* HWLOC_HAVE_PCIUTILS */ vendorname = pci_lookup_name(pciaccess, name, sizeof(name), #if HAVE_DECL_PCI_LOOKUP_NO_NUMBERS PCI_LOOKUP_VENDOR|PCI_LOOKUP_NO_NUMBERS, pcidev->vendor_id #else PCI_LOOKUP_VENDOR, pcidev->vendor_id, 0, 0, 0 #endif ); #endif /* HWLOC_HAVE_PCIUTILS */ if (vendorname && *vendorname) hwloc_obj_add_info(obj, "PCIVendor", vendorname); /* get the device name */ #ifdef HWLOC_HAVE_LIBPCIACCESS devicename = pci_device_get_device_name(pcidev); #else /* HWLOC_HAVE_PCIUTILS */ devicename = pci_lookup_name(pciaccess, name, sizeof(name), #if HAVE_DECL_PCI_LOOKUP_NO_NUMBERS PCI_LOOKUP_DEVICE|PCI_LOOKUP_NO_NUMBERS, pcidev->vendor_id, pcidev->device_id #else PCI_LOOKUP_DEVICE, pcidev->vendor_id, pcidev->device_id, 0, 0 #endif ); #endif /* HWLOC_HAVE_PCIUTILS */ if (devicename && *devicename) hwloc_obj_add_info(obj, "PCIDevice", devicename); /* generate or get the fullname */ #ifdef HWLOC_HAVE_LIBPCIACCESS snprintf(name, sizeof(name), "%s%s%s", vendorname ? vendorname : "", vendorname && devicename ? " " : "", devicename ? devicename : ""); fullname = name; if (*name) obj->name = strdup(name); #else /* HWLOC_HAVE_PCIUTILS */ fullname = pci_lookup_name(pciaccess, name, sizeof(name), #if HAVE_DECL_PCI_LOOKUP_NO_NUMBERS PCI_LOOKUP_VENDOR|PCI_LOOKUP_DEVICE|PCI_LOOKUP_NO_NUMBERS, pcidev->vendor_id, pcidev->device_id #else PCI_LOOKUP_VENDOR|PCI_LOOKUP_DEVICE, pcidev->vendor_id, pcidev->device_id, 0, 0 #endif ); if (fullname && *fullname) obj->name = strdup(fullname); #endif /* HWLOC_HAVE_PCIUTILS */ hwloc_debug(" %04x:%02x:%02x.%01x %04x %04x:%04x %s\n", domain, pcidev->bus, pcidev->dev, pcidev->func, device_class, pcidev->vendor_id, pcidev->device_id, fullname && *fullname ? fullname : "??"); /* queue the object for now */ if (first_obj) last_obj->next_sibling = obj; else first_obj = obj; last_obj = obj; } /* finalize device scanning */ #ifdef HWLOC_HAVE_LIBPCIACCESS pci_iterator_destroy(iter); pci_system_cleanup(); #else /* HWLOC_HAVE_PCIUTILS */ pci_cleanup(pciaccess); #endif return hwloc_insert_pci_device_list(backend, first_obj); }
static int hwloc_look_pci(struct hwloc_backend *backend) { struct hwloc_topology *topology = backend->topology; enum hwloc_type_filter_e pfilter, bfilter; struct hwloc_obj *tree = NULL, *tmp; int ret; struct pci_device_iterator *iter; struct pci_device *pcidev; hwloc_topology_get_type_filter(topology, HWLOC_OBJ_PCI_DEVICE, &pfilter); hwloc_topology_get_type_filter(topology, HWLOC_OBJ_BRIDGE, &bfilter); if (bfilter == HWLOC_TYPE_FILTER_KEEP_NONE && pfilter == HWLOC_TYPE_FILTER_KEEP_NONE) return 0; /* don't do anything if another backend attached PCI already * (they are attached to root until later in the core discovery) */ tmp = hwloc_get_root_obj(topology)->io_first_child; while (tmp) { if (tmp->type == HWLOC_OBJ_PCI_DEVICE || (tmp->type == HWLOC_OBJ_BRIDGE && tmp->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI)) { hwloc_debug("%s", "PCI objects already added, ignoring linuxpci backend.\n"); return 0; } tmp = tmp->next_sibling; } hwloc_debug("%s", "\nScanning PCI buses...\n"); /* initialize PCI scanning */ ret = pci_system_init(); if (ret) { hwloc_debug("%s", "Can not initialize libpciaccess\n"); return -1; } iter = pci_slot_match_iterator_create(NULL); /* iterate over devices */ for (pcidev = pci_device_next(iter); pcidev; pcidev = pci_device_next(iter)) { const char *vendorname, *devicename; unsigned char config_space_cache[CONFIG_SPACE_CACHESIZE]; hwloc_obj_type_t type; struct hwloc_obj *obj; unsigned domain; unsigned device_class; unsigned short tmp16; unsigned offset; /* initialize the config space in case we fail to read it (missing permissions, etc). */ memset(config_space_cache, 0xff, CONFIG_SPACE_CACHESIZE); pci_device_probe(pcidev); pci_device_cfg_read(pcidev, config_space_cache, 0, CONFIG_SPACE_CACHESIZE, NULL); /* try to read the domain */ domain = pcidev->domain; /* try to read the device_class */ device_class = pcidev->device_class >> 8; /* bridge or pci dev? */ type = hwloc_pcidisc_check_bridge_type(device_class, config_space_cache); /* filtered? */ if (type == HWLOC_OBJ_PCI_DEVICE) { enum hwloc_type_filter_e filter; hwloc_topology_get_type_filter(topology, HWLOC_OBJ_PCI_DEVICE, &filter); if (filter == HWLOC_TYPE_FILTER_KEEP_NONE) continue; if (filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT && !hwloc_filter_check_pcidev_subtype_important(device_class)) continue; } else if (type == HWLOC_OBJ_BRIDGE) { enum hwloc_type_filter_e filter; hwloc_topology_get_type_filter(topology, HWLOC_OBJ_BRIDGE, &filter); if (filter == HWLOC_TYPE_FILTER_KEEP_NONE) continue; /* HWLOC_TYPE_FILTER_KEEP_IMPORTANT filtered later in the core */ } /* fixup SR-IOV buggy VF device/vendor IDs */ if (0xffff == pcidev->vendor_id && 0xffff == pcidev->device_id) { /* SR-IOV puts ffff:ffff in Virtual Function config space. * The actual VF device ID is stored at a special (dynamic) location in the Physical Function config space. * VF and PF have the same vendor ID. * * libpciaccess just returns ffff:ffff, needs to be fixed. * linuxpci is OK because sysfs files are already fixed in the kernel. * (pciutils is OK when it uses those Linux sysfs files.) * * Reading these files is an easy way to work around the libpciaccess issue on Linux, * but we have no way to know if this is caused by SR-IOV or not. * * TODO: * If PF has CAP_ID_PCIX or CAP_ID_EXP (offset>0), * look for extended capability PCI_EXT_CAP_ID_SRIOV (need extended config space (more than 256 bytes)), * then read the VF device ID after it (PCI_IOV_DID bytes later). * Needs access to extended config space (needs root on Linux). * TODO: * Add string info attributes in VF and PF objects? */ #ifdef HWLOC_LINUX_SYS /* Workaround for Linux (the kernel returns the VF device/vendor IDs). */ char path[64]; char value[16]; FILE *file; size_t read; snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vendor", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { read = fread(value, 1, sizeof(value), file); fclose(file); if (read) /* fixup the pciaccess struct so that pci_device_get_vendor_name() is correct later. */ pcidev->vendor_id = strtoul(value, NULL, 16); } snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/device", domain, pcidev->bus, pcidev->dev, pcidev->func); file = fopen(path, "r"); if (file) { read = fread(value, 1, sizeof(value), file); fclose(file); if (read) /* fixup the pciaccess struct so that pci_device_get_device_name() is correct later. */ pcidev->device_id = strtoul(value, NULL, 16); } #endif } obj = hwloc_alloc_setup_object(topology, type, HWLOC_UNKNOWN_INDEX); obj->attr->pcidev.domain = domain; obj->attr->pcidev.bus = pcidev->bus; obj->attr->pcidev.dev = pcidev->dev; obj->attr->pcidev.func = pcidev->func; obj->attr->pcidev.vendor_id = pcidev->vendor_id; obj->attr->pcidev.device_id = pcidev->device_id; obj->attr->pcidev.class_id = device_class; obj->attr->pcidev.revision = config_space_cache[PCI_REVISION_ID]; obj->attr->pcidev.linkspeed = 0; /* unknown */ offset = hwloc_pcidisc_find_cap(config_space_cache, PCI_CAP_ID_EXP); if (offset > 0 && offset + 20 /* size of PCI express block up to link status */ <= CONFIG_SPACE_CACHESIZE) hwloc_pcidisc_find_linkspeed(config_space_cache, offset, &obj->attr->pcidev.linkspeed); if (type == HWLOC_OBJ_BRIDGE) { if (hwloc_pcidisc_setup_bridge_attr(obj, config_space_cache) < 0) continue; } if (obj->type == HWLOC_OBJ_PCI_DEVICE) { memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_VENDOR_ID], sizeof(tmp16)); obj->attr->pcidev.subvendor_id = tmp16; memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_ID], sizeof(tmp16)); obj->attr->pcidev.subdevice_id = tmp16; } else { /* TODO: * bridge must lookup PCI_CAP_ID_SSVID and then look at offset+PCI_SSVID_VENDOR/DEVICE_ID * cardbus must look at PCI_CB_SUBSYSTEM_VENDOR_ID and PCI_CB_SUBSYSTEM_ID */ } /* get the vendor name */ vendorname = pci_device_get_vendor_name(pcidev); if (vendorname && *vendorname) hwloc_obj_add_info(obj, "PCIVendor", vendorname); /* get the device name */ devicename = pci_device_get_device_name(pcidev); if (devicename && *devicename) hwloc_obj_add_info(obj, "PCIDevice", devicename); hwloc_debug(" %04x:%02x:%02x.%01x %04x %04x:%04x %s %s\n", domain, pcidev->bus, pcidev->dev, pcidev->func, device_class, pcidev->vendor_id, pcidev->device_id, vendorname && *vendorname ? vendorname : "??", devicename && *devicename ? devicename : "??"); hwloc_pcidisc_tree_insert_by_busid(&tree, obj); } /* finalize device scanning */ pci_iterator_destroy(iter); pci_system_cleanup(); hwloc_pcidisc_tree_attach(topology, tree); return 0; }