linux/arch/um/os-Linux/start_up.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   3 * Licensed under the GPL
   4 */
   5
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <stdarg.h>
   9#include <unistd.h>
  10#include <errno.h>
  11#include <fcntl.h>
  12#include <sched.h>
  13#include <signal.h>
  14#include <string.h>
  15#include <sys/mman.h>
  16#include <sys/ptrace.h>
  17#include <sys/stat.h>
  18#include <sys/wait.h>
  19#include <asm/unistd.h>
  20#include "init.h"
  21#include "kern_constants.h"
  22#include "os.h"
  23#include "mem_user.h"
  24#include "ptrace_user.h"
  25#include "registers.h"
  26#include "skas_ptrace.h"
  27
  28static int ptrace_child(void)
  29{
  30        int ret;
  31        /* Calling os_getpid because some libcs cached getpid incorrectly */
  32        int pid = os_getpid(), ppid = getppid();
  33        int sc_result;
  34
  35        change_sig(SIGWINCH, 0);
  36        if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
  37                perror("ptrace");
  38                kill(pid, SIGKILL);
  39        }
  40        kill(pid, SIGSTOP);
  41
  42        /*
  43         * This syscall will be intercepted by the parent. Don't call more than
  44         * once, please.
  45         */
  46        sc_result = os_getpid();
  47
  48        if (sc_result == pid)
  49                /* Nothing modified by the parent, we are running normally. */
  50                ret = 1;
  51        else if (sc_result == ppid)
  52                /*
  53                 * Expected in check_ptrace and check_sysemu when they succeed
  54                 * in modifying the stack frame
  55                 */
  56                ret = 0;
  57        else
  58                /* Serious trouble! This could be caused by a bug in host 2.6
  59                 * SKAS3/2.6 patch before release -V6, together with a bug in
  60                 * the UML code itself.
  61                 */
  62                ret = 2;
  63        _exit(ret);
  64}
  65
  66static void fatal_perror(char *str)
  67{
  68        perror(str);
  69        exit(1);
  70}
  71
  72static void fatal(char *fmt, ...)
  73{
  74        va_list list;
  75
  76        va_start(list, fmt);
  77        vprintf(fmt, list);
  78        va_end(list);
  79        fflush(stdout);
  80
  81        exit(1);
  82}
  83
  84static void non_fatal(char *fmt, ...)
  85{
  86        va_list list;
  87
  88        va_start(list, fmt);
  89        vprintf(fmt, list);
  90        va_end(list);
  91        fflush(stdout);
  92}
  93
  94static int start_ptraced_child(void)
  95{
  96        int pid, n, status;
  97
  98        pid = fork();
  99        if (pid == 0)
 100                ptrace_child();
 101        else if (pid < 0)
 102                fatal_perror("start_ptraced_child : fork failed");
 103
 104        CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 105        if (n < 0)
 106                fatal_perror("check_ptrace : waitpid failed");
 107        if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
 108                fatal("check_ptrace : expected SIGSTOP, got status = %d",
 109                      status);
 110
 111        return pid;
 112}
 113
 114/* When testing for SYSEMU support, if it is one of the broken versions, we
 115 * must just avoid using sysemu, not panic, but only if SYSEMU features are
 116 * broken.
 117 * So only for SYSEMU features we test mustpanic, while normal host features
 118 * must work anyway!
 119 */
 120static int stop_ptraced_child(int pid, int exitcode, int mustexit)
 121{
 122        int status, n, ret = 0;
 123
 124        if (ptrace(PTRACE_CONT, pid, 0, 0) < 0)
 125                fatal_perror("stop_ptraced_child : ptrace failed");
 126        CATCH_EINTR(n = waitpid(pid, &status, 0));
 127        if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
 128                int exit_with = WEXITSTATUS(status);
 129                if (exit_with == 2)
 130                        non_fatal("check_ptrace : child exited with status 2. "
 131                                  "\nDisabling SYSEMU support.\n");
 132                non_fatal("check_ptrace : child exited with exitcode %d, while "
 133                          "expecting %d; status 0x%x\n", exit_with,
 134                          exitcode, status);
 135                if (mustexit)
 136                        exit(1);
 137                ret = -1;
 138        }
 139
 140        return ret;
 141}
 142
 143/* Changed only during early boot */
 144int ptrace_faultinfo = 1;
 145int ptrace_ldt = 1;
 146int proc_mm = 1;
 147int skas_needs_stub = 0;
 148
 149static int __init skas0_cmd_param(char *str, int* add)
 150{
 151        ptrace_faultinfo = proc_mm = 0;
 152        return 0;
 153}
 154
 155/* The two __uml_setup would conflict, without this stupid alias. */
 156
 157static int __init mode_skas0_cmd_param(char *str, int* add)
 158        __attribute__((alias("skas0_cmd_param")));
 159
 160__uml_setup("skas0", skas0_cmd_param,
 161                "skas0\n"
 162                "    Disables SKAS3 usage, so that SKAS0 is used, unless \n"
 163                "    you specify mode=tt.\n\n");
 164
 165__uml_setup("mode=skas0", mode_skas0_cmd_param,
 166                "mode=skas0\n"
 167                "    Disables SKAS3 usage, so that SKAS0 is used, unless you \n"
 168                "    specify mode=tt. Note that this was recently added - on \n"
 169                "    older kernels you must use simply \"skas0\".\n\n");
 170
 171/* Changed only during early boot */
 172static int force_sysemu_disabled = 0;
 173
 174static int __init nosysemu_cmd_param(char *str, int* add)
 175{
 176        force_sysemu_disabled = 1;
 177        return 0;
 178}
 179
 180__uml_setup("nosysemu", nosysemu_cmd_param,
 181"nosysemu\n"
 182"    Turns off syscall emulation patch for ptrace (SYSEMU) on.\n"
 183"    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
 184"    behaviour of ptrace() and helps reducing host context switch rate.\n"
 185"    To make it working, you need a kernel patch for your host, too.\n"
 186"    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
 187"    information.\n\n");
 188
 189static void __init check_sysemu(void)
 190{
 191        unsigned long regs[MAX_REG_NR];
 192        int pid, n, status, count=0;
 193
 194        non_fatal("Checking syscall emulation patch for ptrace...");
 195        sysemu_supported = 0;
 196        pid = start_ptraced_child();
 197
 198        if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
 199                goto fail;
 200
 201        CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 202        if (n < 0)
 203                fatal_perror("check_sysemu : wait failed");
 204        if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
 205                fatal("check_sysemu : expected SIGTRAP, got status = %d",
 206                      status);
 207
 208        if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
 209                fatal_perror("check_sysemu : PTRACE_GETREGS failed");
 210        if (PT_SYSCALL_NR(regs) != __NR_getpid) {
 211                non_fatal("check_sysemu got system call number %d, "
 212                          "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
 213                goto fail;
 214        }
 215
 216        n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
 217        if (n < 0) {
 218                non_fatal("check_sysemu : failed to modify system call "
 219                          "return");
 220                goto fail;
 221        }
 222
 223        if (stop_ptraced_child(pid, 0, 0) < 0)
 224                goto fail_stopped;
 225
 226        sysemu_supported = 1;
 227        non_fatal("OK\n");
 228        set_using_sysemu(!force_sysemu_disabled);
 229
 230        non_fatal("Checking advanced syscall emulation patch for ptrace...");
 231        pid = start_ptraced_child();
 232
 233        if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
 234                   (void *) PTRACE_O_TRACESYSGOOD) < 0))
 235                fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
 236
 237        while (1) {
 238                count++;
 239                if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
 240                        goto fail;
 241                CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 242                if (n < 0)
 243                        fatal_perror("check_ptrace : wait failed");
 244
 245                if (WIFSTOPPED(status) &&
 246                    (WSTOPSIG(status) == (SIGTRAP|0x80))) {
 247                        if (!count)
 248                                fatal("check_ptrace : SYSEMU_SINGLESTEP "
 249                                      "doesn't singlestep");
 250                        n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_RET_OFFSET,
 251                                   os_getpid());
 252                        if (n < 0)
 253                                fatal_perror("check_sysemu : failed to modify "
 254                                             "system call return");
 255                        break;
 256                }
 257                else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
 258                        count++;
 259                else
 260                        fatal("check_ptrace : expected SIGTRAP or "
 261                              "(SIGTRAP | 0x80), got status = %d", status);
 262        }
 263        if (stop_ptraced_child(pid, 0, 0) < 0)
 264                goto fail_stopped;
 265
 266        sysemu_supported = 2;
 267        non_fatal("OK\n");
 268
 269        if (!force_sysemu_disabled)
 270                set_using_sysemu(sysemu_supported);
 271        return;
 272
 273fail:
 274        stop_ptraced_child(pid, 1, 0);
 275fail_stopped:
 276        non_fatal("missing\n");
 277}
 278
 279static void __init check_ptrace(void)
 280{
 281        int pid, syscall, n, status;
 282
 283        non_fatal("Checking that ptrace can change system call numbers...");
 284        pid = start_ptraced_child();
 285
 286        if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
 287                   (void *) PTRACE_O_TRACESYSGOOD) < 0))
 288                fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
 289
 290        while (1) {
 291                if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
 292                        fatal_perror("check_ptrace : ptrace failed");
 293
 294                CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 295                if (n < 0)
 296                        fatal_perror("check_ptrace : wait failed");
 297
 298                if (!WIFSTOPPED(status) ||
 299                   (WSTOPSIG(status) != (SIGTRAP | 0x80)))
 300                        fatal("check_ptrace : expected (SIGTRAP|0x80), "
 301                               "got status = %d", status);
 302
 303                syscall = ptrace(PTRACE_PEEKUSR, pid, PT_SYSCALL_NR_OFFSET,
 304                                 0);
 305                if (syscall == __NR_getpid) {
 306                        n = ptrace(PTRACE_POKEUSR, pid, PT_SYSCALL_NR_OFFSET,
 307                                   __NR_getppid);
 308                        if (n < 0)
 309                                fatal_perror("check_ptrace : failed to modify "
 310                                             "system call");
 311                        break;
 312                }
 313        }
 314        stop_ptraced_child(pid, 0, 1);
 315        non_fatal("OK\n");
 316        check_sysemu();
 317}
 318
 319extern void check_tmpexec(void);
 320
 321static void __init check_coredump_limit(void)
 322{
 323        struct rlimit lim;
 324        int err = getrlimit(RLIMIT_CORE, &lim);
 325
 326        if (err) {
 327                perror("Getting core dump limit");
 328                return;
 329        }
 330
 331        printf("Core dump limits :\n\tsoft - ");
 332        if (lim.rlim_cur == RLIM_INFINITY)
 333                printf("NONE\n");
 334        else printf("%lu\n", lim.rlim_cur);
 335
 336        printf("\thard - ");
 337        if (lim.rlim_max == RLIM_INFINITY)
 338                printf("NONE\n");
 339        else printf("%lu\n", lim.rlim_max);
 340}
 341
 342void __init os_early_checks(void)
 343{
 344        /* Print out the core dump limits early */
 345        check_coredump_limit();
 346
 347        check_ptrace();
 348
 349        /* Need to check this early because mmapping happens before the
 350         * kernel is running.
 351         */
 352        check_tmpexec();
 353}
 354
 355static int __init noprocmm_cmd_param(char *str, int* add)
 356{
 357        proc_mm = 0;
 358        return 0;
 359}
 360
 361__uml_setup("noprocmm", noprocmm_cmd_param,
 362"noprocmm\n"
 363"    Turns off usage of /proc/mm, even if host supports it.\n"
 364"    To support /proc/mm, the host needs to be patched using\n"
 365"    the current skas3 patch.\n\n");
 366
 367static int __init noptracefaultinfo_cmd_param(char *str, int* add)
 368{
 369        ptrace_faultinfo = 0;
 370        return 0;
 371}
 372
 373__uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param,
 374"noptracefaultinfo\n"
 375"    Turns off usage of PTRACE_FAULTINFO, even if host supports\n"
 376"    it. To support PTRACE_FAULTINFO, the host needs to be patched\n"
 377"    using the current skas3 patch.\n\n");
 378
 379static int __init noptraceldt_cmd_param(char *str, int* add)
 380{
 381        ptrace_ldt = 0;
 382        return 0;
 383}
 384
 385__uml_setup("noptraceldt", noptraceldt_cmd_param,
 386"noptraceldt\n"
 387"    Turns off usage of PTRACE_LDT, even if host supports it.\n"
 388"    To support PTRACE_LDT, the host needs to be patched using\n"
 389"    the current skas3 patch.\n\n");
 390
 391static inline void check_skas3_ptrace_faultinfo(void)
 392{
 393        struct ptrace_faultinfo fi;
 394        int pid, n;
 395
 396        non_fatal("  - PTRACE_FAULTINFO...");
 397        pid = start_ptraced_child();
 398
 399        n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi);
 400        if (n < 0) {
 401                ptrace_faultinfo = 0;
 402                if (errno == EIO)
 403                        non_fatal("not found\n");
 404                else
 405                        perror("not found");
 406        }
 407        else {
 408                if (!ptrace_faultinfo)
 409                        non_fatal("found but disabled on command line\n");
 410                else
 411                        non_fatal("found\n");
 412        }
 413
 414        init_registers(pid);
 415        stop_ptraced_child(pid, 1, 1);
 416}
 417
 418static inline void check_skas3_ptrace_ldt(void)
 419{
 420#ifdef PTRACE_LDT
 421        int pid, n;
 422        unsigned char ldtbuf[40];
 423        struct ptrace_ldt ldt_op = (struct ptrace_ldt) {
 424                .func = 2, /* read default ldt */
 425                .ptr = ldtbuf,
 426                .bytecount = sizeof(ldtbuf)};
 427
 428        non_fatal("  - PTRACE_LDT...");
 429        pid = start_ptraced_child();
 430
 431        n = ptrace(PTRACE_LDT, pid, 0, (unsigned long) &ldt_op);
 432        if (n < 0) {
 433                if (errno == EIO)
 434                        non_fatal("not found\n");
 435                else {
 436                        perror("not found");
 437                }
 438                ptrace_ldt = 0;
 439        }
 440        else {
 441                if (ptrace_ldt)
 442                        non_fatal("found\n");
 443                else
 444                        non_fatal("found, but use is disabled\n");
 445        }
 446
 447        stop_ptraced_child(pid, 1, 1);
 448#else
 449        /* PTRACE_LDT might be disabled via cmdline option.
 450         * We want to override this, else we might use the stub
 451         * without real need
 452         */
 453        ptrace_ldt = 1;
 454#endif
 455}
 456
 457static inline void check_skas3_proc_mm(void)
 458{
 459        non_fatal("  - /proc/mm...");
 460        if (access("/proc/mm", W_OK) < 0) {
 461                proc_mm = 0;
 462                perror("not found");
 463        }
 464        else if (!proc_mm)
 465                non_fatal("found but disabled on command line\n");
 466        else non_fatal("found\n");
 467}
 468
 469int can_do_skas(void)
 470{
 471        non_fatal("Checking for the skas3 patch in the host:\n");
 472
 473        check_skas3_proc_mm();
 474        check_skas3_ptrace_faultinfo();
 475        check_skas3_ptrace_ldt();
 476
 477        if (!proc_mm || !ptrace_faultinfo || !ptrace_ldt)
 478                skas_needs_stub = 1;
 479
 480        return 1;
 481}
 482
 483int __init parse_iomem(char *str, int *add)
 484{
 485        struct iomem_region *new;
 486        struct stat64 buf;
 487        char *file, *driver;
 488        int fd, size;
 489
 490        driver = str;
 491        file = strchr(str,',');
 492        if (file == NULL) {
 493                printf("parse_iomem : failed to parse iomem\n");
 494                goto out;
 495        }
 496        *file = '\0';
 497        file++;
 498        fd = open(file, O_RDWR, 0);
 499        if (fd < 0) {
 500                perror("parse_iomem - Couldn't open io file");
 501                goto out;
 502        }
 503
 504        if (fstat64(fd, &buf) < 0) {
 505                perror("parse_iomem - cannot stat_fd file");
 506                goto out_close;
 507        }
 508
 509        new = malloc(sizeof(*new));
 510        if (new == NULL) {
 511                perror("Couldn't allocate iomem_region struct");
 512                goto out_close;
 513        }
 514
 515        size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
 516
 517        *new = ((struct iomem_region) { .next           = iomem_regions,
 518                                        .driver         = driver,
 519                                        .fd             = fd,
 520                                        .size           = size,
 521                                        .phys           = 0,
 522                                        .virt           = 0 });
 523        iomem_regions = new;
 524        iomem_size += new->size + UM_KERN_PAGE_SIZE;
 525
 526        return 0;
 527 out_close:
 528        close(fd);
 529 out:
 530        return 1;
 531}
 532