Switchtec Userspace  PROJECT_NUMBER = PROJECT_NUMBER=PROJECT_NUMBER = 2.2
linux.c
1 /*
2  * Microsemi Switchtec(tm) PCIe Management Library
3  * Copyright (c) 2017, Microsemi Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21  * OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24 
25 #ifdef __linux__
26 
27 #define SWITCHTEC_LIB_LINUX
28 
29 #include "../switchtec_priv.h"
30 #include "switchtec/switchtec.h"
31 #include "switchtec/pci.h"
32 #include "switchtec/utils.h"
33 #include "mmap_gas.h"
34 #include "gasops.h"
35 
36 #include <linux/switchtec_ioctl.h>
37 
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <endian.h>
41 #include <dirent.h>
42 #include <libgen.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <sys/mman.h>
46 #include <sys/sysmacros.h>
47 #include <glob.h>
48 #include <poll.h>
49 
50 #include <errno.h>
51 #include <string.h>
52 #include <stddef.h>
53 
54 static const char *sys_path = "/sys/class/switchtec";
55 
56 struct switchtec_linux {
57  struct switchtec_dev dev;
58  int fd;
59 };
60 
61 #define to_switchtec_linux(d) \
62  ((struct switchtec_linux *) \
63  ((char *)d - offsetof(struct switchtec_linux, dev)))
64 
65 const char *platform_strerror(void)
66 {
67  return "Success";
68 }
69 
70 static int dev_to_sysfs_path(struct switchtec_linux *ldev, const char *suffix,
71  char *buf, size_t buflen)
72 {
73  int ret;
74  struct stat stat;
75 
76  ret = fstat(ldev->fd, &stat);
77  if (ret < 0)
78  return ret;
79 
80  snprintf(buf, buflen,
81  "/sys/dev/char/%d:%d/%s",
82  major(stat.st_rdev), minor(stat.st_rdev), suffix);
83 
84  return 0;
85 }
86 
87 static int sysfs_read_str(const char *path, char *buf, size_t buflen)
88 {
89  int ret;
90  int fd;
91 
92  fd = open(path, O_RDONLY);
93  if (fd < 0)
94  return -1;
95 
96  ret = read(fd, buf, buflen);
97 
98  close(fd);
99 
100  return ret;
101 }
102 
103 static long long sysfs_read_int(const char *path, int base)
104 {
105  int ret;
106  char buf[64];
107 
108  ret = sysfs_read_str(path, buf, sizeof(buf));
109  if (ret < 0)
110  return ret;
111 
112  return strtoll(buf, NULL, base);
113 }
114 
115 static int check_switchtec_device(struct switchtec_linux *ldev)
116 {
117  int ret;
118  char syspath[PATH_MAX];
119 
120  ret = dev_to_sysfs_path(ldev, "device/switchtec", syspath,
121  sizeof(syspath));
122  if (ret)
123  return ret;
124 
125  ret = access(syspath, F_OK);
126  if (ret)
127  errno = ENOTTY;
128 
129  return ret;
130 }
131 
132 static int get_partition(struct switchtec_linux *ldev)
133 {
134  int ret;
135  char syspath[PATH_MAX];
136 
137  ret = dev_to_sysfs_path(ldev, "partition", syspath,
138  sizeof(syspath));
139  if (ret)
140  return ret;
141 
142  ldev->dev.partition = sysfs_read_int(syspath, 10);
143  if (ldev->dev.partition < 0)
144  return ldev->dev.partition;
145 
146  ret = dev_to_sysfs_path(ldev, "partition_count", syspath,
147  sizeof(syspath));
148  if (ret)
149  return ret;
150 
151  ldev->dev.partition_count = sysfs_read_int(syspath, 10);
152  if (ldev->dev.partition_count < 1)
153  return -1;
154 
155  return 0;
156 }
157 
158 static void linux_close(struct switchtec_dev *dev)
159 {
160  struct switchtec_linux *ldev = to_switchtec_linux(dev);
161 
162  close(ldev->fd);
163  free(ldev);
164 }
165 
166 static int scan_dev_filter(const struct dirent *d)
167 {
168  if (d->d_name[0] == '.')
169  return 0;
170 
171  return 1;
172 }
173 
174 static void get_device_str(const char *path, const char *file,
175  char *buf, size_t buflen)
176 {
177  char sysfs_path[PATH_MAX];
178  int ret;
179 
180  snprintf(sysfs_path, sizeof(sysfs_path), "%s/%s",
181  path, file);
182 
183  ret = sysfs_read_str(sysfs_path, buf, buflen);
184  if (ret < 0 || buf[0] == -1)
185  snprintf(buf, buflen, "unknown");
186 
187  buf[strcspn(buf, "\n")] = 0;
188 }
189 
190 static void get_fw_version(const char *path, char *buf, size_t buflen)
191 {
192  char sysfs_path[PATH_MAX];
193  int fw_ver;
194  int ret;
195 
196  ret = snprintf(sysfs_path, sizeof(sysfs_path), "%s/fw_version",
197  path);
198  if (ret >= sizeof(sysfs_path))
199  goto unknown_version;
200 
201  fw_ver = sysfs_read_int(sysfs_path, 16);
202 
203  if (fw_ver < 0)
204  goto unknown_version;
205 
206  version_to_string(fw_ver, buf, buflen);
207  return;
208 
209 unknown_version:
210  snprintf(buf, buflen, "unknown");
211 }
212 
213 int switchtec_list(struct switchtec_device_info **devlist)
214 {
215  struct dirent **devices;
216  int i, n;
217  char link_path[PATH_MAX];
218  char pci_path[PATH_MAX] = "";
219  struct switchtec_device_info *dl;
220 
221  n = scandir(sys_path, &devices, scan_dev_filter, alphasort);
222  if (n <= 0)
223  return n;
224 
225  dl = *devlist = calloc(n, sizeof(struct switchtec_device_info));
226 
227  if (!dl) {
228  for (i = 0; i < n; i++)
229  free(devices[i]);
230  free(devices);
231  errno = ENOMEM;
232  return -errno;
233  }
234 
235  for (i = 0; i < n; i++) {
236  snprintf(dl[i].name, sizeof(dl[i].name),
237  "%s", devices[i]->d_name);
238  snprintf(dl[i].path, sizeof(dl[i].path),
239  "/dev/%s", devices[i]->d_name);
240 
241  snprintf(link_path, sizeof(link_path), "%s/%s/device",
242  sys_path, devices[i]->d_name);
243 
244  if (readlink(link_path, pci_path, sizeof(pci_path)) > 0)
245  snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
246  "%s", basename(pci_path));
247  else
248  snprintf(dl[i].pci_dev, sizeof(dl[i].pci_dev),
249  "unknown pci device");
250 
251  snprintf(link_path, sizeof(link_path), "%s/%s",
252  sys_path, devices[i]->d_name);
253 
254  get_device_str(link_path, "product_id", dl[i].product_id,
255  sizeof(dl[i].product_id));
256  get_device_str(link_path, "product_revision",
257  dl[i].product_rev, sizeof(dl[i].product_rev));
258  get_fw_version(link_path, dl[i].fw_version,
259  sizeof(dl[i].fw_version));
260 
261  free(devices[i]);
262  }
263 
264  free(devices);
265  return n;
266 }
267 
268 static int linux_get_device_id(struct switchtec_dev *dev)
269 {
270  char link_path[PATH_MAX];
271 
272  snprintf(link_path, sizeof(link_path), "%s/%s/device/device",
273  sys_path, basename(dev->name));
274 
275  return sysfs_read_int(link_path, 16);
276 }
277 
278 static int linux_get_fw_version(struct switchtec_dev *dev, char *buf,
279  size_t buflen)
280 {
281  int ret;
282  long long version;
283  char syspath[PATH_MAX];
284  struct switchtec_linux *ldev = to_switchtec_linux(dev);
285 
286  ret = dev_to_sysfs_path(ldev, "fw_version", syspath, sizeof(syspath));
287  if (ret)
288  return ret;
289 
290  version = sysfs_read_int(syspath, 16);
291  if (version < 0)
292  return version;
293 
294  version_to_string(version, buf, buflen);
295 
296  return 0;
297 }
298 
299 static int submit_cmd(struct switchtec_linux *ldev, uint32_t cmd,
300  const void *payload, size_t payload_len)
301 {
302  int ret;
303  size_t bufsize = payload_len + sizeof(cmd);
304  char buf[bufsize];
305 
306  cmd = htole32(cmd);
307  memcpy(buf, &cmd, sizeof(cmd));
308  memcpy(&buf[sizeof(cmd)], payload, payload_len);
309 
310  ret = write(ldev->fd, buf, bufsize);
311 
312  if (ret < 0)
313  return ret;
314 
315  if (ret != bufsize) {
316  errno = EIO;
317  return -errno;
318  }
319 
320  return 0;
321 }
322 
323 static int read_resp(struct switchtec_linux *ldev, void *resp,
324  size_t resp_len)
325 {
326  int32_t ret;
327  size_t bufsize = sizeof(uint32_t) + resp_len;
328  char buf[bufsize];
329 
330  ret = read(ldev->fd, buf, bufsize);
331 
332  if (ret < 0)
333  return ret;
334 
335  if (ret != bufsize) {
336  errno = EIO;
337  return -errno;
338  }
339 
340  memcpy(&ret, buf, sizeof(ret));
341  if (ret)
342  errno = ret;
343 
344  if (!resp)
345  return ret;
346 
347  memcpy(resp, &buf[sizeof(ret)], resp_len);
348 
349  return ret;
350 }
351 
352 static int linux_cmd(struct switchtec_dev *dev, uint32_t cmd,
353  const void *payload, size_t payload_len, void *resp,
354  size_t resp_len)
355 {
356  int ret;
357  struct switchtec_linux *ldev = to_switchtec_linux(dev);
358 
359 retry:
360  ret = submit_cmd(ldev, cmd, payload, payload_len);
361  if (errno == EBADE) {
362  read_resp(ldev, NULL, 0);
363  errno = 0;
364  goto retry;
365  }
366 
367  if (ret < 0)
368  return ret;
369 
370  return read_resp(ldev, resp, resp_len);
371 }
372 
373 static int get_class_devices(const char *searchpath,
374  struct switchtec_status *status)
375 {
376  int i;
377  ssize_t len;
378  char syspath[PATH_MAX];
379  glob_t paths;
380  int found = 0;
381  const size_t MAX_LEN = 256;
382 
383  snprintf(syspath, sizeof(syspath), "%s*/*/device", searchpath);
384  glob(syspath, 0, NULL, &paths);
385 
386  for (i = 0; i < paths.gl_pathc; i++) {
387  char *p = paths.gl_pathv[i];
388 
389  len = readlink(p, syspath, sizeof(syspath));
390  if (len <= 0)
391  continue;
392 
393  p = dirname(p);
394 
395  if (!status->class_devices) {
396  status->class_devices = calloc(MAX_LEN, 1);
397  strcpy(status->class_devices, basename(p));
398  } else {
399  len = strlen(status->class_devices);
400  snprintf(&status->class_devices[len], MAX_LEN - len,
401  ", %s", basename(p));
402  }
403 
404  found = 1;
405  }
406 
407  globfree(&paths);
408  return found;
409 }
410 
411 static void get_port_bdf(const char *searchpath, int port,
412  struct switchtec_status *status)
413 {
414  char syspath[PATH_MAX];
415  glob_t paths;
416  int ret;
417 
418  ret = snprintf(syspath, sizeof(syspath), "%s/*:*:%02x.*",
419  searchpath, port);
420  if (ret >= sizeof(syspath))
421  return;
422 
423  glob(syspath, 0, NULL, &paths);
424 
425  if (paths.gl_pathc == 1)
426  status->pci_bdf = strdup(basename(paths.gl_pathv[0]));
427 
428  globfree(&paths);
429 }
430 
431 static void get_port_bdf_path(struct switchtec_status *status)
432 {
433  char path[PATH_MAX];
434  char rpath[PATH_MAX];
435  int domain, bus, dev, fn;
436  int ptr = 0;
437  char *subpath;
438  int ret;
439 
440  if (!status->pci_bdf)
441  return;
442 
443  snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s",
444  status->pci_bdf);
445 
446  if (!realpath(path, rpath))
447  return;
448 
449  subpath = strtok(rpath, "/");
450  while (subpath) {
451  ret = sscanf(subpath, "%x:%x:%x.%x", &domain, &bus, &dev, &fn);
452  if (ret == 4) {
453  if (ptr == 0)
454  ret = snprintf(path + ptr, sizeof(path) - ptr,
455  "%04x:%02x:%02x:%x/",
456  domain, bus, dev, fn);
457  else
458  ret = snprintf(path + ptr, sizeof(path) - ptr,
459  "%02x.%x/", dev, fn);
460 
461  if (ret <= 0 || ret >= sizeof(path) - ptr)
462  break;
463 
464  ptr += ret;
465  }
466  subpath = strtok(NULL, "/");
467  }
468 
469  if (ptr)
470  path[ptr - 1] = 0;
471 
472  status->pci_bdf_path = strdup(path);
473 }
474 
475 static void get_port_info(struct switchtec_status *status)
476 {
477  int i;
478  char syspath[PATH_MAX];
479  glob_t paths;
480 
481  if (!status->pci_bdf)
482  return;
483 
484  snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/*:*:*/",
485  status->pci_bdf);
486 
487  glob(syspath, 0, NULL, &paths);
488 
489  for (i = 0; i < paths.gl_pathc; i++) {
490  char *p = paths.gl_pathv[i];
491 
492  snprintf(syspath, sizeof(syspath), "%s/vendor", p);
493  status->vendor_id = sysfs_read_int(syspath, 16);
494  if (status->vendor_id < 0)
495  continue;
496 
497  snprintf(syspath, sizeof(syspath), "%s/device", p);
498  status->device_id = sysfs_read_int(syspath, 16);
499  if (status->device_id < 0)
500  continue;
501 
502  if (get_class_devices(p, status)) {
503  if (status->pci_dev)
504  free(status->pci_dev);
505  status->pci_dev = strdup(basename(p));
506  }
507 
508  if (!status->pci_dev)
509  status->pci_dev = strdup(basename(p));
510  }
511 
512  globfree(&paths);
513 }
514 
515 static void get_config_info(struct switchtec_status *status)
516 {
517  int ret;
518  int fd;
519  char syspath[PATH_MAX];
520  uint32_t extcap;
521  int pos = PCI_EXT_CAP_OFFSET;
522  uint16_t acs;
523 
524  snprintf(syspath, sizeof(syspath), "/sys/bus/pci/devices/%s/config",
525  status->pci_bdf);
526 
527  fd = open(syspath, O_RDONLY);
528  if (fd < -1)
529  return;
530 
531  while (1) {
532  ret = pread(fd, &extcap, sizeof(extcap), pos);
533  if (ret != sizeof(extcap) || !extcap)
534  goto close_and_exit;
535 
536  if (PCI_EXT_CAP_ID(extcap) == PCI_EXT_CAP_ID_ACS)
537  break;
538 
539  pos = PCI_EXT_CAP_NEXT(extcap);
540  if (pos < PCI_EXT_CAP_OFFSET)
541  goto close_and_exit;
542  }
543 
544  ret = pread(fd, &acs, sizeof(acs), pos + PCI_ACS_CTRL);
545  if (ret != sizeof(acs))
546  goto close_and_exit;
547 
548  status->acs_ctrl = acs;
549 
550 close_and_exit:
551  close(fd);
552 }
553 
554 static int linux_get_devices(struct switchtec_dev *dev,
555  struct switchtec_status *status,
556  int ports)
557 {
558  int ret;
559  int i;
560  int local_part;
561  char syspath[PATH_MAX];
562  char searchpath[PATH_MAX];
563  struct switchtec_linux *ldev = to_switchtec_linux(dev);
564 
565  ret = dev_to_sysfs_path(ldev, "device", syspath,
566  sizeof(syspath));
567  if (ret)
568  return ret;
569 
570  if (!realpath(syspath, searchpath)) {
571  errno = ENXIO;
572  return -errno;
573  }
574 
575  //Replace eg "0000:03:00.1" into "0000:03:00.0"
576  searchpath[strlen(searchpath) - 1] = '0';
577 
578  local_part = switchtec_partition(dev);
579 
580  for (i = 0; i < ports; i++) {
581  if (status[i].port.partition != local_part)
582  continue;
583 
584  if (status[i].port.upstream) {
585  status->pci_bdf = strdup(basename(searchpath));
586  get_port_bdf_path(&status[i]);
587  continue;
588  }
589 
590  get_port_bdf(searchpath, status[i].port.log_id - 1, &status[i]);
591  get_port_bdf_path(&status[i]);
592  get_port_info(&status[i]);
593  get_config_info(&status[i]);
594  }
595 
596  return 0;
597 }
598 
599 static int linux_pff_to_port(struct switchtec_dev *dev, int pff,
600  int *partition, int *port)
601 {
602  int ret;
603  struct switchtec_ioctl_pff_port p;
604  struct switchtec_linux *ldev = to_switchtec_linux(dev);
605 
606  p.pff = pff;
607  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PFF_TO_PORT, &p);
608  if (ret)
609  return ret;
610 
611  if (partition)
612  *partition = p.partition;
613  if (port)
614  *port = p.port;
615 
616  return 0;
617 }
618 
619 static int linux_port_to_pff(struct switchtec_dev *dev, int partition,
620  int port, int *pff)
621 {
622  int ret;
623  struct switchtec_ioctl_pff_port p;
624  struct switchtec_linux *ldev = to_switchtec_linux(dev);
625 
626  p.port = port;
627  p.partition = partition;
628 
629  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_PORT_TO_PFF, &p);
630  if (ret)
631  return ret;
632 
633  if (pff)
634  *pff = p.pff;
635 
636  return 0;
637 }
638 
639 #ifdef __CHECKER__
640 #define __force __attribute__((force))
641 #else
642 #define __force
643 #endif
644 
645 static ssize_t resource_size(struct switchtec_linux *ldev, const char *fname)
646 {
647  char respath[PATH_MAX];
648  struct stat stat;
649  int fd, ret;
650 
651  ret = dev_to_sysfs_path(ldev, fname, respath,
652  sizeof(respath));
653  if (ret) {
654  errno = ret;
655  return -1;
656  }
657 
658  fd = open(respath, O_RDONLY);
659  if (fd < 0)
660  return -1;
661 
662  ret = fstat(fd, &stat);
663  if (ret < 0) {
664  close(fd);
665  return -1;
666  }
667 
668  close(fd);
669  return stat.st_size;
670 }
671 
672 static int mmap_resource(struct switchtec_linux *ldev, const char *fname,
673  void *addr, size_t offset, size_t size, int writeable)
674 {
675  char respath[PATH_MAX];
676  void *map;
677  int fd, ret = 0;
678 
679  ret = dev_to_sysfs_path(ldev, fname, respath,
680  sizeof(respath));
681  if (ret) {
682  errno = ret;
683  return -1;
684  }
685 
686  fd = open(respath, writeable ? O_RDWR : O_RDONLY);
687  if (fd < 0)
688  return -1;
689 
690  map = mmap(addr, size, (writeable ? PROT_WRITE : 0) | PROT_READ,
691  MAP_SHARED | MAP_FIXED, fd, offset);
692  if (map == MAP_FAILED)
693  ret = -1;
694 
695  close(fd);
696  return ret;
697 }
698 
699 /*
700  * GAS map maps the hardware registers into user memory space.
701  * Needless to say, this can be very dangerous and should only
702  * be done if you know what you are doing. Any register accesses
703  * that use this will remain unsupported by Microsemi unless it's
704  * done within the switchtec user project or otherwise specified.
705  */
706 static gasptr_t linux_gas_map(struct switchtec_dev *dev, int writeable,
707  size_t *map_size)
708 {
709  int ret;
710  void *map;
711  ssize_t msize;
712  struct switchtec_linux *ldev = to_switchtec_linux(dev);
713 
714  msize = resource_size(ldev, "device/resource0");
715  if (msize <= 0)
716  return SWITCHTEC_MAP_FAILED;
717 
718  /*
719  * Reserve virtual address space for the entire GAS mapping.
720  */
721  map = mmap(NULL, msize, PROT_NONE,
722  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
723  if (map == MAP_FAILED)
724  return SWITCHTEC_MAP_FAILED;
725 
726  ret = mmap_resource(ldev, "device/resource0_wc", map, 0,
727  SWITCHTEC_GAS_TOP_CFG_OFFSET, writeable);
728  if (ret) {
729  ret = mmap_resource(ldev, "device/resource0", map, 0,
730  SWITCHTEC_GAS_TOP_CFG_OFFSET,
731  writeable);
732  if (ret)
733  goto unmap_and_exit;
734  }
735 
736  ret = mmap_resource(ldev, "device/resource0",
737  map + SWITCHTEC_GAS_TOP_CFG_OFFSET,
738  SWITCHTEC_GAS_TOP_CFG_OFFSET,
739  msize - SWITCHTEC_GAS_TOP_CFG_OFFSET,
740  writeable);
741  if (ret)
742  goto unmap_and_exit;
743 
744  if (map_size)
745  *map_size = msize;
746 
747  dev->gas_map = (gasptr_t __force)map;
748  dev->gas_map_size = msize;
749 
750  ret = gasop_access_check(dev);
751  if (ret) {
752  errno = ENODEV;
753  goto unmap_and_exit;
754  }
755  return (gasptr_t __force)map;
756 
757 unmap_and_exit:
758  munmap(map, msize);
759  return SWITCHTEC_MAP_FAILED;
760 }
761 
762 static void linux_gas_unmap(struct switchtec_dev *dev, gasptr_t map)
763 {
764  munmap((void __force *)map, dev->gas_map_size);
765 }
766 
767 static int linux_flash_part(struct switchtec_dev *dev,
768  struct switchtec_fw_image_info *info,
769  enum switchtec_fw_image_part_id_gen3 part)
770 {
771  struct switchtec_linux *ldev = to_switchtec_linux(dev);
772  struct switchtec_ioctl_flash_part_info ioctl_info = {0};
773  int ret;
774 
775  switch (part) {
776  case SWITCHTEC_FW_PART_ID_G3_IMG0:
777  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG0;
778  break;
779  case SWITCHTEC_FW_PART_ID_G3_IMG1:
780  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_IMG1;
781  break;
782  case SWITCHTEC_FW_PART_ID_G3_DAT0:
783  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG0;
784  break;
785  case SWITCHTEC_FW_PART_ID_G3_DAT1:
786  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_CFG1;
787  break;
788  case SWITCHTEC_FW_PART_ID_G3_NVLOG:
789  ioctl_info.flash_partition = SWITCHTEC_IOCTL_PART_NVLOG;
790  break;
791  default:
792  return -EINVAL;
793  }
794 
795  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_FLASH_PART_INFO, &ioctl_info);
796  if (ret)
797  return ret;
798 
799  info->part_addr = ioctl_info.address;
800  info->part_len = ioctl_info.length;
801  info->active = false;
802  info->running = false;
803 
804  if (ioctl_info.active & SWITCHTEC_IOCTL_PART_ACTIVE)
805  info->active = true;
806 
807  if (ioctl_info.active & SWITCHTEC_IOCTL_PART_RUNNING)
808  info->running = true;
809 
810  return 0;
811 }
812 
813 static void event_summary_copy(struct switchtec_event_summary *dst,
814  struct switchtec_ioctl_event_summary *src,
815  int size)
816 {
817  int i;
818 
819  dst->global = src->global;
820  dst->part_bitmap = src->part_bitmap;
821  dst->local_part = src->local_part;
822 
823  for (i = 0; i < SWITCHTEC_MAX_PARTS; i++)
824  dst->part[i] = src->part[i];
825 
826  for (i = 0; i < SWITCHTEC_MAX_PFF_CSR && i < size; i++)
827  dst->pff[i] = src->pff[i];
828 }
829 
830 #define EV(t, n)[SWITCHTEC_ ## t ## _EVT_ ## n] = \
831  SWITCHTEC_IOCTL_EVENT_ ## n
832 
833 static const int event_map[] = {
834  EV(GLOBAL, STACK_ERROR),
835  EV(GLOBAL, PPU_ERROR),
836  EV(GLOBAL, ISP_ERROR),
837  EV(GLOBAL, SYS_RESET),
838  EV(GLOBAL, FW_EXC),
839  EV(GLOBAL, FW_NMI),
840  EV(GLOBAL, FW_NON_FATAL),
841  EV(GLOBAL, FW_FATAL),
842  EV(GLOBAL, TWI_MRPC_COMP),
843  EV(GLOBAL, TWI_MRPC_COMP_ASYNC),
844  EV(GLOBAL, CLI_MRPC_COMP),
845  EV(GLOBAL, CLI_MRPC_COMP_ASYNC),
846  EV(GLOBAL, GPIO_INT),
847  EV(GLOBAL, GFMS),
848  EV(PART, PART_RESET),
849  EV(PART, MRPC_COMP),
850  EV(PART, MRPC_COMP_ASYNC),
851  EV(PART, DYN_PART_BIND_COMP),
852  EV(PFF, AER_IN_P2P),
853  EV(PFF, AER_IN_VEP),
854  EV(PFF, DPC),
855  EV(PFF, CTS),
856  EV(PFF, UEC),
857  EV(PFF, HOTPLUG),
858  EV(PFF, IER),
859  EV(PFF, THRESH),
860  EV(PFF, POWER_MGMT),
861  EV(PFF, TLP_THROTTLING),
862  EV(PFF, FORCE_SPEED),
863  EV(PFF, CREDIT_TIMEOUT),
864  EV(PFF, LINK_STATE),
865 };
866 
867 static int linux_event_summary(struct switchtec_dev *dev,
868  struct switchtec_event_summary *sum)
869 {
870  int ret;
871  struct switchtec_ioctl_event_summary isum;
872  struct switchtec_ioctl_event_summary_legacy isum_legacy;
873  struct switchtec_linux *ldev = to_switchtec_linux(dev);
874 
875  if (!sum)
876  return 0;
877 
878  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY, &isum);
879  if (!ret) {
880  event_summary_copy(sum, &isum, ARRAY_SIZE(isum.pff));
881  return ret;
882  }
883 
884  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY, &isum);
885  if (ret < 0)
886  return ret;
887 
888  event_summary_copy(sum, &isum, ARRAY_SIZE(isum_legacy.pff));
889 
890  return 0;
891 }
892 
893 static int linux_event_ctl(struct switchtec_dev *dev,
894  enum switchtec_event_id e,
895  int index, int flags,
896  uint32_t data[5])
897 {
898  int ret;
899  struct switchtec_ioctl_event_ctl ctl;
900  struct switchtec_linux *ldev = to_switchtec_linux(dev);
901 
902  if (e >= SWITCHTEC_MAX_EVENTS)
903  return -EINVAL;
904 
905  ctl.event_id = event_map[e];
906  ctl.flags = 0;
907 
908  if (flags & SWITCHTEC_EVT_FLAG_CLEAR)
909  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR;
910  if (flags & SWITCHTEC_EVT_FLAG_EN_POLL)
911  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
912  if (flags & SWITCHTEC_EVT_FLAG_EN_LOG)
913  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
914  if (flags & SWITCHTEC_EVT_FLAG_EN_CLI)
915  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
916  if (flags & SWITCHTEC_EVT_FLAG_EN_FATAL)
917  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
918  if (flags & SWITCHTEC_EVT_FLAG_DIS_POLL)
919  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL;
920  if (flags & SWITCHTEC_EVT_FLAG_DIS_LOG)
921  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG;
922  if (flags & SWITCHTEC_EVT_FLAG_DIS_CLI)
923  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI;
924  if (flags & SWITCHTEC_EVT_FLAG_DIS_FATAL)
925  ctl.flags |= SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL;
926 
927  ctl.index = index;
928  ret = ioctl(ldev->fd, SWITCHTEC_IOCTL_EVENT_CTL, &ctl);
929 
930  if (ret)
931  return ret;
932 
933  if (data)
934  memcpy(data, ctl.data, sizeof(ctl.data));
935 
936  return ctl.count;
937 }
938 
939 static int linux_event_wait(struct switchtec_dev *dev, int timeout_ms)
940 {
941  int ret;
942  struct switchtec_linux *ldev = to_switchtec_linux(dev);
943  struct pollfd fds = {
944  .fd = ldev->fd,
945  .events = POLLPRI,
946  };
947 
948  ret = poll(&fds, 1, timeout_ms);
949  if (ret <= 0)
950  return ret;
951 
952  if (fds.revents & POLLERR) {
953  errno = ENODEV;
954  return -1;
955  }
956 
957  if (fds.revents & POLLPRI)
958  return 1;
959 
960  return 0;
961 }
962 
963 static const struct switchtec_ops linux_ops = {
964  .close = linux_close,
965  .get_device_id = linux_get_device_id,
966  .get_fw_version = linux_get_fw_version,
967  .cmd = linux_cmd,
968  .get_devices = linux_get_devices,
969  .pff_to_port = linux_pff_to_port,
970  .port_to_pff = linux_port_to_pff,
971  .gas_map = linux_gas_map,
972  .gas_unmap = linux_gas_unmap,
973  .flash_part = linux_flash_part,
974  .event_summary = linux_event_summary,
975  .event_ctl = linux_event_ctl,
976  .event_wait = linux_event_wait,
977 
978  .gas_read8 = mmap_gas_read8,
979  .gas_read16 = mmap_gas_read16,
980  .gas_read32 = mmap_gas_read32,
981  .gas_read64 = mmap_gas_read64,
982  .gas_write8 = mmap_gas_write8,
983  .gas_write16 = mmap_gas_write16,
984  .gas_write32 = mmap_gas_write32,
985  .gas_write32_no_retry = mmap_gas_write32,
986  .gas_write64 = mmap_gas_write64,
987  .memcpy_to_gas = mmap_memcpy_to_gas,
988  .memcpy_from_gas = mmap_memcpy_from_gas,
989  .write_from_gas = mmap_write_from_gas,
990 };
991 
992 struct switchtec_dev *switchtec_open_by_path(const char *path)
993 {
994  struct switchtec_linux *ldev;
995  int fd;
996 
997  fd = open(path, O_RDWR | O_CLOEXEC);
998  if (fd < 0)
999  return NULL;
1000 
1001  if (isatty(fd))
1002  return switchtec_open_uart(fd);
1003  else
1004  errno = 0;
1005 
1006  ldev = malloc(sizeof(*ldev));
1007  if (!ldev)
1008  return NULL;
1009 
1010  ldev->fd = fd;
1011 
1012  if (check_switchtec_device(ldev))
1013  goto err_close_free;
1014 
1015  if (get_partition(ldev))
1016  goto err_close_free;
1017 
1018  ldev->dev.ops = &linux_ops;
1019 
1020  return &ldev->dev;
1021 
1022 err_close_free:
1023  close(ldev->fd);
1024  free(ldev);
1025  return NULL;
1026 }
1027 
1028 struct switchtec_dev *switchtec_open_by_index(int index)
1029 {
1030  char path[PATH_MAX];
1031  struct switchtec_dev *dev;
1032 
1033  snprintf(path, sizeof(path), "/dev/switchtec%d", index);
1034 
1035  dev = switchtec_open_by_path(path);
1036 
1037  if (errno == ENOENT)
1038  errno = ENODEV;
1039 
1040  return dev;
1041 }
1042 
1043 struct switchtec_dev *switchtec_open_by_pci_addr(int domain, int bus,
1044  int device, int func)
1045 {
1046  char path[PATH_MAX];
1047  struct switchtec_dev *dev;
1048  struct dirent *dirent;
1049  DIR *dir;
1050 
1051  snprintf(path, sizeof(path),
1052  "/sys/bus/pci/devices/%04x:%02x:%02x.%x/switchtec",
1053  domain, bus, device, func);
1054 
1055  dir = opendir(path);
1056  if (!dir)
1057  goto err_out;
1058 
1059  while ((dirent = readdir(dir))) {
1060  if (dirent->d_name[0] != '.')
1061  break;
1062  }
1063 
1064  if (!dirent)
1065  goto err_close;
1066 
1067  /*
1068  * Should only be one switchtec device, if there are
1069  * more then something is wrong
1070  */
1071  if (readdir(dir))
1072  goto err_close;
1073 
1074  snprintf(path, sizeof(path), "/dev/%s", dirent->d_name);
1075  printf("%s\n", path);
1076  dev = switchtec_open(path);
1077 
1078  closedir(dir);
1079  return dev;
1080 
1081 err_close:
1082  closedir(dir);
1083 err_out:
1084  errno = ENODEV;
1085  return NULL;
1086 }
1087 
1088 #endif
char product_rev[8]
Product revision.
Definition: switchtec.h:129
char * pci_dev
PCI BDF of the device on the port.
Definition: switchtec.h:168
unsigned int acs_ctrl
ACS Setting of the Port.
Definition: switchtec.h:172
size_t part_addr
Address of the partition.
Definition: switchtec.h:218
Gas Operations for platforms that the gas is mapped into the address space.
switchtec_event_id
Enumeration of all possible events.
Definition: switchtec.h:265
Information about a firmware image or partition.
Definition: switchtec.h:213
char * pci_bdf_path
PCI BDF path of the port.
Definition: switchtec.h:166
__gas struct switchtec_gas * gasptr_t
Shortform for a pointer to the GAS register space.
Definition: switchtec.h:77
char product_id[32]
Product ID.
Definition: switchtec.h:128
int switchtec_list(struct switchtec_device_info **devlist)
List all the switchtec devices in the system.
Event summary bitmaps.
Definition: switchtec.h:250
uint64_t part_bitmap
Bitmap of partitions with active events.
Definition: switchtec.h:252
Main Switchtec header.
_PURE int switchtec_partition(struct switchtec_dev *dev)
Get the partiton number of the device that was opened.
Definition: switchtec.c:325
int device_id
Device ID.
Definition: switchtec.h:170
Port status structure.
Definition: switchtec.h:153
char * class_devices
Comma seperated list of classes.
Definition: switchtec.h:171
uint64_t global
Bitmap of global events.
Definition: switchtec.h:251
unsigned pff[SWITCHTEC_MAX_PFF_CSR]
Bitmap of events in each port function.
Definition: switchtec.h:259
unsigned part[SWITCHTEC_MAX_PARTS]
Bitmap of events in each partition.
Definition: switchtec.h:256
char pci_dev[256]
PCI BDF string.
Definition: switchtec.h:127
struct switchtec_dev * switchtec_open_uart(int fd)
Open a switchtec device behind a uart device.
struct switchtec_dev * switchtec_open_by_path(const char *path)
Open a switchtec device by path.
char fw_version[32]
Firmware version.
Definition: switchtec.h:130
size_t part_len
Length of the partition.
Definition: switchtec.h:219
int vendor_id
Vendor ID.
Definition: switchtec.h:169
char name[256]
Device name, eg. switchtec0.
Definition: switchtec.h:125
struct switchtec_dev * switchtec_open(const char *device)
Open a Switchtec device by string.
Definition: switchtec.c:188
char * pci_bdf
PCI BDF of the port.
Definition: switchtec.h:165
Represents a Switchtec device in the switchtec_list() function.
Definition: switchtec.h:124
struct switchtec_dev * switchtec_open_by_index(int index)
Open a switchtec device by index.
struct switchtec_dev * switchtec_open_by_pci_addr(int domain, int bus, int device, int func)
Open a switchtec device by PCI address (BDF)
unsigned local_part
Bitmap of events in the local partition.
Definition: switchtec.h:253