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