root/arch/um/os-Linux/start_up.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ptrace_child
  2. fatal_perror
  3. fatal
  4. non_fatal
  5. start_ptraced_child
  6. stop_ptraced_child
  7. nosysemu_cmd_param
  8. check_sysemu
  9. check_ptrace
  10. check_coredump_limit
  11. os_early_checks
  12. parse_iomem

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   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 
  28 static 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 
  67 static void fatal_perror(const char *str)
  68 {
  69         perror(str);
  70         exit(1);
  71 }
  72 
  73 static 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 
  84 static 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 
  93 static 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  */
 121 static 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 */
 147 static int force_sysemu_disabled = 0;
 148 
 149 static 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 
 164 static 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 
 253 fail:
 254         stop_ptraced_child(pid, 1, 0);
 255 fail_stopped:
 256         non_fatal("missing\n");
 257 }
 258 
 259 static 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 
 299 extern void check_tmpexec(void);
 300 
 301 static 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 
 324 void __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 
 344 int __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 }

/* [<][>][^][v][top][bottom][index][help] */