LCOV - code coverage report
Current view: top level - arch/um/os-Linux - start_up.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 191 57.1 %
Date: 2023-03-27 20:00:47 Functions: 8 13 61.5 %

          Line data    Source code
       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           4 : static void ptrace_child(void)
      29             : {
      30             :         int ret;
      31             :         /* Calling os_getpid because some libcs cached getpid incorrectly */
      32           4 :         int pid = os_getpid(), ppid = getppid();
      33             :         int sc_result;
      34             : 
      35           8 :         if (change_sig(SIGWINCH, 0) < 0 ||
      36           4 :             ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
      37           0 :                 perror("ptrace");
      38           0 :                 kill(pid, SIGKILL);
      39             :         }
      40           4 :         kill(pid, SIGSTOP);
      41             : 
      42             :         /*
      43             :          * This syscall will be intercepted by the parent. Don't call more than
      44             :          * once, please.
      45             :          */
      46           4 :         sc_result = os_getpid();
      47             : 
      48           4 :         if (sc_result == pid)
      49             :                 /* Nothing modified by the parent, we are running normally. */
      50             :                 ret = 1;
      51           3 :         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           0 :                 ret = 2;
      63             : 
      64           4 :         exit(ret);
      65             : }
      66             : 
      67           0 : static void fatal_perror(const char *str)
      68             : {
      69           0 :         perror(str);
      70           0 :         exit(1);
      71             : }
      72             : 
      73           0 : static void fatal(char *fmt, ...)
      74             : {
      75             :         va_list list;
      76             : 
      77           0 :         va_start(list, fmt);
      78           0 :         vfprintf(stderr, fmt, list);
      79           0 :         va_end(list);
      80             : 
      81           0 :         exit(1);
      82             : }
      83             : 
      84           0 : static void non_fatal(char *fmt, ...)
      85             : {
      86             :         va_list list;
      87             : 
      88           0 :         va_start(list, fmt);
      89           0 :         vfprintf(stderr, fmt, list);
      90           0 :         va_end(list);
      91           0 : }
      92             : 
      93          14 : static int start_ptraced_child(void)
      94             : {
      95             :         int pid, n, status;
      96             : 
      97          14 :         fflush(stdout);
      98             : 
      99          14 :         pid = fork();
     100          14 :         if (pid == 0)
     101           4 :                 ptrace_child();
     102          10 :         else if (pid < 0)
     103           0 :                 fatal_perror("start_ptraced_child : fork failed");
     104             : 
     105          10 :         CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
     106          10 :         if (n < 0)
     107           0 :                 fatal_perror("check_ptrace : waitpid failed");
     108          10 :         if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
     109           0 :                 fatal("check_ptrace : expected SIGSTOP, got status = %d",
     110             :                       status);
     111             : 
     112          10 :         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          10 : static int stop_ptraced_child(int pid, int exitcode, int mustexit)
     122             : {
     123          10 :         int status, n, ret = 0;
     124             : 
     125          10 :         if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
     126           0 :                 perror("stop_ptraced_child : ptrace failed");
     127           0 :                 return -1;
     128             :         }
     129          10 :         CATCH_EINTR(n = waitpid(pid, &status, 0));
     130          10 :         if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
     131           0 :                 int exit_with = WEXITSTATUS(status);
     132           0 :                 if (exit_with == 2)
     133           0 :                         non_fatal("check_ptrace : child exited with status 2. "
     134             :                                   "\nDisabling SYSEMU support.\n");
     135           0 :                 non_fatal("check_ptrace : child exited with exitcode %d, while "
     136             :                           "expecting %d; status 0x%x\n", exit_with,
     137             :                           exitcode, status);
     138           0 :                 if (mustexit)
     139           0 :                         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           0 : static int __init nosysemu_cmd_param(char *str, int* add)
     150             : {
     151           0 :         force_sysemu_disabled = 1;
     152           0 :         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           4 : static void __init check_sysemu(void)
     165             : {
     166             :         unsigned long regs[MAX_REG_NR];
     167           4 :         int pid, n, status, count=0;
     168             : 
     169           4 :         os_info("Checking syscall emulation patch for ptrace...");
     170           4 :         sysemu_supported = 0;
     171           4 :         pid = start_ptraced_child();
     172             : 
     173           3 :         if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
     174             :                 goto fail;
     175             : 
     176           3 :         CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
     177           3 :         if (n < 0)
     178           0 :                 fatal_perror("check_sysemu : wait failed");
     179           3 :         if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
     180           0 :                 fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
     181             :                       status);
     182             : 
     183           3 :         if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
     184           0 :                 fatal_perror("check_sysemu : PTRACE_GETREGS failed");
     185           3 :         if (PT_SYSCALL_NR(regs) != __NR_getpid) {
     186           0 :                 non_fatal("check_sysemu got system call number %d, "
     187             :                           "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
     188           0 :                 goto fail;
     189             :         }
     190             : 
     191           3 :         n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
     192           3 :         if (n < 0) {
     193           0 :                 non_fatal("check_sysemu : failed to modify system call "
     194             :                           "return");
     195           0 :                 goto fail;
     196             :         }
     197             : 
     198           3 :         if (stop_ptraced_child(pid, 0, 0) < 0)
     199             :                 goto fail_stopped;
     200             : 
     201           3 :         sysemu_supported = 1;
     202           3 :         os_info("OK\n");
     203           3 :         set_using_sysemu(!force_sysemu_disabled);
     204             : 
     205           3 :         os_info("Checking advanced syscall emulation patch for ptrace...");
     206           3 :         pid = start_ptraced_child();
     207             : 
     208           2 :         if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
     209             :                    (void *) PTRACE_O_TRACESYSGOOD) < 0))
     210           0 :                 fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
     211             : 
     212             :         while (1) {
     213          54 :                 count++;
     214          54 :                 if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
     215             :                         goto fail;
     216          54 :                 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
     217          54 :                 if (n < 0)
     218           0 :                         fatal_perror("check_sysemu: wait failed");
     219             : 
     220         108 :                 if (WIFSTOPPED(status) &&
     221          54 :                     (WSTOPSIG(status) == (SIGTRAP|0x80))) {
     222             :                         if (!count) {
     223             :                                 non_fatal("check_sysemu: SYSEMU_SINGLESTEP "
     224             :                                           "doesn't singlestep");
     225             :                                 goto fail;
     226             :                         }
     227           2 :                         n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
     228             :                                    os_getpid());
     229           2 :                         if (n < 0)
     230           0 :                                 fatal_perror("check_sysemu : failed to modify "
     231             :                                              "system call return");
     232             :                         break;
     233             :                 }
     234          52 :                 else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
     235             :                         count++;
     236             :                 else {
     237           0 :                         non_fatal("check_sysemu: expected SIGTRAP or "
     238             :                                   "(SIGTRAP | 0x80), got status = %d\n",
     239             :                                   status);
     240           0 :                         goto fail;
     241             :                 }
     242             :         }
     243           2 :         if (stop_ptraced_child(pid, 0, 0) < 0)
     244             :                 goto fail_stopped;
     245             : 
     246           2 :         sysemu_supported = 2;
     247           2 :         os_info("OK\n");
     248             : 
     249           2 :         if (!force_sysemu_disabled)
     250           2 :                 set_using_sysemu(sysemu_supported);
     251           2 :         return;
     252             : 
     253             : fail:
     254           0 :         stop_ptraced_child(pid, 1, 0);
     255             : fail_stopped:
     256           0 :         non_fatal("missing\n");
     257             : }
     258             : 
     259           5 : static void __init check_ptrace(void)
     260             : {
     261             :         int pid, syscall, n, status;
     262             : 
     263           5 :         os_info("Checking that ptrace can change system call numbers...");
     264           5 :         pid = start_ptraced_child();
     265             : 
     266           4 :         if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
     267             :                    (void *) PTRACE_O_TRACESYSGOOD) < 0))
     268           0 :                 fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
     269             : 
     270             :         while (1) {
     271           4 :                 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
     272           0 :                         fatal_perror("check_ptrace : ptrace failed");
     273             : 
     274           4 :                 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
     275           4 :                 if (n < 0)
     276           0 :                         fatal_perror("check_ptrace : wait failed");
     277             : 
     278           8 :                 if (!WIFSTOPPED(status) ||
     279           4 :                    (WSTOPSIG(status) != (SIGTRAP | 0x80)))
     280           0 :                         fatal("check_ptrace : expected (SIGTRAP|0x80), "
     281             :                                "got status = %d", status);
     282             : 
     283           4 :                 syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
     284             :                                  0);
     285           4 :                 if (syscall == __NR_getpid) {
     286           4 :                         n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
     287             :                                    __NR_getppid);
     288           4 :                         if (n < 0)
     289           0 :                                 fatal_perror("check_ptrace : failed to modify "
     290             :                                              "system call");
     291             :                         break;
     292             :                 }
     293             :         }
     294           4 :         stop_ptraced_child(pid, 0, 1);
     295           4 :         os_info("OK\n");
     296           4 :         check_sysemu();
     297           2 : }
     298             : 
     299             : extern void check_tmpexec(void);
     300             : 
     301           5 : static void __init check_coredump_limit(void)
     302             : {
     303             :         struct rlimit lim;
     304           5 :         int err = getrlimit(RLIMIT_CORE, &lim);
     305             : 
     306           5 :         if (err) {
     307           0 :                 perror("Getting core dump limit");
     308           0 :                 return;
     309             :         }
     310             : 
     311           5 :         os_info("Core dump limits :\n\tsoft - ");
     312           5 :         if (lim.rlim_cur == RLIM_INFINITY)
     313           0 :                 os_info("NONE\n");
     314             :         else
     315           5 :                 os_info("%llu\n", (unsigned long long)lim.rlim_cur);
     316             : 
     317           5 :         os_info("\thard - ");
     318           5 :         if (lim.rlim_max == RLIM_INFINITY)
     319           5 :                 os_info("NONE\n");
     320             :         else
     321           0 :                 os_info("%llu\n", (unsigned long long)lim.rlim_max);
     322             : }
     323             : 
     324           1 : void  __init get_host_cpu_features(
     325             :                 void (*flags_helper_func)(char *line),
     326             :                 void (*cache_helper_func)(char *line))
     327             : {
     328             :         FILE *cpuinfo;
     329           1 :         char *line = NULL;
     330           1 :         size_t len = 0;
     331           1 :         int done_parsing = 0;
     332             : 
     333           1 :         cpuinfo = fopen("/proc/cpuinfo", "r");
     334           1 :         if (cpuinfo == NULL) {
     335           0 :                 os_info("Failed to get host CPU features\n");
     336             :         } else {
     337          21 :                 while ((getline(&line, &len, cpuinfo)) != -1) {
     338          21 :                         if (strstr(line, "flags")) {
     339           2 :                                 flags_helper_func(line);
     340           2 :                                 done_parsing++;
     341             :                         }
     342          21 :                         if (strstr(line, "cache_alignment")) {
     343           0 :                                 cache_helper_func(line);
     344           0 :                                 done_parsing++;
     345             :                         }
     346          21 :                         free(line);
     347          21 :                         line = NULL;
     348          21 :                         if (done_parsing > 1)
     349             :                                 break;
     350             :                 }
     351           1 :                 fclose(cpuinfo);
     352             :         }
     353           1 : }
     354             : 
     355             : 
     356           5 : void __init os_early_checks(void)
     357             : {
     358             :         int pid;
     359             : 
     360             :         /* Print out the core dump limits early */
     361           5 :         check_coredump_limit();
     362             : 
     363           5 :         check_ptrace();
     364             : 
     365             :         /* Need to check this early because mmapping happens before the
     366             :          * kernel is running.
     367             :          */
     368           2 :         check_tmpexec();
     369             : 
     370           2 :         pid = start_ptraced_child();
     371           1 :         if (init_pid_registers(pid))
     372           0 :                 fatal("Failed to initialize default registers");
     373           1 :         stop_ptraced_child(pid, 1, 1);
     374           1 : }
     375             : 
     376           0 : int __init parse_iomem(char *str, int *add)
     377             : {
     378             :         struct iomem_region *new;
     379             :         struct stat64 buf;
     380             :         char *file, *driver;
     381             :         int fd, size;
     382             : 
     383           0 :         driver = str;
     384           0 :         file = strchr(str,',');
     385           0 :         if (file == NULL) {
     386           0 :                 os_warn("parse_iomem : failed to parse iomem\n");
     387           0 :                 goto out;
     388             :         }
     389           0 :         *file = '\0';
     390           0 :         file++;
     391           0 :         fd = open(file, O_RDWR, 0);
     392           0 :         if (fd < 0) {
     393           0 :                 perror("parse_iomem - Couldn't open io file");
     394           0 :                 goto out;
     395             :         }
     396             : 
     397           0 :         if (fstat64(fd, &buf) < 0) {
     398           0 :                 perror("parse_iomem - cannot stat_fd file");
     399           0 :                 goto out_close;
     400             :         }
     401             : 
     402           0 :         new = malloc(sizeof(*new));
     403           0 :         if (new == NULL) {
     404           0 :                 perror("Couldn't allocate iomem_region struct");
     405           0 :                 goto out_close;
     406             :         }
     407             : 
     408           0 :         size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
     409             : 
     410           0 :         *new = ((struct iomem_region) { .next           = iomem_regions,
     411             :                                         .driver         = driver,
     412             :                                         .fd             = fd,
     413             :                                         .size           = size,
     414             :                                         .phys           = 0,
     415             :                                         .virt           = 0 });
     416           0 :         iomem_regions = new;
     417           0 :         iomem_size += new->size + UM_KERN_PAGE_SIZE;
     418             : 
     419           0 :         return 0;
     420             :  out_close:
     421           0 :         close(fd);
     422             :  out:
     423             :         return 1;
     424             : }

Generated by: LCOV version 1.14