111f7b31bSMartin Mares /*
211f7b31bSMartin Mares * The PCI Library -- Configuration Access via /sys/bus/pci
311f7b31bSMartin Mares *
4535fca0eSMartin Mares * Copyright (c) 2003 Matthew Wilcox <[email protected]>
589cb2ae8SMartin Mares * Copyright (c) 1997--2024 Martin Mares <[email protected]>
611f7b31bSMartin Mares *
761829219SMartin Mares * Can be freely distributed and used under the terms of the GNU GPL v2+.
861829219SMartin Mares *
961829219SMartin Mares * SPDX-License-Identifier: GPL-2.0-or-later
1011f7b31bSMartin Mares */
1111f7b31bSMartin Mares
1211f7b31bSMartin Mares #define _GNU_SOURCE
1311f7b31bSMartin Mares
1411f7b31bSMartin Mares #include <stdio.h>
1511f7b31bSMartin Mares #include <stdlib.h>
1611f7b31bSMartin Mares #include <string.h>
1711f7b31bSMartin Mares #include <stdarg.h>
1811f7b31bSMartin Mares #include <unistd.h>
1911f7b31bSMartin Mares #include <errno.h>
2011f7b31bSMartin Mares #include <dirent.h>
2111f7b31bSMartin Mares #include <fcntl.h>
2289cb2ae8SMartin Mares #include <libgen.h>
2311f7b31bSMartin Mares #include <sys/types.h>
2411f7b31bSMartin Mares
2511f7b31bSMartin Mares #include "internal.h"
2611f7b31bSMartin Mares
2711f7b31bSMartin Mares static void
sysfs_config(struct pci_access * a)2811f7b31bSMartin Mares sysfs_config(struct pci_access *a)
2911f7b31bSMartin Mares {
30cb6ee324SMartin Mares pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree");
3111f7b31bSMartin Mares }
3211f7b31bSMartin Mares
3311f7b31bSMartin Mares static inline char *
sysfs_name(struct pci_access * a)3411f7b31bSMartin Mares sysfs_name(struct pci_access *a)
3511f7b31bSMartin Mares {
36cb6ee324SMartin Mares return pci_get_param(a, "sysfs.path");
3711f7b31bSMartin Mares }
3811f7b31bSMartin Mares
3911f7b31bSMartin Mares static int
sysfs_detect(struct pci_access * a)4011f7b31bSMartin Mares sysfs_detect(struct pci_access *a)
4111f7b31bSMartin Mares {
4211f7b31bSMartin Mares if (access(sysfs_name(a), R_OK))
4311f7b31bSMartin Mares {
4484c8d1bbSMartin Mares a->debug("...cannot open %s", sysfs_name(a));
4511f7b31bSMartin Mares return 0;
4611f7b31bSMartin Mares }
4711f7b31bSMartin Mares a->debug("...using %s", sysfs_name(a));
4811f7b31bSMartin Mares return 1;
4911f7b31bSMartin Mares }
5011f7b31bSMartin Mares
5111f7b31bSMartin Mares static void
sysfs_init(struct pci_access * a)5211f7b31bSMartin Mares sysfs_init(struct pci_access *a)
5311f7b31bSMartin Mares {
5411f7b31bSMartin Mares a->fd = -1;
5507ad085dSMartin Mares a->fd_vpd = -1;
5611f7b31bSMartin Mares }
5711f7b31bSMartin Mares
5811f7b31bSMartin Mares static void
sysfs_flush_cache(struct pci_access * a)5907ad085dSMartin Mares sysfs_flush_cache(struct pci_access *a)
6011f7b31bSMartin Mares {
6111f7b31bSMartin Mares if (a->fd >= 0)
6211f7b31bSMartin Mares {
6311f7b31bSMartin Mares close(a->fd);
6411f7b31bSMartin Mares a->fd = -1;
6511f7b31bSMartin Mares }
6607ad085dSMartin Mares if (a->fd_vpd >= 0)
6707ad085dSMartin Mares {
6807ad085dSMartin Mares close(a->fd_vpd);
6907ad085dSMartin Mares a->fd_vpd = -1;
7007ad085dSMartin Mares }
7107ad085dSMartin Mares a->cached_dev = NULL;
7207ad085dSMartin Mares }
7307ad085dSMartin Mares
7407ad085dSMartin Mares static void
sysfs_cleanup(struct pci_access * a)7507ad085dSMartin Mares sysfs_cleanup(struct pci_access *a)
7607ad085dSMartin Mares {
7707ad085dSMartin Mares sysfs_flush_cache(a);
7811f7b31bSMartin Mares }
7911f7b31bSMartin Mares
8011f7b31bSMartin Mares #define OBJNAMELEN 1024
8111f7b31bSMartin Mares static void
sysfs_obj_name(struct pci_dev * d,char * object,char * buf)8211f7b31bSMartin Mares sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
8311f7b31bSMartin Mares {
8411f7b31bSMartin Mares int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
8511f7b31bSMartin Mares sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
8611f7b31bSMartin Mares if (n < 0 || n >= OBJNAMELEN)
8711f7b31bSMartin Mares d->access->error("File name too long");
8811f7b31bSMartin Mares }
8911f7b31bSMartin Mares
90b1861a8dSMartin Mares #define OBJBUFSIZE 1024
91b1861a8dSMartin Mares
9211f7b31bSMartin Mares static int
sysfs_get_string(struct pci_dev * d,char * object,char * buf,int mandatory)93b1861a8dSMartin Mares sysfs_get_string(struct pci_dev *d, char *object, char *buf, int mandatory)
9411f7b31bSMartin Mares {
9511f7b31bSMartin Mares struct pci_access *a = d->access;
9611f7b31bSMartin Mares int fd, n;
97b1861a8dSMartin Mares char namebuf[OBJNAMELEN];
98e5bb28afSMartin Mares void (*warn)(char *msg, ...) = (mandatory ? a->error : a->warning);
9911f7b31bSMartin Mares
10011f7b31bSMartin Mares sysfs_obj_name(d, object, namebuf);
10111f7b31bSMartin Mares fd = open(namebuf, O_RDONLY);
10211f7b31bSMartin Mares if (fd < 0)
103b1861a8dSMartin Mares {
104e5bb28afSMartin Mares if (mandatory || errno != ENOENT)
105e5bb28afSMartin Mares warn("Cannot open %s: %s", namebuf, strerror(errno));
106b1861a8dSMartin Mares return 0;
107b1861a8dSMartin Mares }
108b1861a8dSMartin Mares n = read(fd, buf, OBJBUFSIZE);
1099b31027fSMartin Mares int read_errno = errno;
11011f7b31bSMartin Mares close(fd);
11111f7b31bSMartin Mares if (n < 0)
112e5bb28afSMartin Mares {
1139b31027fSMartin Mares warn("Error reading %s: %s", namebuf, strerror(read_errno));
114e5bb28afSMartin Mares return 0;
115e5bb28afSMartin Mares }
116b1861a8dSMartin Mares if (n >= OBJBUFSIZE)
117e5bb28afSMartin Mares {
118e5bb28afSMartin Mares warn("Value in %s too long", namebuf);
119e5bb28afSMartin Mares return 0;
120e5bb28afSMartin Mares }
12111f7b31bSMartin Mares buf[n] = 0;
122b1861a8dSMartin Mares return 1;
123b1861a8dSMartin Mares }
124b1861a8dSMartin Mares
1256d701ce3SOliver O'Halloran static char *
sysfs_deref_link(struct pci_dev * d,char * link_name)1266d701ce3SOliver O'Halloran sysfs_deref_link(struct pci_dev *d, char *link_name)
1276d701ce3SOliver O'Halloran {
1286d701ce3SOliver O'Halloran char path[2*OBJNAMELEN], rel_path[OBJNAMELEN];
1296d701ce3SOliver O'Halloran
1306d701ce3SOliver O'Halloran sysfs_obj_name(d, link_name, path);
1316d701ce3SOliver O'Halloran memset(rel_path, 0, sizeof(rel_path));
1326d701ce3SOliver O'Halloran
1336d701ce3SOliver O'Halloran if (readlink(path, rel_path, sizeof(rel_path)) < 0)
1346d701ce3SOliver O'Halloran return NULL;
1356d701ce3SOliver O'Halloran
1366d701ce3SOliver O'Halloran sysfs_obj_name(d, "", path);
1376d701ce3SOliver O'Halloran strcat(path, rel_path);
1386d701ce3SOliver O'Halloran
139f15db375SMartin Mares // Returns a pointer to malloc'ed memory
140f15db375SMartin Mares return realpath(path, NULL);
1416d701ce3SOliver O'Halloran }
1426d701ce3SOliver O'Halloran
143b1861a8dSMartin Mares static int
sysfs_get_value(struct pci_dev * d,char * object,int mandatory)1449c876849SMartin Mares sysfs_get_value(struct pci_dev *d, char *object, int mandatory)
145b1861a8dSMartin Mares {
146b1861a8dSMartin Mares char buf[OBJBUFSIZE];
147b1861a8dSMartin Mares
1489c876849SMartin Mares if (sysfs_get_string(d, object, buf, mandatory))
14911f7b31bSMartin Mares return strtol(buf, NULL, 0);
1509c876849SMartin Mares else
1519c876849SMartin Mares return -1;
15211f7b31bSMartin Mares }
15311f7b31bSMartin Mares
15411f7b31bSMartin Mares static void
sysfs_get_resources(struct pci_dev * d)15511f7b31bSMartin Mares sysfs_get_resources(struct pci_dev *d)
15611f7b31bSMartin Mares {
15711f7b31bSMartin Mares struct pci_access *a = d->access;
15811f7b31bSMartin Mares char namebuf[OBJNAMELEN], buf[256];
159119c1376SPali Rohár struct { pciaddr_t flags, base_addr, size; } lines[10];
160119c1376SPali Rohár int have_bar_bases, have_rom_base, have_bridge_bases;
16111f7b31bSMartin Mares FILE *file;
16211f7b31bSMartin Mares int i;
16311f7b31bSMartin Mares
164119c1376SPali Rohár have_bar_bases = have_rom_base = have_bridge_bases = 0;
16511f7b31bSMartin Mares sysfs_obj_name(d, "resource", namebuf);
16611f7b31bSMartin Mares file = fopen(namebuf, "r");
16711f7b31bSMartin Mares if (!file)
16811f7b31bSMartin Mares a->error("Cannot open %s: %s", namebuf, strerror(errno));
169119c1376SPali Rohár for (i = 0; i < 7+6+4+1; i++)
17011f7b31bSMartin Mares {
1716d143c32SYu Zhao unsigned long long start, end, size, flags;
17211f7b31bSMartin Mares if (!fgets(buf, sizeof(buf), file))
17311f7b31bSMartin Mares break;
1746d143c32SYu Zhao if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3)
17511f7b31bSMartin Mares a->error("Syntax error in %s", namebuf);
17600bf6625SAaron Sierra if (end > start)
17711f7b31bSMartin Mares size = end - start + 1;
17811f7b31bSMartin Mares else
17911f7b31bSMartin Mares size = 0;
1809bb4b4eaSMartin Mares if (i < 6)
18111f7b31bSMartin Mares {
182558f736bSSean O. Stalley d->flags[i] = flags;
183558f736bSSean O. Stalley flags &= PCI_ADDR_FLAG_MASK;
1846d143c32SYu Zhao d->base_addr[i] = start | flags;
18511f7b31bSMartin Mares d->size[i] = size;
186119c1376SPali Rohár have_bar_bases = 1;
18711f7b31bSMartin Mares }
188119c1376SPali Rohár else if (i == 6)
18911f7b31bSMartin Mares {
190558f736bSSean O. Stalley d->rom_flags = flags;
191558f736bSSean O. Stalley flags &= PCI_ADDR_FLAG_MASK;
1926d143c32SYu Zhao d->rom_base_addr = start | flags;
19311f7b31bSMartin Mares d->rom_size = size;
194119c1376SPali Rohár have_rom_base = 1;
19511f7b31bSMartin Mares }
196119c1376SPali Rohár else if (i < 7+6+4)
197119c1376SPali Rohár {
198119c1376SPali Rohár /*
199119c1376SPali Rohár * If kernel was compiled without CONFIG_PCI_IOV option then after
200119c1376SPali Rohár * the ROM line for configured bridge device (that which had set
201119c1376SPali Rohár * subordinary bus number to non-zero value) are four additional lines
202119c1376SPali Rohár * which describe resources behind bridge. For PCI-to-PCI bridges they
203119c1376SPali Rohár * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0,
204119c1376SPali Rohár * IO1, MEM0 and MEM1. For unconfigured bridges and other devices
205119c1376SPali Rohár * there is no additional line after the ROM line. If kernel was
206119c1376SPali Rohár * compiled with CONFIG_PCI_IOV option then after the ROM line and
207119c1376SPali Rohár * before the first bridge resource line are six additional lines
208119c1376SPali Rohár * which describe IOV resources. Read all remaining lines in resource
209119c1376SPali Rohár * file and based on the number of remaining lines (0, 4, 6, 10) parse
210119c1376SPali Rohár * resources behind bridge.
211119c1376SPali Rohár */
212119c1376SPali Rohár lines[i-7].flags = flags;
213119c1376SPali Rohár lines[i-7].base_addr = start;
214119c1376SPali Rohár lines[i-7].size = size;
215119c1376SPali Rohár }
216119c1376SPali Rohár }
217119c1376SPali Rohár if (i == 7+4 || i == 7+6+4)
218119c1376SPali Rohár {
219119c1376SPali Rohár int offset = (i == 7+6+4) ? 6 : 0;
220119c1376SPali Rohár for (i = 0; i < 4; i++)
221119c1376SPali Rohár {
222119c1376SPali Rohár d->bridge_flags[i] = lines[offset+i].flags;
223119c1376SPali Rohár d->bridge_base_addr[i] = lines[offset+i].base_addr;
224119c1376SPali Rohár d->bridge_size[i] = lines[offset+i].size;
225119c1376SPali Rohár }
226119c1376SPali Rohár have_bridge_bases = 1;
22711f7b31bSMartin Mares }
22811f7b31bSMartin Mares fclose(file);
229119c1376SPali Rohár if (!have_bar_bases)
230119c1376SPali Rohár clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS);
231119c1376SPali Rohár if (!have_rom_base)
232119c1376SPali Rohár clear_fill(d, PCI_FILL_ROM_BASE);
233119c1376SPali Rohár if (!have_bridge_bases)
234119c1376SPali Rohár clear_fill(d, PCI_FILL_BRIDGE_BASES);
23511f7b31bSMartin Mares }
23611f7b31bSMartin Mares
sysfs_scan(struct pci_access * a)23711f7b31bSMartin Mares static void sysfs_scan(struct pci_access *a)
23811f7b31bSMartin Mares {
239e84876c6SMartin Mares char dirname[1024];
24011f7b31bSMartin Mares DIR *dir;
24111f7b31bSMartin Mares struct dirent *entry;
24211f7b31bSMartin Mares int n;
24311f7b31bSMartin Mares
24411f7b31bSMartin Mares n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
24511f7b31bSMartin Mares if (n < 0 || n >= (int) sizeof(dirname))
24611f7b31bSMartin Mares a->error("Directory name too long");
24711f7b31bSMartin Mares dir = opendir(dirname);
24811f7b31bSMartin Mares if (!dir)
24911f7b31bSMartin Mares a->error("Cannot open %s", dirname);
25011f7b31bSMartin Mares while ((entry = readdir(dir)))
25111f7b31bSMartin Mares {
25211f7b31bSMartin Mares struct pci_dev *d;
25311f7b31bSMartin Mares unsigned int dom, bus, dev, func;
25411f7b31bSMartin Mares
25511f7b31bSMartin Mares /* ".", ".." or a special non-device perhaps */
25611f7b31bSMartin Mares if (entry->d_name[0] == '.')
25711f7b31bSMartin Mares continue;
25811f7b31bSMartin Mares
25911f7b31bSMartin Mares d = pci_alloc_dev(a);
26011f7b31bSMartin Mares if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
26111f7b31bSMartin Mares a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
262ab61451dSKeith Busch
263ab61451dSKeith Busch /* Ensure kernel provided domain that fits in a signed integer */
264ab61451dSKeith Busch if (dom > 0x7fffffff)
2654186391bSMartin Mares a->error("sysfs_scan: Invalid domain %x", dom);
266ab61451dSKeith Busch
26711f7b31bSMartin Mares d->domain = dom;
26811f7b31bSMartin Mares d->bus = bus;
26911f7b31bSMartin Mares d->dev = dev;
27011f7b31bSMartin Mares d->func = func;
27111f7b31bSMartin Mares pci_link_dev(a, d);
27211f7b31bSMartin Mares }
27311f7b31bSMartin Mares closedir(dir);
27411f7b31bSMartin Mares }
27511f7b31bSMartin Mares
2762849a165SAlex Chiang static void
sysfs_fill_slots(struct pci_access * a)2776171c06dSMartin Mares sysfs_fill_slots(struct pci_access *a)
2782849a165SAlex Chiang {
2792849a165SAlex Chiang char dirname[1024];
2802849a165SAlex Chiang DIR *dir;
2812849a165SAlex Chiang struct dirent *entry;
2822849a165SAlex Chiang int n;
2832849a165SAlex Chiang
2842849a165SAlex Chiang n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a));
2852849a165SAlex Chiang if (n < 0 || n >= (int) sizeof(dirname))
2862849a165SAlex Chiang a->error("Directory name too long");
2872849a165SAlex Chiang dir = opendir(dirname);
2882849a165SAlex Chiang if (!dir)
2896171c06dSMartin Mares return;
2906171c06dSMartin Mares
2916171c06dSMartin Mares while (entry = readdir(dir))
2922849a165SAlex Chiang {
2932849a165SAlex Chiang char namebuf[OBJNAMELEN], buf[16];
2942849a165SAlex Chiang FILE *file;
2952849a165SAlex Chiang unsigned int dom, bus, dev;
2964134538aSMartin Mares int res = 0;
2976171c06dSMartin Mares struct pci_dev *d;
2982849a165SAlex Chiang
2992849a165SAlex Chiang /* ".", ".." or a special non-device perhaps */
3002849a165SAlex Chiang if (entry->d_name[0] == '.')
3012849a165SAlex Chiang continue;
3022849a165SAlex Chiang
3036171c06dSMartin Mares n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address");
3042849a165SAlex Chiang if (n < 0 || n >= OBJNAMELEN)
3056171c06dSMartin Mares a->error("File name too long");
3062849a165SAlex Chiang file = fopen(namebuf, "r");
307534fbba9SMatthew Wilcox /*
308534fbba9SMatthew Wilcox * Old versions of Linux had a fakephp which didn't have an 'address'
309534fbba9SMatthew Wilcox * file. There's no useful information to be gleaned from these
310534fbba9SMatthew Wilcox * devices, pretend they're not there.
311534fbba9SMatthew Wilcox */
3122849a165SAlex Chiang if (!file)
3136171c06dSMartin Mares continue;
3146171c06dSMartin Mares
3154134538aSMartin Mares if (!fgets(buf, sizeof(buf), file) || (res = sscanf(buf, "%x:%x:%x", &dom, &bus, &dev)) < 3)
3164134538aSMartin Mares {
3174134538aSMartin Mares /*
3184134538aSMartin Mares * In some cases, the slot is not tied to a specific device before
3194134538aSMartin Mares * a card gets inserted. This happens for example on IBM pSeries
3204134538aSMartin Mares * and we need not warn about it.
3214134538aSMartin Mares */
3224134538aSMartin Mares if (res != 2)
3236171c06dSMartin Mares a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf);
3244134538aSMartin Mares }
3256171c06dSMartin Mares else
3266171c06dSMartin Mares {
3276171c06dSMartin Mares for (d = a->devices; d; d = d->next)
328ab61451dSKeith Busch if (dom == (unsigned)d->domain && bus == d->bus && dev == d->dev && !d->phy_slot)
32962bd0c5cSMartin Mares d->phy_slot = pci_set_property(d, PCI_FILL_PHYS_SLOT, entry->d_name);
3302849a165SAlex Chiang }
3312849a165SAlex Chiang fclose(file);
3322849a165SAlex Chiang }
3332849a165SAlex Chiang closedir(dir);
3342849a165SAlex Chiang }
3352849a165SAlex Chiang
3368e9299e4SMartin Mares static void
sysfs_fill_info(struct pci_dev * d,unsigned int flags)33782c06b47SMartin Mares sysfs_fill_info(struct pci_dev *d, unsigned int flags)
3382849a165SAlex Chiang {
339a936caf6SPali Rohár int value, want_class, want_class_ext;
340a936caf6SPali Rohár
34182c06b47SMartin Mares if (!d->access->buscentric)
34282c06b47SMartin Mares {
34382c06b47SMartin Mares /*
34482c06b47SMartin Mares * These fields can be read from the config registers, but we want to show
34582c06b47SMartin Mares * the kernel's view, which has regions and IRQs remapped and other fields
34682c06b47SMartin Mares * (most importantly classes) possibly fixed if the device is known broken.
34782c06b47SMartin Mares */
3488e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_IDENT))
34982c06b47SMartin Mares {
35082c06b47SMartin Mares d->vendor_id = sysfs_get_value(d, "vendor", 1);
35182c06b47SMartin Mares d->device_id = sysfs_get_value(d, "device", 1);
35282c06b47SMartin Mares }
353a936caf6SPali Rohár want_class = want_fill(d, flags, PCI_FILL_CLASS);
354a936caf6SPali Rohár want_class_ext = want_fill(d, flags, PCI_FILL_CLASS_EXT);
355a936caf6SPali Rohár if (want_class || want_class_ext)
356a936caf6SPali Rohár {
357a936caf6SPali Rohár value = sysfs_get_value(d, "class", 1);
358a936caf6SPali Rohár if (want_class)
359a936caf6SPali Rohár d->device_class = value >> 8;
360a936caf6SPali Rohár if (want_class_ext)
361a936caf6SPali Rohár {
362a936caf6SPali Rohár d->prog_if = value & 0xff;
363a936caf6SPali Rohár value = sysfs_get_value(d, "revision", 0);
364a936caf6SPali Rohár if (value < 0)
365a936caf6SPali Rohár value = pci_read_byte(d, PCI_REVISION_ID);
366a936caf6SPali Rohár if (value >= 0)
367a936caf6SPali Rohár d->rev_id = value;
368a936caf6SPali Rohár }
369a936caf6SPali Rohár }
370a936caf6SPali Rohár if (want_fill(d, flags, PCI_FILL_SUBSYS))
371a936caf6SPali Rohár {
372a936caf6SPali Rohár value = sysfs_get_value(d, "subsystem_vendor", 0);
373a936caf6SPali Rohár if (value >= 0)
374a936caf6SPali Rohár {
375a936caf6SPali Rohár d->subsys_vendor_id = value;
376a936caf6SPali Rohár value = sysfs_get_value(d, "subsystem_device", 0);
377a936caf6SPali Rohár if (value >= 0)
378a936caf6SPali Rohár d->subsys_id = value;
379a936caf6SPali Rohár }
380a936caf6SPali Rohár else
381a936caf6SPali Rohár clear_fill(d, PCI_FILL_SUBSYS);
382a936caf6SPali Rohár }
3838e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_IRQ))
38482c06b47SMartin Mares d->irq = sysfs_get_value(d, "irq", 1);
385119c1376SPali Rohár if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
38682c06b47SMartin Mares sysfs_get_resources(d);
387e9e7fab1SPali Rohár if (want_fill(d, flags, PCI_FILL_PARENT))
388e9e7fab1SPali Rohár {
389e9e7fab1SPali Rohár unsigned int domain, bus, dev, func;
390e9e7fab1SPali Rohár char *path_abs, *path_canon, *name;
391e9e7fab1SPali Rohár char path_rel[OBJNAMELEN];
392e9e7fab1SPali Rohár struct pci_dev *parent;
393e9e7fab1SPali Rohár
394e9e7fab1SPali Rohár /* Construct sysfs path for parent device */
395e9e7fab1SPali Rohár sysfs_obj_name(d, "..", path_rel);
396e9e7fab1SPali Rohár path_abs = realpath(path_rel, NULL);
397e9e7fab1SPali Rohár name = path_abs ? strrchr(path_abs, '/') : NULL;
398e9e7fab1SPali Rohár name = name ? name+1 : name;
399e9e7fab1SPali Rohár parent = NULL;
400e9e7fab1SPali Rohár
401e9e7fab1SPali Rohár if (name && sscanf(name, "%x:%x:%x.%d", &domain, &bus, &dev, &func) == 4 && domain <= 0x7fffffff)
402e9e7fab1SPali Rohár for (parent = d->access->devices; parent; parent = parent->next)
403e9e7fab1SPali Rohár if (parent->domain == (int)domain && parent->bus == bus && parent->dev == dev && parent->func == func)
404e9e7fab1SPali Rohár break;
405e9e7fab1SPali Rohár
406e9e7fab1SPali Rohár if (parent)
407e9e7fab1SPali Rohár {
408e9e7fab1SPali Rohár /* Check if parsed BDF address from parent sysfs device is really expected PCI device */
409e9e7fab1SPali Rohár sysfs_obj_name(parent, ".", path_rel);
410e9e7fab1SPali Rohár path_canon = realpath(path_rel, NULL);
411e9e7fab1SPali Rohár if (!path_canon || strcmp(path_canon, path_abs) != 0)
412e9e7fab1SPali Rohár parent = NULL;
413869a9f66Snsf.cd
414869a9f66Snsf.cd if (path_canon)
415869a9f66Snsf.cd free(path_canon);
416e9e7fab1SPali Rohár }
417e9e7fab1SPali Rohár
418e9e7fab1SPali Rohár if (parent)
419e9e7fab1SPali Rohár d->parent = parent;
420e9e7fab1SPali Rohár else
421e9e7fab1SPali Rohár clear_fill(d, PCI_FILL_PARENT);
422869a9f66Snsf.cd
423869a9f66Snsf.cd if (path_abs)
424869a9f66Snsf.cd free(path_abs);
425e9e7fab1SPali Rohár }
42682c06b47SMartin Mares }
42782c06b47SMartin Mares
4288e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_PHYS_SLOT))
4296171c06dSMartin Mares {
4306171c06dSMartin Mares struct pci_dev *pd;
4316171c06dSMartin Mares sysfs_fill_slots(d->access);
4326171c06dSMartin Mares for (pd = d->access->devices; pd; pd = pd->next)
4336171c06dSMartin Mares pd->known_fields |= PCI_FILL_PHYS_SLOT;
4346171c06dSMartin Mares }
435b1861a8dSMartin Mares
4368e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_MODULE_ALIAS))
437b1861a8dSMartin Mares {
438b1861a8dSMartin Mares char buf[OBJBUFSIZE];
439b1861a8dSMartin Mares if (sysfs_get_string(d, "modalias", buf, 0))
44062bd0c5cSMartin Mares d->module_alias = pci_set_property(d, PCI_FILL_MODULE_ALIAS, buf);
441b1861a8dSMartin Mares }
442b1861a8dSMartin Mares
4438e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_LABEL))
444e84876c6SMartin Mares {
445e84876c6SMartin Mares char buf[OBJBUFSIZE];
446e84876c6SMartin Mares if (sysfs_get_string(d, "label", buf, 0))
44762bd0c5cSMartin Mares d->label = pci_set_property(d, PCI_FILL_LABEL, buf);
448e84876c6SMartin Mares }
449e84876c6SMartin Mares
4508e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_NUMA_NODE))
4519c876849SMartin Mares d->numa_node = sysfs_get_value(d, "numa_node", 0);
4529c876849SMartin Mares
4538e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_IOMMU_GROUP))
454a5065438SAlex Xu (Hello71) {
455a5065438SAlex Xu (Hello71) char *group_link = sysfs_deref_link(d, "iommu_group");
456a5065438SAlex Xu (Hello71) if (group_link)
457a5065438SAlex Xu (Hello71) {
458a5065438SAlex Xu (Hello71) pci_set_property(d, PCI_FILL_IOMMU_GROUP, basename(group_link));
459a5065438SAlex Xu (Hello71) free(group_link);
460a5065438SAlex Xu (Hello71) }
461a5065438SAlex Xu (Hello71) }
462a5065438SAlex Xu (Hello71)
4638e9299e4SMartin Mares if (want_fill(d, flags, PCI_FILL_DT_NODE))
464f15db375SMartin Mares {
465f15db375SMartin Mares char *node = sysfs_deref_link(d, "of_node");
466f15db375SMartin Mares if (node)
467f15db375SMartin Mares {
468f15db375SMartin Mares pci_set_property(d, PCI_FILL_DT_NODE, node);
469f15db375SMartin Mares free(node);
470f15db375SMartin Mares }
471f15db375SMartin Mares }
4726d701ce3SOliver O'Halloran
4739b744f76SPali Rohár if (want_fill(d, flags, PCI_FILL_DRIVER))
4749b744f76SPali Rohár {
4759b744f76SPali Rohár char *driver_path = sysfs_deref_link(d, "driver");
4769b744f76SPali Rohár if (driver_path)
4779b744f76SPali Rohár {
4789b744f76SPali Rohár char *driver = strrchr(driver_path, '/');
4799b744f76SPali Rohár driver = driver ? driver+1 : driver_path;
4809b744f76SPali Rohár pci_set_property(d, PCI_FILL_DRIVER, driver);
4819b744f76SPali Rohár free(driver_path);
4829b744f76SPali Rohár }
4839b744f76SPali Rohár else
4849b744f76SPali Rohár clear_fill(d, PCI_FILL_DRIVER);
4859b744f76SPali Rohár }
4869b744f76SPali Rohár
487*49efa87fSKobayashi Daisuke if (want_fill(d, flags, PCI_FILL_RCD_LNK))
488*49efa87fSKobayashi Daisuke {
489*49efa87fSKobayashi Daisuke char buf[OBJBUFSIZE];
490*49efa87fSKobayashi Daisuke if (sysfs_get_string(d, "rcd_link_cap", buf, 0))
491*49efa87fSKobayashi Daisuke d->rcd_link_cap = strtoul(buf, NULL, 16);
492*49efa87fSKobayashi Daisuke if (sysfs_get_string(d, "rcd_link_ctrl", buf, 0))
493*49efa87fSKobayashi Daisuke d->rcd_link_ctrl = strtoul(buf, NULL, 16);
494*49efa87fSKobayashi Daisuke if (sysfs_get_string(d, "rcd_link_status", buf, 0))
495*49efa87fSKobayashi Daisuke d->rcd_link_status = strtoul(buf, NULL, 16);
496*49efa87fSKobayashi Daisuke }
497*49efa87fSKobayashi Daisuke
4988e9299e4SMartin Mares pci_generic_fill_info(d, flags);
4992849a165SAlex Chiang }
5002849a165SAlex Chiang
5019bd5b1cfSBen Hutchings /* Intent of the sysfs_setup() caller */
5029bd5b1cfSBen Hutchings enum
5039bd5b1cfSBen Hutchings {
5049bd5b1cfSBen Hutchings SETUP_READ_CONFIG = 0,
5059bd5b1cfSBen Hutchings SETUP_WRITE_CONFIG = 1,
5069bd5b1cfSBen Hutchings SETUP_READ_VPD = 2
5079bd5b1cfSBen Hutchings };
5089bd5b1cfSBen Hutchings
50911f7b31bSMartin Mares static int
sysfs_setup(struct pci_dev * d,int intent)5109bd5b1cfSBen Hutchings sysfs_setup(struct pci_dev *d, int intent)
51111f7b31bSMartin Mares {
51211f7b31bSMartin Mares struct pci_access *a = d->access;
51311f7b31bSMartin Mares char namebuf[OBJNAMELEN];
5149bd5b1cfSBen Hutchings
51507ad085dSMartin Mares if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
5169bd5b1cfSBen Hutchings {
51707ad085dSMartin Mares sysfs_flush_cache(a);
51807ad085dSMartin Mares a->cached_dev = d;
51907ad085dSMartin Mares }
52007ad085dSMartin Mares
52107ad085dSMartin Mares if (intent == SETUP_READ_VPD)
52207ad085dSMartin Mares {
52307ad085dSMartin Mares if (a->fd_vpd < 0)
52407ad085dSMartin Mares {
52507ad085dSMartin Mares sysfs_obj_name(d, "vpd", namebuf);
52607ad085dSMartin Mares a->fd_vpd = open(namebuf, O_RDONLY);
52707ad085dSMartin Mares /* No warning on error; vpd may be absent or accessible only to root */
52807ad085dSMartin Mares }
52907ad085dSMartin Mares return a->fd_vpd;
53007ad085dSMartin Mares }
53107ad085dSMartin Mares
532c886948cSMartin Mares if (a->fd < 0)
533c886948cSMartin Mares {
53411f7b31bSMartin Mares sysfs_obj_name(d, "config", namebuf);
5359bd5b1cfSBen Hutchings a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
53611f7b31bSMartin Mares a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
53711f7b31bSMartin Mares if (a->fd < 0)
53811f7b31bSMartin Mares a->warning("Cannot open %s", namebuf);
539c886948cSMartin Mares }
54007ad085dSMartin Mares return a->fd;
54111f7b31bSMartin Mares }
54211f7b31bSMartin Mares
sysfs_read(struct pci_dev * d,int pos,byte * buf,int len)54311f7b31bSMartin Mares static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
54411f7b31bSMartin Mares {
5459bd5b1cfSBen Hutchings int fd = sysfs_setup(d, SETUP_READ_CONFIG);
54611f7b31bSMartin Mares int res;
54711f7b31bSMartin Mares
54811f7b31bSMartin Mares if (fd < 0)
54911f7b31bSMartin Mares return 0;
55030e9f21eSMartin Mares res = pread(fd, buf, len, pos);
55111f7b31bSMartin Mares if (res < 0)
55211f7b31bSMartin Mares {
55311f7b31bSMartin Mares d->access->warning("sysfs_read: read failed: %s", strerror(errno));
55411f7b31bSMartin Mares return 0;
55511f7b31bSMartin Mares }
55611f7b31bSMartin Mares else if (res != len)
55711f7b31bSMartin Mares return 0;
55811f7b31bSMartin Mares return 1;
55911f7b31bSMartin Mares }
56011f7b31bSMartin Mares
sysfs_write(struct pci_dev * d,int pos,byte * buf,int len)56111f7b31bSMartin Mares static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
56211f7b31bSMartin Mares {
5639bd5b1cfSBen Hutchings int fd = sysfs_setup(d, SETUP_WRITE_CONFIG);
56411f7b31bSMartin Mares int res;
56511f7b31bSMartin Mares
56611f7b31bSMartin Mares if (fd < 0)
56711f7b31bSMartin Mares return 0;
56830e9f21eSMartin Mares res = pwrite(fd, buf, len, pos);
56911f7b31bSMartin Mares if (res < 0)
57011f7b31bSMartin Mares {
57111f7b31bSMartin Mares d->access->warning("sysfs_write: write failed: %s", strerror(errno));
57211f7b31bSMartin Mares return 0;
57311f7b31bSMartin Mares }
57411f7b31bSMartin Mares else if (res != len)
57511f7b31bSMartin Mares {
57609817437SMartin Mares d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
57711f7b31bSMartin Mares return 0;
57811f7b31bSMartin Mares }
57911f7b31bSMartin Mares return 1;
58011f7b31bSMartin Mares }
58111f7b31bSMartin Mares
sysfs_read_vpd(struct pci_dev * d,int pos,byte * buf,int len)5829bd5b1cfSBen Hutchings static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
5839bd5b1cfSBen Hutchings {
5849bd5b1cfSBen Hutchings int fd = sysfs_setup(d, SETUP_READ_VPD);
5859bd5b1cfSBen Hutchings int res;
5869bd5b1cfSBen Hutchings
5879bd5b1cfSBen Hutchings if (fd < 0)
5889bd5b1cfSBen Hutchings return 0;
5899bd5b1cfSBen Hutchings res = pread(fd, buf, len, pos);
5909bd5b1cfSBen Hutchings if (res < 0)
5919bd5b1cfSBen Hutchings {
5929bd5b1cfSBen Hutchings d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno));
5939bd5b1cfSBen Hutchings return 0;
5949bd5b1cfSBen Hutchings }
5959bd5b1cfSBen Hutchings else if (res != len)
5969bd5b1cfSBen Hutchings return 0;
5979bd5b1cfSBen Hutchings return 1;
5989bd5b1cfSBen Hutchings }
5999bd5b1cfSBen Hutchings
sysfs_cleanup_dev(struct pci_dev * d)60011f7b31bSMartin Mares static void sysfs_cleanup_dev(struct pci_dev *d)
60111f7b31bSMartin Mares {
60211f7b31bSMartin Mares struct pci_access *a = d->access;
60311f7b31bSMartin Mares
60411f7b31bSMartin Mares if (a->cached_dev == d)
60507ad085dSMartin Mares sysfs_flush_cache(a);
60611f7b31bSMartin Mares }
60711f7b31bSMartin Mares
60811f7b31bSMartin Mares struct pci_methods pm_linux_sysfs = {
6091660c737SMartin Mares .name = "linux-sysfs",
6101660c737SMartin Mares .help = "The sys filesystem on Linux",
6111660c737SMartin Mares .config = sysfs_config,
6121660c737SMartin Mares .detect = sysfs_detect,
6131660c737SMartin Mares .init = sysfs_init,
6141660c737SMartin Mares .cleanup = sysfs_cleanup,
6151660c737SMartin Mares .scan = sysfs_scan,
6161660c737SMartin Mares .fill_info = sysfs_fill_info,
6171660c737SMartin Mares .read = sysfs_read,
6181660c737SMartin Mares .write = sysfs_write,
6191660c737SMartin Mares .read_vpd = sysfs_read_vpd,
6201660c737SMartin Mares .cleanup_dev = sysfs_cleanup_dev,
62111f7b31bSMartin Mares };
622