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