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/stat.h>
  17#include <sys/wait.h>
  18#include <sys/time.h>
  19#include <sys/resource.h>
  20#include <asm/unistd.h>
  21#include <init.h>
  22#include <os.h>
  23#include <mem_user.h>
  24#include <ptrace_user.h>
  25#include <registers.h>
  26#include <skas.h>
  27
  28static void 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        if (change_sig(SIGWINCH, 0) < 0 ||
  36            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
  64        exit(ret);
  65}
  66
  67static void fatal_perror(const char *str)
  68{
  69        perror(str);
  70        exit(1);
  71}
  72
  73static void fatal(char *fmt, ...)
  74{
  75        va_list list;
  76
  77        va_start(list, fmt);
  78        vfprintf(stderr, fmt, list);
  79        va_end(list);
  80
  81        exit(1);
  82}
  83
  84static void non_fatal(char *fmt, ...)
  85{
  86        va_list list;
  87
  88        va_start(list, fmt);
  89        vfprintf(stderr, fmt, list);
  90        va_end(list);
  91}
  92
  93static int start_ptraced_child(void)
  94{
  95        int pid, n, status;
  96
  97        pid = fork();
  98        if (pid == 0)
  99                ptrace_child();
 100        else if (pid < 0)
 101                fatal_perror("start_ptraced_child : fork failed");
 102
 103        CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 104        if (n < 0)
 105                fatal_perror("check_ptrace : waitpid failed");
 106        if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
 107                fatal("check_ptrace : expected SIGSTOP, got status = %d",
 108                      status);
 109
 110        return pid;
 111}
 112
 113/* When testing for SYSEMU support, if it is one of the broken versions, we
 114 * must just avoid using sysemu, not panic, but only if SYSEMU features are
 115 * broken.
 116 * So only for SYSEMU features we test mustpanic, while normal host features
 117 * must work anyway!
 118 */
 119static int stop_ptraced_child(int pid, int exitcode, int mustexit)
 120{
 121        int status, n, ret = 0;
 122
 123        if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
 124                perror("stop_ptraced_child : ptrace failed");
 125                return -1;
 126        }
 127        CATCH_EINTR(n = waitpid(pid, &status, 0));
 128        if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
 129                int exit_with = WEXITSTATUS(status);
 130                if (exit_with == 2)
 131                        non_fatal("check_ptrace : child exited with status 2. "
 132                                  "\nDisabling SYSEMU support.\n");
 133                non_fatal("check_ptrace : child exited with exitcode %d, while "
 134                          "expecting %d; status 0x%x\n", exit_with,
 135                          exitcode, status);
 136                if (mustexit)
 137                        exit(1);
 138                ret = -1;
 139        }
 140
 141        return ret;
 142}
 143
 144/* Changed only during early boot */
 145static int force_sysemu_disabled = 0;
 146
 147static int __init nosysemu_cmd_param(char *str, int* add)
 148{
 149        force_sysemu_disabled = 1;
 150        return 0;
 151}
 152
 153__uml_setup("nosysemu", nosysemu_cmd_param,
 154"nosysemu\n"
 155"    Turns off syscall emulation patch for ptrace (SYSEMU) on.\n"
 156"    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
 157"    behaviour of ptrace() and helps reducing host context switch rate.\n"
 158"    To make it working, you need a kernel patch for your host, too.\n"
 159"    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
 160"    information.\n\n");
 161
 162static void __init check_sysemu(void)
 163{
 164        unsigned long regs[MAX_REG_NR];
 165        int pid, n, status, count=0;
 166
 167        non_fatal("Checking syscall emulation patch for ptrace...");
 168        sysemu_supported = 0;
 169        pid = start_ptraced_child();
 170
 171        if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
 172                goto fail;
 173
 174        CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 175        if (n < 0)
 176                fatal_perror("check_sysemu : wait failed");
 177        if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
 178                fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
 179                      status);
 180
 181        if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
 182                fatal_perror("check_sysemu : PTRACE_GETREGS failed");
 183        if (PT_SYSCALL_NR(regs) != __NR_getpid) {
 184                non_fatal("check_sysemu got system call number %d, "
 185                          "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
 186                goto fail;
 187        }
 188
 189        n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
 190        if (n < 0) {
 191                non_fatal("check_sysemu : failed to modify system call "
 192                          "return");
 193                goto fail;
 194        }
 195
 196        if (stop_ptraced_child(pid, 0, 0) < 0)
 197                goto fail_stopped;
 198
 199        sysemu_supported = 1;
 200        non_fatal("OK\n");
 201        set_using_sysemu(!force_sysemu_disabled);
 202
 203        non_fatal("Checking advanced syscall emulation patch for ptrace...");
 204        pid = start_ptraced_child();
 205
 206        if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
 207                   (void *) PTRACE_O_TRACESYSGOOD) < 0))
 208                fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
 209
 210        while (1) {
 211                count++;
 212                if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
 213                        goto fail;
 214                CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 215                if (n < 0)
 216                        fatal_perror("check_sysemu: wait failed");
 217
 218                if (WIFSTOPPED(status) &&
 219                    (WSTOPSIG(status) == (SIGTRAP|0x80))) {
 220                        if (!count) {
 221                                non_fatal("check_sysemu: SYSEMU_SINGLESTEP "
 222                                          "doesn't singlestep");
 223                                goto fail;
 224                        }
 225                        n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
 226                                   os_getpid());
 227                        if (n < 0)
 228                                fatal_perror("check_sysemu : failed to modify "
 229                                             "system call return");
 230                        break;
 231                }
 232                else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
 233                        count++;
 234                else {
 235                        non_fatal("check_sysemu: expected SIGTRAP or "
 236                                  "(SIGTRAP | 0x80), got status = %d\n",
 237                                  status);
 238                        goto fail;
 239                }
 240        }
 241        if (stop_ptraced_child(pid, 0, 0) < 0)
 242                goto fail_stopped;
 243
 244        sysemu_supported = 2;
 245        non_fatal("OK\n");
 246
 247        if (!force_sysemu_disabled)
 248                set_using_sysemu(sysemu_supported);
 249        return;
 250
 251fail:
 252        stop_ptraced_child(pid, 1, 0);
 253fail_stopped:
 254        non_fatal("missing\n");
 255}
 256
 257static void __init check_ptrace(void)
 258{
 259        int pid, syscall, n, status;
 260
 261        non_fatal("Checking that ptrace can change system call numbers...");
 262        pid = start_ptraced_child();
 263
 264        if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
 265                   (void *) PTRACE_O_TRACESYSGOOD) < 0))
 266                fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
 267
 268        while (1) {
 269                if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
 270                        fatal_perror("check_ptrace : ptrace failed");
 271
 272                CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
 273                if (n < 0)
 274                        fatal_perror("check_ptrace : wait failed");
 275
 276                if (!WIFSTOPPED(status) ||
 277                   (WSTOPSIG(status) != (SIGTRAP | 0x80)))
 278                        fatal("check_ptrace : expected (SIGTRAP|0x80), "
 279                               "got status = %d", status);
 280
 281                syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
 282                                 0);
 283                if (syscall == __NR_getpid) {
 284                        n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
 285                                   __NR_getppid);
 286                        if (n < 0)
 287                                fatal_perror("check_ptrace : failed to modify "
 288                                             "system call");
 289                        break;
 290                }
 291        }
 292        stop_ptraced_child(pid, 0, 1);
 293        non_fatal("OK\n");
 294        check_sysemu();
 295}
 296
 297extern void check_tmpexec(void);
 298
 299static void __init check_coredump_limit(void)
 300{
 301        struct rlimit lim;
 302        int err = getrlimit(RLIMIT_CORE, &lim);
 303
 304        if (err) {
 305                perror("Getting core dump limit");
 306                return;
 307        }
 308
 309        printf("Core dump limits :\n\tsoft - ");
 310        if (lim.rlim_cur == RLIM_INFINITY)
 311                printf("NONE\n");
 312        else printf("%lu\n", lim.rlim_cur);
 313
 314        printf("\thard - ");
 315        if (lim.rlim_max == RLIM_INFINITY)
 316                printf("NONE\n");
 317        else printf("%lu\n", lim.rlim_max);
 318}
 319
 320void __init os_early_checks(void)
 321{
 322        int pid;
 323
 324        /* Print out the core dump limits early */
 325        check_coredump_limit();
 326
 327        check_ptrace();
 328
 329        /* Need to check this early because mmapping happens before the
 330         * kernel is running.
 331         */
 332        check_tmpexec();
 333
 334        pid = start_ptraced_child();
 335        if (init_registers(pid))
 336                fatal("Failed to initialize default registers");
 337        stop_ptraced_child(pid, 1, 1);
 338}
 339
 340int __init parse_iomem(char *str, int *add)
 341{
 342        struct iomem_region *new;
 343        struct stat64 buf;
 344        char *file, *driver;
 345        int fd, size;
 346
 347        driver = str;
 348        file = strchr(str,',');
 349        if (file == NULL) {
 350                fprintf(stderr, "parse_iomem : failed to parse iomem\n");
 351                goto out;
 352        }
 353        *file = '\0';
 354        file++;
 355        fd = open(file, O_RDWR, 0);
 356        if (fd < 0) {
 357                perror("parse_iomem - Couldn't open io file");
 358                goto out;
 359        }
 360
 361        if (fstat64(fd, &buf) < 0) {
 362                perror("parse_iomem - cannot stat_fd file");
 363                goto out_close;
 364        }
 365
 366        new = malloc(sizeof(*new));
 367        if (new == NULL) {
 368                perror("Couldn't allocate iomem_region struct");
 369                goto out_close;
 370        }
 371
 372        size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
 373
 374        *new = ((struct iomem_region) { .next           = iomem_regions,
 375                                        .driver         = driver,
 376                                        .fd             = fd,
 377                                        .size           = size,
 378                                        .phys           = 0,
 379                                        .virt           = 0 });
 380        iomem_regions = new;
 381        iomem_size += new->size + UM_KERN_PAGE_SIZE;
 382
 383        return 0;
 384 out_close:
 385        close(fd);
 386 out:
 387        return 1;
 388}
 389