libxcoder  5.5.0
ni_quadraprobe.c
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stddef.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <dirent.h>
12 #include <sys/mman.h>
13 #include <stdbool.h>
14 
15 #define LOG_SIZE (1024 * 1024)
16 #define NUM_CORES 5
17 #define PCI_SYSFS_PATH "/sys/bus/pci/devices"
18 #define QUADRA_VENDOR_ID 0x1d82
19 #define QUADRA_DEVICE_ID 0x0401
20 
21 #if defined(__linux__)
22 
23 const char *const core_names[NUM_CORES] = {"np", "dp", "ep", "tp", "fp"};
24 
25 typedef struct {
26  char sysfs_path[256];
27  int domain;
28  int bus;
29  int slot;
30  int bar4_fd;
31  unsigned char *bar4_base;
32  off_t barsize;
33  uint32_t core_log_offsets[NUM_CORES];
34  int log_size;
35 } QuadraDevice;
36 
37 int bdf_to_sysfs(const char* bdf, char* sysfs_path, size_t pathsz, int *domain, int *bus, int *slot)
38 {
39  unsigned d=0, b=0, s=0, f=0;
40  if (sscanf(bdf, "%x:%x:%x.%x", &d, &b, &s, &f) != 4)
41  return -1;
42  if (snprintf(sysfs_path, pathsz, "/sys/bus/pci/devices/%04x:%02x:%02x.%x", d, b, s, f) < 0)
43  return -1;
44  if (domain) *domain = (int)d;
45  if (bus) *bus = (int)b;
46  if (slot) *slot = (int)s;
47  return 0;
48 }
49 
50 static int safe_realloc_devices(QuadraDevice **p_devs, int new_cap)
51 {
52  QuadraDevice *new_devs = realloc(*p_devs, new_cap * sizeof(**p_devs));
53  if (!new_devs)
54  {
55  free(*p_devs);
56  *p_devs = NULL;
57  return -1;
58  }
59  *p_devs = new_devs;
60  return 0;
61 }
62 
63 int quadra_device_find(QuadraDevice **out_devs)
64 {
65  DIR *dir = opendir(PCI_SYSFS_PATH);
66  if (!dir)
67  {
68  perror("opendir(sysfs)");
69  return -1;
70  }
71 
72  struct dirent *entry;
73  QuadraDevice *devs = calloc(8, sizeof(*devs));
74  int dev_count = 0, capacity = 8;
75  if (!devs)
76  {
77  closedir(dir);
78  return -1;
79  }
80 
81  char path[PATH_MAX];
82  while ((entry = readdir(dir)))
83  {
84  if (entry->d_name[0] == '.')
85  continue;
86 
87  if (snprintf(path, sizeof(path), "%s/%s", PCI_SYSFS_PATH, entry->d_name) < 0)
88  continue;
89 
90  struct stat st;
91  if (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))
92  continue;
93 
94  char ven_path[PATH_MAX], dev_path[PATH_MAX];
95  if (snprintf(ven_path, sizeof(ven_path), "%s/vendor", path) < 0)
96  continue;
97  if (snprintf(dev_path, sizeof(dev_path), "%s/device", path) < 0)
98  continue;
99 
100  FILE *fv = fopen(ven_path, "r");
101  if (!fv)
102  continue;
103 
104  char buf[32];
105  if (!fgets(buf, sizeof(buf), fv))
106  {
107  fclose(fv);
108  continue;
109  }
110  fclose(fv);
111 
112  unsigned long ven = strtoul(buf, NULL, 0);
113  if (ven != QUADRA_VENDOR_ID)
114  continue;
115 
116  /* Read device */
117  FILE *fd = fopen(dev_path, "r");
118  if (!fd)
119  continue;
120 
121  if (!fgets(buf, sizeof(buf), fd))
122  {
123  fclose(fd);
124  continue;
125  }
126  fclose(fd);
127 
128  unsigned long dev = strtoul(buf, NULL, 0);
129  if (dev != QUADRA_DEVICE_ID)
130  continue;
131 
132  if (dev_count >= capacity)
133  {
134  capacity *= 2;
135  if (safe_realloc_devices(&devs, capacity) < 0)
136  {
137  closedir(dir);
138  return -1;
139  }
140  }
141 
142  if (bdf_to_sysfs(entry->d_name,
143  devs[dev_count].sysfs_path, sizeof(devs[dev_count].sysfs_path),
144  &devs[dev_count].domain, &devs[dev_count].bus, &devs[dev_count].slot) == 0)
145  {
146  dev_count++;
147  }
148  }
149 
150  closedir(dir);
151 
152  if (dev_count == 0)
153  {
154  free(devs);
155  return 0;
156  }
157 
158  *out_devs = devs;
159  return dev_count;
160 }
161 
162 int quadra_init_mmap(QuadraDevice *qd)
163 {
164  char bar4path[PATH_MAX];
165  struct stat bar4stat;
166  if (snprintf(bar4path, sizeof(bar4path), "%s/resource4", qd->sysfs_path) < 0)
167  return -1;
168  if (geteuid() != 0)
169  {
170  (void)fprintf(stderr, "Permission error: You must run as root (sudo)\n");
171  return -1;
172  }
173  qd->bar4_fd = open(bar4path, O_RDWR | O_SYNC);
174  if (qd->bar4_fd == -1)
175  {
176  perror("open(bar4)");
177  return -1;
178  }
179  if (fstat(qd->bar4_fd, &bar4stat) == 0)
180  {
181  qd->barsize = bar4stat.st_size;
182  }
183  qd->bar4_base = mmap(NULL, qd->barsize, PROT_READ | PROT_WRITE, MAP_SHARED, qd->bar4_fd, 0);
184  if (qd->bar4_base == MAP_FAILED)
185  {
186  if (errno == EACCES)
187  (void)fprintf(stderr, "Permission error: Cannot mmap %s. Run as root (sudo).\n", bar4path);
188  else if (errno == EINVAL)
189  (void)fprintf(stderr, "EINVAL: mmap() on BAR4 failed. Try kernel flag 'iomem=relaxed'.\n");
190  else
191  (void)fprintf(stderr, "Failed to mmap BAR4 at %s: %s\n", bar4path, strerror(errno));
192  close(qd->bar4_fd);
193  qd->bar4_fd = -1;
194  return -1;
195  }
196  return 0;
197 }
198 
199 void fill_core_log_offsets(QuadraDevice *qd)
200 {
201  uint8_t *base = qd->bar4_base;
202  off_t offset = 0xf8f5000;
203  for (int i = 0; i < NUM_CORES; ++i)
204  {
205  qd->core_log_offsets[i] = *(uint32_t *)(base + offset + 4 * (size_t)i) & 0x0FFFFFFF;
206  }
207  qd->log_size = 1024 * 1024;
208 }
209 
210 void fill_core_reset_log_offsets(QuadraDevice *qd)
211 {
212  uint8_t *base = qd->bar4_base;
213  off_t offset = 0xf8fd0c0;
214  for (int i = 0; i < NUM_CORES; ++i)
215  {
216  qd->core_log_offsets[i] = *(uint32_t *)(base + offset + 4 * (size_t)i) & 0x0FFFFFFF;
217  }
218  qd->log_size = 64 * 1024;
219 }
220 
221 int dump_raw_logs(QuadraDevice *qd, const char *outdir, bool core_reset_log)
222 {
223  if (core_reset_log)
224  {
225  fill_core_reset_log_offsets(qd);
226  }
227  else
228  {
229  fill_core_log_offsets(qd);
230  }
231  int rc = 0;
232  for (int core_idx = 0; core_idx < NUM_CORES; ++core_idx)
233  {
234  const char *core = core_names[core_idx];
235  uint32_t log_offset = qd->core_log_offsets[core_idx];
236  char fname[PATH_MAX];
237  if (snprintf(fname, sizeof(fname), "%s/raw_%s_slot_%02x_%04x.bin",
238  outdir, core, qd->bus, qd->domain) < 0) continue;
239  FILE *fp = fopen(fname, "wb");
240  if (!fp)
241  {
242  perror("fopen");
243  rc = -1;
244  continue;
245  }
246  if (fwrite(qd->bar4_base + log_offset, 1, (size_t)qd->log_size, fp) != (size_t)qd->log_size)
247  {
248  (void)fprintf(stderr, "Short write on %s\n", fname);
249  rc = -1;
250  }
251  (void)fclose(fp);
252  chmod(fname, 0664);
253  if (memcmp(qd->bar4_base + log_offset, "hashid", 6) == 0)
254  {
255  char hstr[17] = {0};
256  memcpy(hstr, qd->bar4_base + log_offset, 16);
257  (void)fprintf(stderr, "%s: %s\n", core, hstr);
258  }
259  (void)fprintf(stderr, "[INFO] Dumped log: %s\n", fname);
260  }
261  return rc;
262 }
263 
264 int ni_rsrc_log_dump(const char *outdir, bool core_reset_log)
265 {
266  QuadraDevice *devs = NULL;
267  int ndev = quadra_device_find(&devs);
268  if (ndev <= 0)
269  {
270  free(devs);
271  (void)fprintf(stderr, "[WARN] No Quadra devices found in sysfs (vendor 1d82).\n");
272  return 1;
273  }
274  int fail_count = 0;
275  for (int i = 0; i < ndev; ++i)
276  {
277  if (quadra_init_mmap(&devs[i]) == 0)
278  {
279  int rc = dump_raw_logs(&devs[i], outdir ? outdir : ".", core_reset_log);
280  fail_count += (rc != 0);
281  }
282  else
283  {
284  (void)fprintf(stderr, "[ERROR] Failed to mmap device %d\n", i);
285  fail_count++;
286  }
287  }
288  free(devs);
289  return (fail_count == 0) ? 0 : 2;
290 }
291 #endif
PCI_SYSFS_PATH
#define PCI_SYSFS_PATH
Definition: ni_quadraprobe.c:17
NUM_CORES
#define NUM_CORES
Definition: ni_quadraprobe.c:16
QUADRA_VENDOR_ID
#define QUADRA_VENDOR_ID
Definition: ni_quadraprobe.c:18
QUADRA_DEVICE_ID
#define QUADRA_DEVICE_ID
Definition: ni_quadraprobe.c:19