Linux-libre 4.19.8-gnu
[librecmc/linux-libre.git] / tools / testing / selftests / x86 / mpx-dig.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Written by Dave Hansen <dave.hansen@intel.com>
4  */
5
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <sys/mman.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include "mpx-debug.h"
18 #include "mpx-mm.h"
19 #include "mpx-hw.h"
20
21 unsigned long bounds_dir_global;
22
23 #define mpx_dig_abort() __mpx_dig_abort(__FILE__, __func__, __LINE__)
24 static void inline __mpx_dig_abort(const char *file, const char *func, int line)
25 {
26         fprintf(stderr, "MPX dig abort @ %s::%d in %s()\n", file, line, func);
27         printf("MPX dig abort @ %s::%d in %s()\n", file, line, func);
28         abort();
29 }
30
31 /*
32  * run like this (BDIR finds the probably bounds directory):
33  *
34  *      BDIR="$(cat /proc/$pid/smaps | grep -B1 2097152 \
35  *              | head -1 | awk -F- '{print $1}')";
36  *      ./mpx-dig $pid 0x$BDIR
37  *
38  * NOTE:
39  *      assumes that the only 2097152-kb VMA is the bounds dir
40  */
41
42 long nr_incore(void *ptr, unsigned long size_bytes)
43 {
44         int i;
45         long ret = 0;
46         long vec_len = size_bytes / PAGE_SIZE;
47         unsigned char *vec = malloc(vec_len);
48         int incore_ret;
49
50         if (!vec)
51                 mpx_dig_abort();
52
53         incore_ret = mincore(ptr, size_bytes, vec);
54         if (incore_ret) {
55                 printf("mincore ret: %d\n", incore_ret);
56                 perror("mincore");
57                 mpx_dig_abort();
58         }
59         for (i = 0; i < vec_len; i++)
60                 ret += vec[i];
61         free(vec);
62         return ret;
63 }
64
65 int open_proc(int pid, char *file)
66 {
67         static char buf[100];
68         int fd;
69
70         snprintf(&buf[0], sizeof(buf), "/proc/%d/%s", pid, file);
71         fd = open(&buf[0], O_RDONLY);
72         if (fd < 0)
73                 perror(buf);
74
75         return fd;
76 }
77
78 struct vaddr_range {
79         unsigned long start;
80         unsigned long end;
81 };
82 struct vaddr_range *ranges;
83 int nr_ranges_allocated;
84 int nr_ranges_populated;
85 int last_range = -1;
86
87 int __pid_load_vaddrs(int pid)
88 {
89         int ret = 0;
90         int proc_maps_fd = open_proc(pid, "maps");
91         char linebuf[10000];
92         unsigned long start;
93         unsigned long end;
94         char rest[1000];
95         FILE *f = fdopen(proc_maps_fd, "r");
96
97         if (!f)
98                 mpx_dig_abort();
99         nr_ranges_populated = 0;
100         while (!feof(f)) {
101                 char *readret = fgets(linebuf, sizeof(linebuf), f);
102                 int parsed;
103
104                 if (readret == NULL) {
105                         if (feof(f))
106                                 break;
107                         mpx_dig_abort();
108                 }
109
110                 parsed = sscanf(linebuf, "%lx-%lx%s", &start, &end, rest);
111                 if (parsed != 3)
112                         mpx_dig_abort();
113
114                 dprintf4("result[%d]: %lx-%lx<->%s\n", parsed, start, end, rest);
115                 if (nr_ranges_populated >= nr_ranges_allocated) {
116                         ret = -E2BIG;
117                         break;
118                 }
119                 ranges[nr_ranges_populated].start = start;
120                 ranges[nr_ranges_populated].end = end;
121                 nr_ranges_populated++;
122         }
123         last_range = -1;
124         fclose(f);
125         close(proc_maps_fd);
126         return ret;
127 }
128
129 int pid_load_vaddrs(int pid)
130 {
131         int ret;
132
133         dprintf2("%s(%d)\n", __func__, pid);
134         if (!ranges) {
135                 nr_ranges_allocated = 4;
136                 ranges = malloc(nr_ranges_allocated * sizeof(ranges[0]));
137                 dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__, pid,
138                          nr_ranges_allocated, ranges);
139                 assert(ranges != NULL);
140         }
141         do {
142                 ret = __pid_load_vaddrs(pid);
143                 if (!ret)
144                         break;
145                 if (ret == -E2BIG) {
146                         dprintf2("%s(%d) need to realloc\n", __func__, pid);
147                         nr_ranges_allocated *= 2;
148                         ranges = realloc(ranges,
149                                         nr_ranges_allocated * sizeof(ranges[0]));
150                         dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__,
151                                         pid, nr_ranges_allocated, ranges);
152                         assert(ranges != NULL);
153                         dprintf1("reallocating to hold %d ranges\n", nr_ranges_allocated);
154                 }
155         } while (1);
156
157         dprintf2("%s(%d) done\n", __func__, pid);
158
159         return ret;
160 }
161
162 static inline int vaddr_in_range(unsigned long vaddr, struct vaddr_range *r)
163 {
164         if (vaddr < r->start)
165                 return 0;
166         if (vaddr >= r->end)
167                 return 0;
168         return 1;
169 }
170
171 static inline int vaddr_mapped_by_range(unsigned long vaddr)
172 {
173         int i;
174
175         if (last_range > 0 && vaddr_in_range(vaddr, &ranges[last_range]))
176                 return 1;
177
178         for (i = 0; i < nr_ranges_populated; i++) {
179                 struct vaddr_range *r = &ranges[i];
180
181                 if (vaddr_in_range(vaddr, r))
182                         continue;
183                 last_range = i;
184                 return 1;
185         }
186         return 0;
187 }
188
189 const int bt_entry_size_bytes = sizeof(unsigned long) * 4;
190
191 void *read_bounds_table_into_buf(unsigned long table_vaddr)
192 {
193 #ifdef MPX_DIG_STANDALONE
194         static char bt_buf[MPX_BOUNDS_TABLE_SIZE_BYTES];
195         off_t seek_ret = lseek(fd, table_vaddr, SEEK_SET);
196         if (seek_ret != table_vaddr)
197                 mpx_dig_abort();
198
199         int read_ret = read(fd, &bt_buf, sizeof(bt_buf));
200         if (read_ret != sizeof(bt_buf))
201                 mpx_dig_abort();
202         return &bt_buf;
203 #else
204         return (void *)table_vaddr;
205 #endif
206 }
207
208 int dump_table(unsigned long table_vaddr, unsigned long base_controlled_vaddr,
209                 unsigned long bde_vaddr)
210 {
211         unsigned long offset_inside_bt;
212         int nr_entries = 0;
213         int do_abort = 0;
214         char *bt_buf;
215
216         dprintf3("%s() base_controlled_vaddr: 0x%012lx bde_vaddr: 0x%012lx\n",
217                         __func__, base_controlled_vaddr, bde_vaddr);
218
219         bt_buf = read_bounds_table_into_buf(table_vaddr);
220
221         dprintf4("%s() read done\n", __func__);
222
223         for (offset_inside_bt = 0;
224              offset_inside_bt < MPX_BOUNDS_TABLE_SIZE_BYTES;
225              offset_inside_bt += bt_entry_size_bytes) {
226                 unsigned long bt_entry_index;
227                 unsigned long bt_entry_controls;
228                 unsigned long this_bt_entry_for_vaddr;
229                 unsigned long *bt_entry_buf;
230                 int i;
231
232                 dprintf4("%s() offset_inside_bt: 0x%lx of 0x%llx\n", __func__,
233                         offset_inside_bt, MPX_BOUNDS_TABLE_SIZE_BYTES);
234                 bt_entry_buf = (void *)&bt_buf[offset_inside_bt];
235                 if (!bt_buf) {
236                         printf("null bt_buf\n");
237                         mpx_dig_abort();
238                 }
239                 if (!bt_entry_buf) {
240                         printf("null bt_entry_buf\n");
241                         mpx_dig_abort();
242                 }
243                 dprintf4("%s() reading *bt_entry_buf @ %p\n", __func__,
244                                 bt_entry_buf);
245                 if (!bt_entry_buf[0] &&
246                     !bt_entry_buf[1] &&
247                     !bt_entry_buf[2] &&
248                     !bt_entry_buf[3])
249                         continue;
250
251                 nr_entries++;
252
253                 bt_entry_index = offset_inside_bt/bt_entry_size_bytes;
254                 bt_entry_controls = sizeof(void *);
255                 this_bt_entry_for_vaddr =
256                         base_controlled_vaddr + bt_entry_index*bt_entry_controls;
257                 /*
258                  * We sign extend vaddr bits 48->63 which effectively
259                  * creates a hole in the virtual address space.
260                  * This calculation corrects for the hole.
261                  */
262                 if (this_bt_entry_for_vaddr > 0x00007fffffffffffUL)
263                         this_bt_entry_for_vaddr |= 0xffff800000000000;
264
265                 if (!vaddr_mapped_by_range(this_bt_entry_for_vaddr)) {
266                         printf("bt_entry_buf: %p\n", bt_entry_buf);
267                         printf("there is a bte for %lx but no mapping\n",
268                                         this_bt_entry_for_vaddr);
269                         printf("          bde   vaddr: %016lx\n", bde_vaddr);
270                         printf("base_controlled_vaddr: %016lx\n", base_controlled_vaddr);
271                         printf("          table_vaddr: %016lx\n", table_vaddr);
272                         printf("          entry vaddr: %016lx @ offset %lx\n",
273                                 table_vaddr + offset_inside_bt, offset_inside_bt);
274                         do_abort = 1;
275                         mpx_dig_abort();
276                 }
277                 if (DEBUG_LEVEL < 4)
278                         continue;
279
280                 printf("table entry[%lx]: ", offset_inside_bt);
281                 for (i = 0; i < bt_entry_size_bytes; i += sizeof(unsigned long))
282                         printf("0x%016lx ", bt_entry_buf[i]);
283                 printf("\n");
284         }
285         if (do_abort)
286                 mpx_dig_abort();
287         dprintf4("%s() done\n",  __func__);
288         return nr_entries;
289 }
290
291 int search_bd_buf(char *buf, int len_bytes, unsigned long bd_offset_bytes,
292                 int *nr_populated_bdes)
293 {
294         unsigned long i;
295         int total_entries = 0;
296
297         dprintf3("%s(%p, %x, %lx, ...) buf end: %p\n", __func__, buf,
298                         len_bytes, bd_offset_bytes, buf + len_bytes);
299
300         for (i = 0; i < len_bytes; i += sizeof(unsigned long)) {
301                 unsigned long bd_index = (bd_offset_bytes + i) / sizeof(unsigned long);
302                 unsigned long *bounds_dir_entry_ptr = (unsigned long *)&buf[i];
303                 unsigned long bounds_dir_entry;
304                 unsigned long bd_for_vaddr;
305                 unsigned long bt_start;
306                 unsigned long bt_tail;
307                 int nr_entries;
308
309                 dprintf4("%s() loop i: %ld bounds_dir_entry_ptr: %p\n", __func__, i,
310                                 bounds_dir_entry_ptr);
311
312                 bounds_dir_entry = *bounds_dir_entry_ptr;
313                 if (!bounds_dir_entry) {
314                         dprintf4("no bounds dir at index 0x%lx / 0x%lx "
315                                  "start at offset:%lx %lx\n", bd_index, bd_index,
316                                         bd_offset_bytes, i);
317                         continue;
318                 }
319                 dprintf3("found bounds_dir_entry: 0x%lx @ "
320                          "index 0x%lx buf ptr: %p\n", bounds_dir_entry, i,
321                                         &buf[i]);
322                 /* mask off the enable bit: */
323                 bounds_dir_entry &= ~0x1;
324                 (*nr_populated_bdes)++;
325                 dprintf4("nr_populated_bdes: %p\n", nr_populated_bdes);
326                 dprintf4("*nr_populated_bdes: %d\n", *nr_populated_bdes);
327
328                 bt_start = bounds_dir_entry;
329                 bt_tail = bounds_dir_entry + MPX_BOUNDS_TABLE_SIZE_BYTES - 1;
330                 if (!vaddr_mapped_by_range(bt_start)) {
331                         printf("bounds directory 0x%lx points to nowhere\n",
332                                         bounds_dir_entry);
333                         mpx_dig_abort();
334                 }
335                 if (!vaddr_mapped_by_range(bt_tail)) {
336                         printf("bounds directory end 0x%lx points to nowhere\n",
337                                         bt_tail);
338                         mpx_dig_abort();
339                 }
340                 /*
341                  * Each bounds directory entry controls 1MB of virtual address
342                  * space.  This variable is the virtual address in the process
343                  * of the beginning of the area controlled by this bounds_dir.
344                  */
345                 bd_for_vaddr = bd_index * (1UL<<20);
346
347                 nr_entries = dump_table(bounds_dir_entry, bd_for_vaddr,
348                                 bounds_dir_global+bd_offset_bytes+i);
349                 total_entries += nr_entries;
350                 dprintf5("dir entry[%4ld @ %p]: 0x%lx %6d entries "
351                          "total this buf: %7d bd_for_vaddrs: 0x%lx -> 0x%lx\n",
352                                 bd_index, buf+i,
353                                 bounds_dir_entry, nr_entries, total_entries,
354                                 bd_for_vaddr, bd_for_vaddr + (1UL<<20));
355         }
356         dprintf3("%s(%p, %x, %lx, ...) done\n", __func__, buf, len_bytes,
357                         bd_offset_bytes);
358         return total_entries;
359 }
360
361 int proc_pid_mem_fd = -1;
362
363 void *fill_bounds_dir_buf_other(long byte_offset_inside_bounds_dir,
364                            long buffer_size_bytes, void *buffer)
365 {
366         unsigned long seekto = bounds_dir_global + byte_offset_inside_bounds_dir;
367         int read_ret;
368         off_t seek_ret = lseek(proc_pid_mem_fd, seekto, SEEK_SET);
369
370         if (seek_ret != seekto)
371                 mpx_dig_abort();
372
373         read_ret = read(proc_pid_mem_fd, buffer, buffer_size_bytes);
374         /* there shouldn't practically be short reads of /proc/$pid/mem */
375         if (read_ret != buffer_size_bytes)
376                 mpx_dig_abort();
377
378         return buffer;
379 }
380 void *fill_bounds_dir_buf_self(long byte_offset_inside_bounds_dir,
381                            long buffer_size_bytes, void *buffer)
382
383 {
384         unsigned char vec[buffer_size_bytes / PAGE_SIZE];
385         char *dig_bounds_dir_ptr =
386                 (void *)(bounds_dir_global + byte_offset_inside_bounds_dir);
387         /*
388          * use mincore() to quickly find the areas of the bounds directory
389          * that have memory and thus will be worth scanning.
390          */
391         int incore_ret;
392
393         int incore = 0;
394         int i;
395
396         dprintf4("%s() dig_bounds_dir_ptr: %p\n", __func__, dig_bounds_dir_ptr);
397
398         incore_ret = mincore(dig_bounds_dir_ptr, buffer_size_bytes, &vec[0]);
399         if (incore_ret) {
400                 printf("mincore ret: %d\n", incore_ret);
401                 perror("mincore");
402                 mpx_dig_abort();
403         }
404         for (i = 0; i < sizeof(vec); i++)
405                 incore += vec[i];
406         dprintf4("%s() total incore: %d\n", __func__, incore);
407         if (!incore)
408                 return NULL;
409         dprintf3("%s() total incore: %d\n", __func__, incore);
410         return dig_bounds_dir_ptr;
411 }
412
413 int inspect_pid(int pid)
414 {
415         static int dig_nr;
416         long offset_inside_bounds_dir;
417         char bounds_dir_buf[sizeof(unsigned long) * (1UL << 15)];
418         char *dig_bounds_dir_ptr;
419         int total_entries = 0;
420         int nr_populated_bdes = 0;
421         int inspect_self;
422
423         if (getpid() == pid) {
424                 dprintf4("inspecting self\n");
425                 inspect_self = 1;
426         } else {
427                 dprintf4("inspecting pid %d\n", pid);
428                 mpx_dig_abort();
429         }
430
431         for (offset_inside_bounds_dir = 0;
432              offset_inside_bounds_dir < MPX_BOUNDS_TABLE_SIZE_BYTES;
433              offset_inside_bounds_dir += sizeof(bounds_dir_buf)) {
434                 static int bufs_skipped;
435                 int this_entries;
436
437                 if (inspect_self) {
438                         dig_bounds_dir_ptr =
439                                 fill_bounds_dir_buf_self(offset_inside_bounds_dir,
440                                                          sizeof(bounds_dir_buf),
441                                                          &bounds_dir_buf[0]);
442                 } else {
443                         dig_bounds_dir_ptr =
444                                 fill_bounds_dir_buf_other(offset_inside_bounds_dir,
445                                                           sizeof(bounds_dir_buf),
446                                                           &bounds_dir_buf[0]);
447                 }
448                 if (!dig_bounds_dir_ptr) {
449                         bufs_skipped++;
450                         continue;
451                 }
452                 this_entries = search_bd_buf(dig_bounds_dir_ptr,
453                                         sizeof(bounds_dir_buf),
454                                         offset_inside_bounds_dir,
455                                         &nr_populated_bdes);
456                 total_entries += this_entries;
457         }
458         printf("mpx dig (%3d) complete, SUCCESS (%8d / %4d)\n", ++dig_nr,
459                         total_entries, nr_populated_bdes);
460         return total_entries + nr_populated_bdes;
461 }
462
463 #ifdef MPX_DIG_REMOTE
464 int main(int argc, char **argv)
465 {
466         int err;
467         char *c;
468         unsigned long bounds_dir_entry;
469         int pid;
470
471         printf("mpx-dig starting...\n");
472         err = sscanf(argv[1], "%d", &pid);
473         printf("parsing: '%s', err: %d\n", argv[1], err);
474         if (err != 1)
475                 mpx_dig_abort();
476
477         err = sscanf(argv[2], "%lx", &bounds_dir_global);
478         printf("parsing: '%s': %d\n", argv[2], err);
479         if (err != 1)
480                 mpx_dig_abort();
481
482         proc_pid_mem_fd = open_proc(pid, "mem");
483         if (proc_pid_mem_fd < 0)
484                 mpx_dig_abort();
485
486         inspect_pid(pid);
487         return 0;
488 }
489 #endif
490
491 long inspect_me(struct mpx_bounds_dir *bounds_dir)
492 {
493         int pid = getpid();
494
495         pid_load_vaddrs(pid);
496         bounds_dir_global = (unsigned long)bounds_dir;
497         dprintf4("enter %s() bounds dir: %p\n", __func__, bounds_dir);
498         return inspect_pid(pid);
499 }