1 // SPDX-License-Identifier: GPL-2.0
3 * Written by Dave Hansen <dave.hansen@intel.com>
11 #include <sys/types.h>
17 #include "mpx-debug.h"
21 unsigned long bounds_dir_global
;
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
)
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
);
32 * run like this (BDIR finds the probably bounds directory):
34 * BDIR="$(cat /proc/$pid/smaps | grep -B1 2097152 \
35 * | head -1 | awk -F- '{print $1}')";
36 * ./mpx-dig $pid 0x$BDIR
39 * assumes that the only 2097152-kb VMA is the bounds dir
42 long nr_incore(void *ptr
, unsigned long size_bytes
)
46 long vec_len
= size_bytes
/ PAGE_SIZE
;
47 unsigned char *vec
= malloc(vec_len
);
53 incore_ret
= mincore(ptr
, size_bytes
, vec
);
55 printf("mincore ret: %d\n", incore_ret
);
59 for (i
= 0; i
< vec_len
; i
++)
65 int open_proc(int pid
, char *file
)
70 snprintf(&buf
[0], sizeof(buf
), "/proc/%d/%s", pid
, file
);
71 fd
= open(&buf
[0], O_RDONLY
);
82 struct vaddr_range
*ranges
;
83 int nr_ranges_allocated
;
84 int nr_ranges_populated
;
87 int __pid_load_vaddrs(int pid
)
90 int proc_maps_fd
= open_proc(pid
, "maps");
95 FILE *f
= fdopen(proc_maps_fd
, "r");
99 nr_ranges_populated
= 0;
101 char *readret
= fgets(linebuf
, sizeof(linebuf
), f
);
104 if (readret
== NULL
) {
110 parsed
= sscanf(linebuf
, "%lx-%lx%s", &start
, &end
, rest
);
114 dprintf4("result[%d]: %lx-%lx<->%s\n", parsed
, start
, end
, rest
);
115 if (nr_ranges_populated
>= nr_ranges_allocated
) {
119 ranges
[nr_ranges_populated
].start
= start
;
120 ranges
[nr_ranges_populated
].end
= end
;
121 nr_ranges_populated
++;
129 int pid_load_vaddrs(int pid
)
133 dprintf2("%s(%d)\n", __func__
, pid
);
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
);
142 ret
= __pid_load_vaddrs(pid
);
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
);
157 dprintf2("%s(%d) done\n", __func__
, pid
);
162 static inline int vaddr_in_range(unsigned long vaddr
, struct vaddr_range
*r
)
164 if (vaddr
< r
->start
)
171 static inline int vaddr_mapped_by_range(unsigned long vaddr
)
175 if (last_range
> 0 && vaddr_in_range(vaddr
, &ranges
[last_range
]))
178 for (i
= 0; i
< nr_ranges_populated
; i
++) {
179 struct vaddr_range
*r
= &ranges
[i
];
181 if (vaddr_in_range(vaddr
, r
))
189 const int bt_entry_size_bytes
= sizeof(unsigned long) * 4;
191 void *read_bounds_table_into_buf(unsigned long table_vaddr
)
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
)
199 int read_ret
= read(fd
, &bt_buf
, sizeof(bt_buf
));
200 if (read_ret
!= sizeof(bt_buf
))
204 return (void *)table_vaddr
;
208 int dump_table(unsigned long table_vaddr
, unsigned long base_controlled_vaddr
,
209 unsigned long bde_vaddr
)
211 unsigned long offset_inside_bt
;
216 dprintf3("%s() base_controlled_vaddr: 0x%012lx bde_vaddr: 0x%012lx\n",
217 __func__
, base_controlled_vaddr
, bde_vaddr
);
219 bt_buf
= read_bounds_table_into_buf(table_vaddr
);
221 dprintf4("%s() read done\n", __func__
);
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
;
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
];
236 printf("null bt_buf\n");
240 printf("null bt_entry_buf\n");
243 dprintf4("%s() reading *bt_entry_buf @ %p\n", __func__
,
245 if (!bt_entry_buf
[0] &&
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
;
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.
262 if (this_bt_entry_for_vaddr
> 0x00007fffffffffffUL
)
263 this_bt_entry_for_vaddr
|= 0xffff800000000000;
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
);
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
]);
287 dprintf4("%s() done\n", __func__
);
291 int search_bd_buf(char *buf
, int len_bytes
, unsigned long bd_offset_bytes
,
292 int *nr_populated_bdes
)
295 int total_entries
= 0;
297 dprintf3("%s(%p, %x, %lx, ...) buf end: %p\n", __func__
, buf
,
298 len_bytes
, bd_offset_bytes
, buf
+ len_bytes
);
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
;
309 dprintf4("%s() loop i: %ld bounds_dir_entry_ptr: %p\n", __func__
, i
,
310 bounds_dir_entry_ptr
);
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
,
319 dprintf3("found bounds_dir_entry: 0x%lx @ "
320 "index 0x%lx buf ptr: %p\n", bounds_dir_entry
, 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
);
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",
335 if (!vaddr_mapped_by_range(bt_tail
)) {
336 printf("bounds directory end 0x%lx points to nowhere\n",
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.
345 bd_for_vaddr
= bd_index
* (1UL<<20);
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",
353 bounds_dir_entry
, nr_entries
, total_entries
,
354 bd_for_vaddr
, bd_for_vaddr
+ (1UL<<20));
356 dprintf3("%s(%p, %x, %lx, ...) done\n", __func__
, buf
, len_bytes
,
358 return total_entries
;
361 int proc_pid_mem_fd
= -1;
363 void *fill_bounds_dir_buf_other(long byte_offset_inside_bounds_dir
,
364 long buffer_size_bytes
, void *buffer
)
366 unsigned long seekto
= bounds_dir_global
+ byte_offset_inside_bounds_dir
;
368 off_t seek_ret
= lseek(proc_pid_mem_fd
, seekto
, SEEK_SET
);
370 if (seek_ret
!= seekto
)
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
)
380 void *fill_bounds_dir_buf_self(long byte_offset_inside_bounds_dir
,
381 long buffer_size_bytes
, void *buffer
)
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
);
388 * use mincore() to quickly find the areas of the bounds directory
389 * that have memory and thus will be worth scanning.
396 dprintf4("%s() dig_bounds_dir_ptr: %p\n", __func__
, dig_bounds_dir_ptr
);
398 incore_ret
= mincore(dig_bounds_dir_ptr
, buffer_size_bytes
, &vec
[0]);
400 printf("mincore ret: %d\n", incore_ret
);
404 for (i
= 0; i
< sizeof(vec
); i
++)
406 dprintf4("%s() total incore: %d\n", __func__
, incore
);
409 dprintf3("%s() total incore: %d\n", __func__
, incore
);
410 return dig_bounds_dir_ptr
;
413 int inspect_pid(int pid
)
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;
423 if (getpid() == pid
) {
424 dprintf4("inspecting self\n");
427 dprintf4("inspecting pid %d\n", pid
);
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
;
439 fill_bounds_dir_buf_self(offset_inside_bounds_dir
,
440 sizeof(bounds_dir_buf
),
444 fill_bounds_dir_buf_other(offset_inside_bounds_dir
,
445 sizeof(bounds_dir_buf
),
448 if (!dig_bounds_dir_ptr
) {
452 this_entries
= search_bd_buf(dig_bounds_dir_ptr
,
453 sizeof(bounds_dir_buf
),
454 offset_inside_bounds_dir
,
456 total_entries
+= this_entries
;
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
;
463 #ifdef MPX_DIG_REMOTE
464 int main(int argc
, char **argv
)
468 unsigned long bounds_dir_entry
;
471 printf("mpx-dig starting...\n");
472 err
= sscanf(argv
[1], "%d", &pid
);
473 printf("parsing: '%s', err: %d\n", argv
[1], err
);
477 err
= sscanf(argv
[2], "%lx", &bounds_dir_global
);
478 printf("parsing: '%s': %d\n", argv
[2], err
);
482 proc_pid_mem_fd
= open_proc(pid
, "mem");
483 if (proc_pid_mem_fd
< 0)
491 long inspect_me(struct mpx_bounds_dir
*bounds_dir
)
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
);