cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

start_up.c (9807B)


      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
     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 get_host_cpu_features(
    325		void (*flags_helper_func)(char *line),
    326		void (*cache_helper_func)(char *line))
    327{
    328	FILE *cpuinfo;
    329	char *line = NULL;
    330	size_t len = 0;
    331	int done_parsing = 0;
    332
    333	cpuinfo = fopen("/proc/cpuinfo", "r");
    334	if (cpuinfo == NULL) {
    335		os_info("Failed to get host CPU features\n");
    336	} else {
    337		while ((getline(&line, &len, cpuinfo)) != -1) {
    338			if (strstr(line, "flags")) {
    339				flags_helper_func(line);
    340				done_parsing++;
    341			}
    342			if (strstr(line, "cache_alignment")) {
    343				cache_helper_func(line);
    344				done_parsing++;
    345			}
    346			free(line);
    347			line = NULL;
    348			if (done_parsing > 1)
    349				break;
    350		}
    351		fclose(cpuinfo);
    352	}
    353}
    354
    355
    356void __init os_early_checks(void)
    357{
    358	int pid;
    359
    360	/* Print out the core dump limits early */
    361	check_coredump_limit();
    362
    363	check_ptrace();
    364
    365	/* Need to check this early because mmapping happens before the
    366	 * kernel is running.
    367	 */
    368	check_tmpexec();
    369
    370	pid = start_ptraced_child();
    371	if (init_pid_registers(pid))
    372		fatal("Failed to initialize default registers");
    373	stop_ptraced_child(pid, 1, 1);
    374}
    375
    376int __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	driver = str;
    384	file = strchr(str,',');
    385	if (file == NULL) {
    386		os_warn("parse_iomem : failed to parse iomem\n");
    387		goto out;
    388	}
    389	*file = '\0';
    390	file++;
    391	fd = open(file, O_RDWR, 0);
    392	if (fd < 0) {
    393		perror("parse_iomem - Couldn't open io file");
    394		goto out;
    395	}
    396
    397	if (fstat64(fd, &buf) < 0) {
    398		perror("parse_iomem - cannot stat_fd file");
    399		goto out_close;
    400	}
    401
    402	new = malloc(sizeof(*new));
    403	if (new == NULL) {
    404		perror("Couldn't allocate iomem_region struct");
    405		goto out_close;
    406	}
    407
    408	size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
    409
    410	*new = ((struct iomem_region) { .next		= iomem_regions,
    411					.driver		= driver,
    412					.fd		= fd,
    413					.size		= size,
    414					.phys		= 0,
    415					.virt		= 0 });
    416	iomem_regions = new;
    417	iomem_size += new->size + UM_KERN_PAGE_SIZE;
    418
    419	return 0;
    420 out_close:
    421	close(fd);
    422 out:
    423	return 1;
    424}