xref: /aosp_15_r20/external/pciutils/lib/sysfs.c (revision c2e0c6b56a71da9abe8df5c8348fb3eb5c2c9251)
1 /*
2  *	The PCI Library -- Configuration Access via /sys/bus/pci
3  *
4  *	Copyright (c) 2003 Matthew Wilcox <[email protected]>
5  *	Copyright (c) 1997--2024 Martin Mares <[email protected]>
6  *
7  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
8  *
9  *	SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 #define _GNU_SOURCE
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdarg.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <libgen.h>
23 #include <sys/types.h>
24 
25 #include "internal.h"
26 
27 static void
sysfs_config(struct pci_access * a)28 sysfs_config(struct pci_access *a)
29 {
30   pci_define_param(a, "sysfs.path", PCI_PATH_SYS_BUS_PCI, "Path to the sysfs device tree");
31 }
32 
33 static inline char *
sysfs_name(struct pci_access * a)34 sysfs_name(struct pci_access *a)
35 {
36   return pci_get_param(a, "sysfs.path");
37 }
38 
39 static int
sysfs_detect(struct pci_access * a)40 sysfs_detect(struct pci_access *a)
41 {
42   if (access(sysfs_name(a), R_OK))
43     {
44       a->debug("...cannot open %s", sysfs_name(a));
45       return 0;
46     }
47   a->debug("...using %s", sysfs_name(a));
48   return 1;
49 }
50 
51 static void
sysfs_init(struct pci_access * a)52 sysfs_init(struct pci_access *a)
53 {
54   a->fd = -1;
55   a->fd_vpd = -1;
56 }
57 
58 static void
sysfs_flush_cache(struct pci_access * a)59 sysfs_flush_cache(struct pci_access *a)
60 {
61   if (a->fd >= 0)
62     {
63       close(a->fd);
64       a->fd = -1;
65     }
66   if (a->fd_vpd >= 0)
67     {
68       close(a->fd_vpd);
69       a->fd_vpd = -1;
70     }
71   a->cached_dev = NULL;
72 }
73 
74 static void
sysfs_cleanup(struct pci_access * a)75 sysfs_cleanup(struct pci_access *a)
76 {
77   sysfs_flush_cache(a);
78 }
79 
80 #define OBJNAMELEN 1024
81 static void
sysfs_obj_name(struct pci_dev * d,char * object,char * buf)82 sysfs_obj_name(struct pci_dev *d, char *object, char *buf)
83 {
84   int n = snprintf(buf, OBJNAMELEN, "%s/devices/%04x:%02x:%02x.%d/%s",
85 		   sysfs_name(d->access), d->domain, d->bus, d->dev, d->func, object);
86   if (n < 0 || n >= OBJNAMELEN)
87     d->access->error("File name too long");
88 }
89 
90 #define OBJBUFSIZE 1024
91 
92 static int
sysfs_get_string(struct pci_dev * d,char * object,char * buf,int mandatory)93 sysfs_get_string(struct pci_dev *d, char *object, char *buf, int mandatory)
94 {
95   struct pci_access *a = d->access;
96   int fd, n;
97   char namebuf[OBJNAMELEN];
98   void (*warn)(char *msg, ...) = (mandatory ? a->error : a->warning);
99 
100   sysfs_obj_name(d, object, namebuf);
101   fd = open(namebuf, O_RDONLY);
102   if (fd < 0)
103     {
104       if (mandatory || errno != ENOENT)
105 	warn("Cannot open %s: %s", namebuf, strerror(errno));
106       return 0;
107     }
108   n = read(fd, buf, OBJBUFSIZE);
109   int read_errno = errno;
110   close(fd);
111   if (n < 0)
112     {
113       warn("Error reading %s: %s", namebuf, strerror(read_errno));
114       return 0;
115     }
116   if (n >= OBJBUFSIZE)
117     {
118       warn("Value in %s too long", namebuf);
119       return 0;
120     }
121   buf[n] = 0;
122   return 1;
123 }
124 
125 static char *
sysfs_deref_link(struct pci_dev * d,char * link_name)126 sysfs_deref_link(struct pci_dev *d, char *link_name)
127 {
128   char path[2*OBJNAMELEN], rel_path[OBJNAMELEN];
129 
130   sysfs_obj_name(d, link_name, path);
131   memset(rel_path, 0, sizeof(rel_path));
132 
133   if (readlink(path, rel_path, sizeof(rel_path)) < 0)
134     return NULL;
135 
136   sysfs_obj_name(d, "", path);
137   strcat(path, rel_path);
138 
139   // Returns a pointer to malloc'ed memory
140   return realpath(path, NULL);
141 }
142 
143 static int
sysfs_get_value(struct pci_dev * d,char * object,int mandatory)144 sysfs_get_value(struct pci_dev *d, char *object, int mandatory)
145 {
146   char buf[OBJBUFSIZE];
147 
148   if (sysfs_get_string(d, object, buf, mandatory))
149     return strtol(buf, NULL, 0);
150   else
151     return -1;
152 }
153 
154 static void
sysfs_get_resources(struct pci_dev * d)155 sysfs_get_resources(struct pci_dev *d)
156 {
157   struct pci_access *a = d->access;
158   char namebuf[OBJNAMELEN], buf[256];
159   struct { pciaddr_t flags, base_addr, size; } lines[10];
160   int have_bar_bases, have_rom_base, have_bridge_bases;
161   FILE *file;
162   int i;
163 
164   have_bar_bases = have_rom_base = have_bridge_bases = 0;
165   sysfs_obj_name(d, "resource", namebuf);
166   file = fopen(namebuf, "r");
167   if (!file)
168     a->error("Cannot open %s: %s", namebuf, strerror(errno));
169   for (i = 0; i < 7+6+4+1; i++)
170     {
171       unsigned long long start, end, size, flags;
172       if (!fgets(buf, sizeof(buf), file))
173 	break;
174       if (sscanf(buf, "%llx %llx %llx", &start, &end, &flags) != 3)
175 	a->error("Syntax error in %s", namebuf);
176       if (end > start)
177 	size = end - start + 1;
178       else
179 	size = 0;
180       if (i < 6)
181 	{
182 	  d->flags[i] = flags;
183 	  flags &= PCI_ADDR_FLAG_MASK;
184 	  d->base_addr[i] = start | flags;
185 	  d->size[i] = size;
186 	  have_bar_bases = 1;
187 	}
188       else if (i == 6)
189 	{
190 	  d->rom_flags = flags;
191 	  flags &= PCI_ADDR_FLAG_MASK;
192 	  d->rom_base_addr = start | flags;
193 	  d->rom_size = size;
194 	  have_rom_base = 1;
195 	}
196       else if (i < 7+6+4)
197         {
198           /*
199            * If kernel was compiled without CONFIG_PCI_IOV option then after
200            * the ROM line for configured bridge device (that which had set
201            * subordinary bus number to non-zero value) are four additional lines
202            * which describe resources behind bridge. For PCI-to-PCI bridges they
203            * are: IO, MEM, PREFMEM and empty. For CardBus bridges they are: IO0,
204            * IO1, MEM0 and MEM1. For unconfigured bridges and other devices
205            * there is no additional line after the ROM line. If kernel was
206            * compiled with CONFIG_PCI_IOV option then after the ROM line and
207            * before the first bridge resource line are six additional lines
208            * which describe IOV resources. Read all remaining lines in resource
209            * file and based on the number of remaining lines (0, 4, 6, 10) parse
210            * resources behind bridge.
211            */
212           lines[i-7].flags = flags;
213           lines[i-7].base_addr = start;
214           lines[i-7].size = size;
215         }
216     }
217   if (i == 7+4 || i == 7+6+4)
218     {
219       int offset = (i == 7+6+4) ? 6 : 0;
220       for (i = 0; i < 4; i++)
221         {
222           d->bridge_flags[i] = lines[offset+i].flags;
223           d->bridge_base_addr[i] = lines[offset+i].base_addr;
224           d->bridge_size[i] = lines[offset+i].size;
225         }
226       have_bridge_bases = 1;
227     }
228   fclose(file);
229   if (!have_bar_bases)
230     clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS);
231   if (!have_rom_base)
232     clear_fill(d, PCI_FILL_ROM_BASE);
233   if (!have_bridge_bases)
234     clear_fill(d, PCI_FILL_BRIDGE_BASES);
235 }
236 
sysfs_scan(struct pci_access * a)237 static void sysfs_scan(struct pci_access *a)
238 {
239   char dirname[1024];
240   DIR *dir;
241   struct dirent *entry;
242   int n;
243 
244   n = snprintf(dirname, sizeof(dirname), "%s/devices", sysfs_name(a));
245   if (n < 0 || n >= (int) sizeof(dirname))
246     a->error("Directory name too long");
247   dir = opendir(dirname);
248   if (!dir)
249     a->error("Cannot open %s", dirname);
250   while ((entry = readdir(dir)))
251     {
252       struct pci_dev *d;
253       unsigned int dom, bus, dev, func;
254 
255       /* ".", ".." or a special non-device perhaps */
256       if (entry->d_name[0] == '.')
257 	continue;
258 
259       d = pci_alloc_dev(a);
260       if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4)
261 	a->error("sysfs_scan: Couldn't parse entry name %s", entry->d_name);
262 
263       /* Ensure kernel provided domain that fits in a signed integer */
264       if (dom > 0x7fffffff)
265 	a->error("sysfs_scan: Invalid domain %x", dom);
266 
267       d->domain = dom;
268       d->bus = bus;
269       d->dev = dev;
270       d->func = func;
271       pci_link_dev(a, d);
272     }
273   closedir(dir);
274 }
275 
276 static void
sysfs_fill_slots(struct pci_access * a)277 sysfs_fill_slots(struct pci_access *a)
278 {
279   char dirname[1024];
280   DIR *dir;
281   struct dirent *entry;
282   int n;
283 
284   n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a));
285   if (n < 0 || n >= (int) sizeof(dirname))
286     a->error("Directory name too long");
287   dir = opendir(dirname);
288   if (!dir)
289     return;
290 
291   while (entry = readdir(dir))
292     {
293       char namebuf[OBJNAMELEN], buf[16];
294       FILE *file;
295       unsigned int dom, bus, dev;
296       int res = 0;
297       struct pci_dev *d;
298 
299       /* ".", ".." or a special non-device perhaps */
300       if (entry->d_name[0] == '.')
301 	continue;
302 
303       n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address");
304       if (n < 0 || n >= OBJNAMELEN)
305 	a->error("File name too long");
306       file = fopen(namebuf, "r");
307       /*
308        * Old versions of Linux had a fakephp which didn't have an 'address'
309        * file.  There's no useful information to be gleaned from these
310        * devices, pretend they're not there.
311        */
312       if (!file)
313 	continue;
314 
315       if (!fgets(buf, sizeof(buf), file) || (res = sscanf(buf, "%x:%x:%x", &dom, &bus, &dev)) < 3)
316 	{
317 	  /*
318 	   * In some cases, the slot is not tied to a specific device before
319 	   * a card gets inserted. This happens for example on IBM pSeries
320 	   * and we need not warn about it.
321 	   */
322 	  if (res != 2)
323 	    a->warning("sysfs_fill_slots: Couldn't parse entry address %s", buf);
324 	}
325       else
326 	{
327 	  for (d = a->devices; d; d = d->next)
328 	    if (dom == (unsigned)d->domain && bus == d->bus && dev == d->dev && !d->phy_slot)
329 	      d->phy_slot = pci_set_property(d, PCI_FILL_PHYS_SLOT, entry->d_name);
330 	}
331       fclose(file);
332     }
333   closedir(dir);
334 }
335 
336 static void
sysfs_fill_info(struct pci_dev * d,unsigned int flags)337 sysfs_fill_info(struct pci_dev *d, unsigned int flags)
338 {
339   int value, want_class, want_class_ext;
340 
341   if (!d->access->buscentric)
342     {
343       /*
344        *  These fields can be read from the config registers, but we want to show
345        *  the kernel's view, which has regions and IRQs remapped and other fields
346        *  (most importantly classes) possibly fixed if the device is known broken.
347        */
348       if (want_fill(d, flags, PCI_FILL_IDENT))
349 	{
350 	  d->vendor_id = sysfs_get_value(d, "vendor", 1);
351 	  d->device_id = sysfs_get_value(d, "device", 1);
352 	}
353       want_class = want_fill(d, flags, PCI_FILL_CLASS);
354       want_class_ext = want_fill(d, flags, PCI_FILL_CLASS_EXT);
355       if (want_class || want_class_ext)
356         {
357 	  value = sysfs_get_value(d, "class", 1);
358 	  if (want_class)
359 	    d->device_class = value >> 8;
360 	  if (want_class_ext)
361 	    {
362 	      d->prog_if = value & 0xff;
363 	      value = sysfs_get_value(d, "revision", 0);
364 	      if (value < 0)
365 	        value = pci_read_byte(d, PCI_REVISION_ID);
366 	      if (value >= 0)
367 	        d->rev_id = value;
368 	    }
369 	}
370       if (want_fill(d, flags, PCI_FILL_SUBSYS))
371 	{
372 	  value = sysfs_get_value(d, "subsystem_vendor", 0);
373 	  if (value >= 0)
374 	    {
375 	      d->subsys_vendor_id = value;
376 	      value = sysfs_get_value(d, "subsystem_device", 0);
377 	      if (value >= 0)
378 	        d->subsys_id = value;
379 	    }
380 	  else
381 	    clear_fill(d, PCI_FILL_SUBSYS);
382 	}
383       if (want_fill(d, flags, PCI_FILL_IRQ))
384 	  d->irq = sysfs_get_value(d, "irq", 1);
385       if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
386 	  sysfs_get_resources(d);
387       if (want_fill(d, flags, PCI_FILL_PARENT))
388 	{
389 	  unsigned int domain, bus, dev, func;
390 	  char *path_abs, *path_canon, *name;
391 	  char path_rel[OBJNAMELEN];
392 	  struct pci_dev *parent;
393 
394 	  /* Construct sysfs path for parent device */
395 	  sysfs_obj_name(d, "..", path_rel);
396 	  path_abs = realpath(path_rel, NULL);
397 	  name = path_abs ? strrchr(path_abs, '/') : NULL;
398 	  name = name ? name+1 : name;
399 	  parent = NULL;
400 
401 	  if (name && sscanf(name, "%x:%x:%x.%d", &domain, &bus, &dev, &func) == 4 && domain <= 0x7fffffff)
402 	    for (parent = d->access->devices; parent; parent = parent->next)
403 	      if (parent->domain == (int)domain && parent->bus == bus && parent->dev == dev && parent->func == func)
404 	        break;
405 
406 	  if (parent)
407 	    {
408 	      /* Check if parsed BDF address from parent sysfs device is really expected PCI device */
409 	      sysfs_obj_name(parent, ".", path_rel);
410 	      path_canon = realpath(path_rel, NULL);
411 	      if (!path_canon || strcmp(path_canon, path_abs) != 0)
412 	        parent = NULL;
413 
414 	      if (path_canon)
415 		free(path_canon);
416 	    }
417 
418 	  if (parent)
419 	    d->parent = parent;
420 	  else
421 	    clear_fill(d, PCI_FILL_PARENT);
422 
423 	  if (path_abs)
424 	    free(path_abs);
425 	}
426     }
427 
428   if (want_fill(d, flags, PCI_FILL_PHYS_SLOT))
429     {
430       struct pci_dev *pd;
431       sysfs_fill_slots(d->access);
432       for (pd = d->access->devices; pd; pd = pd->next)
433 	pd->known_fields |= PCI_FILL_PHYS_SLOT;
434     }
435 
436   if (want_fill(d, flags, PCI_FILL_MODULE_ALIAS))
437     {
438       char buf[OBJBUFSIZE];
439       if (sysfs_get_string(d, "modalias", buf, 0))
440 	d->module_alias = pci_set_property(d, PCI_FILL_MODULE_ALIAS, buf);
441     }
442 
443   if (want_fill(d, flags, PCI_FILL_LABEL))
444     {
445       char buf[OBJBUFSIZE];
446       if (sysfs_get_string(d, "label", buf, 0))
447 	d->label = pci_set_property(d, PCI_FILL_LABEL, buf);
448     }
449 
450   if (want_fill(d, flags, PCI_FILL_NUMA_NODE))
451     d->numa_node = sysfs_get_value(d, "numa_node", 0);
452 
453   if (want_fill(d, flags, PCI_FILL_IOMMU_GROUP))
454     {
455       char *group_link = sysfs_deref_link(d, "iommu_group");
456       if (group_link)
457         {
458           pci_set_property(d, PCI_FILL_IOMMU_GROUP, basename(group_link));
459           free(group_link);
460         }
461     }
462 
463   if (want_fill(d, flags, PCI_FILL_DT_NODE))
464     {
465       char *node = sysfs_deref_link(d, "of_node");
466       if (node)
467 	{
468 	  pci_set_property(d, PCI_FILL_DT_NODE, node);
469 	  free(node);
470 	}
471     }
472 
473   if (want_fill(d, flags, PCI_FILL_DRIVER))
474     {
475       char *driver_path = sysfs_deref_link(d, "driver");
476       if (driver_path)
477         {
478           char *driver = strrchr(driver_path, '/');
479           driver = driver ? driver+1 : driver_path;
480           pci_set_property(d, PCI_FILL_DRIVER, driver);
481           free(driver_path);
482         }
483       else
484         clear_fill(d, PCI_FILL_DRIVER);
485     }
486 
487   if (want_fill(d, flags, PCI_FILL_RCD_LNK))
488     {
489       char buf[OBJBUFSIZE];
490       if (sysfs_get_string(d, "rcd_link_cap", buf, 0))
491         d->rcd_link_cap = strtoul(buf, NULL, 16);
492       if (sysfs_get_string(d, "rcd_link_ctrl", buf, 0))
493         d->rcd_link_ctrl = strtoul(buf, NULL, 16);
494       if (sysfs_get_string(d, "rcd_link_status", buf, 0))
495         d->rcd_link_status = strtoul(buf, NULL, 16);
496     }
497 
498   pci_generic_fill_info(d, flags);
499 }
500 
501 /* Intent of the sysfs_setup() caller */
502 enum
503   {
504     SETUP_READ_CONFIG = 0,
505     SETUP_WRITE_CONFIG = 1,
506     SETUP_READ_VPD = 2
507   };
508 
509 static int
sysfs_setup(struct pci_dev * d,int intent)510 sysfs_setup(struct pci_dev *d, int intent)
511 {
512   struct pci_access *a = d->access;
513   char namebuf[OBJNAMELEN];
514 
515   if (a->cached_dev != d || (intent == SETUP_WRITE_CONFIG && !a->fd_rw))
516     {
517       sysfs_flush_cache(a);
518       a->cached_dev = d;
519     }
520 
521   if (intent == SETUP_READ_VPD)
522     {
523       if (a->fd_vpd < 0)
524 	{
525 	  sysfs_obj_name(d, "vpd", namebuf);
526 	  a->fd_vpd = open(namebuf, O_RDONLY);
527 	  /* No warning on error; vpd may be absent or accessible only to root */
528 	}
529       return a->fd_vpd;
530     }
531 
532   if (a->fd < 0)
533     {
534       sysfs_obj_name(d, "config", namebuf);
535       a->fd_rw = a->writeable || intent == SETUP_WRITE_CONFIG;
536       a->fd = open(namebuf, a->fd_rw ? O_RDWR : O_RDONLY);
537       if (a->fd < 0)
538 	a->warning("Cannot open %s", namebuf);
539     }
540   return a->fd;
541 }
542 
sysfs_read(struct pci_dev * d,int pos,byte * buf,int len)543 static int sysfs_read(struct pci_dev *d, int pos, byte *buf, int len)
544 {
545   int fd = sysfs_setup(d, SETUP_READ_CONFIG);
546   int res;
547 
548   if (fd < 0)
549     return 0;
550   res = pread(fd, buf, len, pos);
551   if (res < 0)
552     {
553       d->access->warning("sysfs_read: read failed: %s", strerror(errno));
554       return 0;
555     }
556   else if (res != len)
557     return 0;
558   return 1;
559 }
560 
sysfs_write(struct pci_dev * d,int pos,byte * buf,int len)561 static int sysfs_write(struct pci_dev *d, int pos, byte *buf, int len)
562 {
563   int fd = sysfs_setup(d, SETUP_WRITE_CONFIG);
564   int res;
565 
566   if (fd < 0)
567     return 0;
568   res = pwrite(fd, buf, len, pos);
569   if (res < 0)
570     {
571       d->access->warning("sysfs_write: write failed: %s", strerror(errno));
572       return 0;
573     }
574   else if (res != len)
575     {
576       d->access->warning("sysfs_write: tried to write %d bytes at %d, but only %d succeeded", len, pos, res);
577       return 0;
578     }
579   return 1;
580 }
581 
sysfs_read_vpd(struct pci_dev * d,int pos,byte * buf,int len)582 static int sysfs_read_vpd(struct pci_dev *d, int pos, byte *buf, int len)
583 {
584   int fd = sysfs_setup(d, SETUP_READ_VPD);
585   int res;
586 
587   if (fd < 0)
588     return 0;
589   res = pread(fd, buf, len, pos);
590   if (res < 0)
591     {
592       d->access->warning("sysfs_read_vpd: read failed: %s", strerror(errno));
593       return 0;
594     }
595   else if (res != len)
596     return 0;
597   return 1;
598 }
599 
sysfs_cleanup_dev(struct pci_dev * d)600 static void sysfs_cleanup_dev(struct pci_dev *d)
601 {
602   struct pci_access *a = d->access;
603 
604   if (a->cached_dev == d)
605     sysfs_flush_cache(a);
606 }
607 
608 struct pci_methods pm_linux_sysfs = {
609   .name = "linux-sysfs",
610   .help = "The sys filesystem on Linux",
611   .config = sysfs_config,
612   .detect = sysfs_detect,
613   .init = sysfs_init,
614   .cleanup = sysfs_cleanup,
615   .scan = sysfs_scan,
616   .fill_info = sysfs_fill_info,
617   .read = sysfs_read,
618   .write = sysfs_write,
619   .read_vpd = sysfs_read_vpd,
620   .cleanup_dev = sysfs_cleanup_dev,
621 };
622