mpx-dig.c 12 KB

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