linux/tools/testing/selftests/x86/mpx-dig.c
<<
>>
Prefs
   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}
 499