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