static inline PdaDebugReturnCode PciDevice_invoke_libpci ( PciDevice *device, const char *bus_id ) { DEBUG_PRINTF(PDADEBUG_ENTER, ""); if(device == NULL) { RETURN( ERROR( EINVAL, "Invalid pointer!\n") ); } if(bus_id == NULL) { RETURN( ERROR( EINVAL, "Invalid pointer!\n") ); } if ( (strlen(bus_id) != 12) || (bus_id[4] != ':') || (bus_id[7] != ':') || (bus_id[10] != '.') ) { RETURN( ERROR( EINVAL, "Invalid bus ID string!\n") ); } struct pci_access *pci_access_handler = pci_alloc(); pci_init(pci_access_handler); pci_scan_bus(pci_access_handler); for ( struct pci_dev *pci_device = pci_access_handler->devices; pci_device; pci_device = pci_device->next ) { pci_fill_info(pci_device, PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_CLASS | PCI_FILL_CAPS | PCI_FILL_EXT_CAPS | PCI_FILL_PHYS_SLOT ); char device_bus_id[] = "0000:00:00.0"; snprintf(device_bus_id, 13,"%04x:%02x:%02x.%d", pci_device->domain, pci_device->bus, pci_device->dev, pci_device->func); if(strcmp(device_bus_id, bus_id) == 0) { device->domain_id = pci_device->domain; device->bus_id = pci_device->bus; device->device_id = pci_device->dev; device->function_id = pci_device->func; for(uint32_t i = 0; i < PDA_MAX_PCI_32_BARS; i++) { uint32_t bar = pci_device->base_addr[i]; DEBUG_PRINTF(PDADEBUG_VALUE, "bar%d : 0x%x\n", i, bar); uint8_t config[64]; pci_read_block(pci_device, 0, config, 64); uint64_t addr = PCI_BASE_ADDRESS_0 + (4*i); uint32_t virt_flag = (config[addr+0] ) | (config[addr+1] << 8 ) | (config[addr+2] << 16 ) | (config[addr+3] << 24 ) ; if(bar && !virt_flag) { device->bar_types[i] = PCIBARTYPES_NOT_MAPPED; DEBUG_PRINTF(PDADEBUG_CONTROL_FLOW, "bar%u is virtual\n", i); continue; } if(bar == 0x0) { if(device->bar_types[i] != PCIBARTYPES_NOT_MAPPED) { DEBUG_PRINTF(PDADEBUG_CONTROL_FLOW, "Inconsistency detected -> bar%u not classified correctly\n", i); device->bar_types[i] = PCIBARTYPES_NOT_MAPPED; } PciDevice_invoke_libpci_set_barsize(device, i, pci_device->size[i]); continue; } if( (bar & 0x00000001) == 0x1) { if(device->bar_types[i] != PCIBARTYPES_IO) { DEBUG_PRINTF(PDADEBUG_CONTROL_FLOW, "Inconsistency detected -> bar%u not classified correctly\n", i); device->bar_types[i] = PCIBARTYPES_IO; } PciDevice_invoke_libpci_set_barsize(device, i, pci_device->size[i]); continue; } uint32_t width_bit = bar & 0x00000006; if(width_bit == 0x00000000) { if(device->bar_types[i] != PCIBARTYPES_BAR32) { DEBUG_PRINTF(PDADEBUG_CONTROL_FLOW, "Inconsistency detected -> bar%u not classified correctly\n", i); device->bar_types[i] = PCIBARTYPES_BAR32; } PciDevice_invoke_libpci_set_barsize(device, i, pci_device->size[i]); continue; } if(width_bit == 0x00000004) { if(device->bar_types[i] != PCIBARTYPES_BAR64) { DEBUG_PRINTF(PDADEBUG_CONTROL_FLOW, "Inconsistency detected -> bar%u not classified correctly\n", i); device->bar_types[i] = PCIBARTYPES_BAR64; device->bar_types[i + 1] = PCIBARTYPES_NOT_MAPPED; } PciDevice_invoke_libpci_set_barsize(device, i, pci_device->size[i]); continue; } } /** Older versions of libpci leak memory when this function is called. **/ #if PCI_LIB_VERSION >= 0x030301 pci_lookup_name(pci_access_handler, device->description, PDA_STRING_LIMIT, PCI_LOOKUP_DEVICE, pci_device->vendor_id, pci_device->device_id); #else #warning "At least version 3.3.1 of libpci is needed. Otherwise the device description can't be returned." strcpy(device->description, ""); #endif DEBUG_PRINTF(PDADEBUG_VALUE, " (%s) %s\n", device->description, device_bus_id); } } pci_cleanup(pci_access_handler); RETURN(PDA_SUCCESS); }
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); }
void pci::probe(void) { pci_scan_bus(_pacc); uint8_t buf[CONFIG_SPACE_SIZE] = {0}; char classbuf[128] = {0}, vendorbuf[128] {0}, devbuf[128] = {0}; for (struct pci_dev *dev = _pacc->devices; dev && _entries.size() < MAX_DEVICES; dev = dev->next) { _entries.push_back(pciEntry()); pciEntry &e = _entries.back(); memset(buf, 0, sizeof(buf)); memset(classbuf, 0, sizeof(classbuf)); memset(vendorbuf, 0, sizeof(vendorbuf)); memset(devbuf, 0, sizeof(devbuf)); pci_setup_cache(dev, buf, CONFIG_SPACE_SIZE); pci_read_block(dev, 0, buf, CONFIG_SPACE_SIZE); pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_CAPS); pci_lookup_name(_pacc, vendorbuf, sizeof(vendorbuf), PCI_LOOKUP_VENDOR, dev->vendor_id, dev->device_id); pci_lookup_name(_pacc, devbuf, sizeof(devbuf), PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); e.text.append(vendorbuf).append("|").append(devbuf); e.class_type += classbuf; e.vendor = dev->vendor_id; e.device = dev->device_id; e.pci_domain = dev->domain; e.bus = dev->bus; e.pciusb_device = dev->dev; e.pci_function = dev->func; e.class_id = dev->device_class; e.subvendor = pci_read_word(dev, PCI_SUBSYSTEM_VENDOR_ID); e.subdevice = pci_read_word(dev, PCI_SUBSYSTEM_ID); e.pci_revision = pci_read_byte(dev, PCI_REVISION_ID); if ((e.subvendor == 0 && e.subdevice == 0) || (e.subvendor == e.vendor && e.subdevice == e.device)) { e.subvendor = 0xffff; e.subdevice = 0xffff; } if (pci_find_cap(dev,PCI_CAP_ID_EXP, PCI_CAP_NORMAL)) e.is_pciexpress = true; /* special case for realtek 8139 that has two drivers */ if (e.vendor == 0x10ec && e.device == 0x8139) { if (e.pci_revision < 0x20) e.module = "8139too"; else e.module = "8139cp"; } } // fake two PCI controllers for xen struct stat sb; if (!stat("/sys/bus/xen", &sb)) { // FIXME: use C++ streams.. FILE *f; if ((f = fopen("/sys/hypervisor/uuid", "r"))) { char buf[38]; fgets(buf, sizeof(buf) - 1, f); fclose(f); if (strncmp(buf, "00000000-0000-0000-0000-000000000000", sizeof(buf))) { // We're now sure to be in a Xen guest: { _entries.push_back(pciEntry()); pciEntry &e = _entries.back(); e.text.append("XenSource, Inc.|Block Frontend"); e.class_id = 0x0106; // STORAGE_SATA e.vendor = 0x1a71; // XenSource e.device = 0xfffa; // fake e.subvendor = 0; e.subdevice = 0; e.class_id = 0x0106; e.module = "xen_blkfront"; } { _entries.push_back(pciEntry()); pciEntry &e = _entries.back(); e.text.append("XenSource, Inc.|Network Frontend"); e.class_id = 0x0200; // NETWORK_ETHERNET e.vendor = 0x1a71; // XenSource e.device = 0xfffb; // fake e.subvendor = 0; e.subdevice = 0; e.class_id = 0x0200; e.module = "xen_netfront"; } } } } findModules("pcitable", false); }