xref: /pciutils/lib/sysfs.c (revision 49efa87f)
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