diff options
author | Wen Jin <wenx.jin@intel.com> | 2015-12-24 11:05:22 +0800 |
---|---|---|
committer | Andi Kleen <ak@linux.intel.com> | 2015-12-25 19:39:09 -0800 |
commit | ac82804edef3137aa153838c038d35f7e80f50f9 (patch) | |
tree | 6a5df68d7037b2d77508adaf3457f05a6ce99aae | |
parent | fe0b3b24605543ea81b37446cf3abb93f6dc5906 (diff) | |
download | mce-test-ac82804edef3137aa153838c038d35f7e80f50f9.tar.gz |
Unify test case for error injection
We have two similiar to-be-injected "victim" programs but
it is a little bit redundant. Merge them into one. BTW,
add a new function to enable to choose if injecting a
error by hand.
Minor fix by Gong.
Signed-off-by: Wen Jin <wenx.jin@intel.com>
Signed-off-by: Chen, Gong <gong.chen@linux.intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
-rw-r--r-- | cases/function/Makefile | 3 | ||||
-rw-r--r-- | cases/function/core_recovery/Makefile | 11 | ||||
-rw-r--r-- | cases/function/core_recovery/core_recovery.c | 182 | ||||
-rwxr-xr-x | cases/function/core_recovery/srar_recovery.sh | 8 | ||||
-rw-r--r-- | tools/victim/victim.c | 361 |
5 files changed, 212 insertions, 353 deletions
diff --git a/cases/function/Makefile b/cases/function/Makefile index a94da1a..6d66c0c 100644 --- a/cases/function/Makefile +++ b/cases/function/Makefile @@ -2,20 +2,17 @@ all: $(MAKE) -C erst-inject $(MAKE) -C pfa $(MAKE) -C hwpoison - $(MAKE) -C core_recovery # $(MAKE) -C kvm clean: $(MAKE) -C erst-inject clean $(MAKE) -C pfa clean $(MAKE) -C hwpoison clean - $(MAKE) -C core_recovery clean # $(MAKE) -C kvm clean install: $(MAKE) -C erst-inject install $(MAKE) -C pfa install $(MAKE) -C hwpoison install - $(MAKE) -C core_recovery install # $(MAKE) -C kvm install diff --git a/cases/function/core_recovery/Makefile b/cases/function/core_recovery/Makefile deleted file mode 100644 index c18edf1..0000000 --- a/cases/function/core_recovery/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# pread needs: -# _XOPEN_SOURCE >= 500 || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200809L -# but this hides MAP_ANONYMOUS which comes back via _BSD_SOURCE -CFLAGS := -Wall -D _BSD_SOURCE -D _XOPEN_SOURCE=500 - -core_recovery: core_recovery.o - -install: core_recovery - -clean: - rm -f core_recovery *.o diff --git a/cases/function/core_recovery/core_recovery.c b/cases/function/core_recovery/core_recovery.c deleted file mode 100644 index 835c243..0000000 --- a/cases/function/core_recovery/core_recovery.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Set up to get zapped by a machine check (injected elsewhere) - * To use this test case please ensure your SUT(System Under Test) - * can support MCE/SRAR. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should find a copy of v2 of the GNU General Public License somewhere on - * your Linux system; if not, write to the Free Software Foundation, Inc., 59 - * Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * Copyright (C) 2012 Intel corporation - * - * Author: - * Tony Luck <tony.luck@intel.com> - * Gong Chen <gong.chen@intel.com> - */ - -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <time.h> -#include <fcntl.h> -#include <getopt.h> -#include <sys/mman.h> - -/* - * Definition of /proc/pid/pagemap - * Bits 0-54 page frame number (PFN) if present - * Bits 0-4 swap type if swapped - * Bits 5-54 swap offset if swapped - * Bits 55-60 page shift (page size = 1<<page shift) - * Bit 61 reserved for future use - * Bit 62 page swapped - * Bit 63 page present - */ - -struct pagemaps { - unsigned long long pfn:55; - unsigned long long pgshift:6; - unsigned long long rsvd:1; - unsigned long long swapped:1; - unsigned long long present:1; -}; - -static int pagesize; - -/* - * dummyfunc size should be less than one page after complied, - * otherwise, caller will not return from this function - */ -void dummyfunc(void) -{ - int fatarray[64]; - - fatarray[0] = 0xdeadbeaf; - fatarray[8] = 0xdeadbeaf; - fatarray[16] = 0xdeadbeaf; - fatarray[32] = 0xdeadbeaf; -} - -/* - * get information about address from /proc/{pid}/pagemap - */ -unsigned long long vtop(unsigned long long addr) -{ - struct pagemaps pinfo; - unsigned int pinfo_size = sizeof pinfo; - long offset = addr / pagesize * pinfo_size; - int fd, pgmask; - char pagemapname[64]; - - sprintf(pagemapname, "/proc/%d/pagemap", getpid()); - fd = open(pagemapname, O_RDONLY); - if (fd == -1) { - perror(pagemapname); - return 0; - } - if (pread(fd, (void*)&pinfo, pinfo_size, offset) != pinfo_size) { - perror(pagemapname); - close(fd); - return 0; - } - close(fd); - pgmask = (1 << pinfo.pgshift) - 1; - return (pinfo.pfn << pinfo.pgshift) | (addr & pgmask); -} - -static void usage(void) -{ - printf( -"core_recovery [options]\n" -" -d|--data Inject data error(DCU error) under user context\n" -" -i|--instruction Inject instruction error(IFU error) under user context\n" -" -h|--help Show this usage message\n" - ); -} - -static const struct option opts[] = { - { "data" , 0, NULL, 'd' }, - { "instruction" , 0, NULL, 'i' }, - { "help" , 0, NULL, 'h' }, - { NULL , 0, NULL, 0 } -}; - -int main(int argc, char **argv) -{ - unsigned long long phys; - long total; - char *buf, answer[16]; - int c, i; - int iflag = 0, dflag = 0; - time_t now; - - if (argc <= 1) { - usage(); - return 0; - } - - pagesize = getpagesize(); - - while ((c = getopt_long(argc, argv, "dih", opts, NULL)) != -1) { - switch (c) { - case 'd': - dflag = 1; - break; - case 'i': - iflag = 1; - break; - case 'h': - default: - usage(); - return 0; - } - } - - buf = mmap(NULL, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_ANONYMOUS|MAP_PRIVATE|MAP_LOCKED, -1, 0); - - if (buf == MAP_FAILED) { - fprintf(stderr, "Can't get a single page of memory!\n"); - return 1; - } - memset(buf, '*', pagesize); - phys = vtop((unsigned long long)buf); - if (phys == 0) { - fprintf(stderr, "Can't get physical address of the page!\n"); - return 1; - } - - if (iflag) - memcpy(buf, (void*)dummyfunc, pagesize); - - printf("physical address of (0x%llx) = 0x%llx\n" - "Hit any key to trigger error: ", (unsigned long long)buf, phys); - fflush(stdout); - read(0, answer, 16); - now = time(NULL); - printf("Access time at %s\n", ctime(&now)); - - if (iflag) { - void (*f)(void) = (void (*)(void))buf; - - while (1) f() ; - } - - if (dflag) { - while (1) { - for (i = 0; i < pagesize; i += sizeof(int)) - total += *(int*)(buf + i); - } - } - - return 0; -} diff --git a/cases/function/core_recovery/srar_recovery.sh b/cases/function/core_recovery/srar_recovery.sh index 7b13b5b..ae64af9 100755 --- a/cases/function/core_recovery/srar_recovery.sh +++ b/cases/function/core_recovery/srar_recovery.sh @@ -3,6 +3,8 @@ #set -x export ROOT=`(cd ../../../; pwd)` +. $ROOT/lib/functions.sh +setup_path . $ROOT/lib/mce.sh inject_type=0x00000010 @@ -65,15 +67,17 @@ else fi rmmod $EDAC_TYPE >/dev/null 2>&1 +[ -e $ROOT/bin/victim ] || invalid "file victim doesn't exist!" \ +"maybe you forget to execute make install under directory $ROOT before test" touch trigger -tail -f trigger | ./core_recovery $1 > log & +tail -f trigger | victim $1 > log & addr=`cat log |cut -d' ' -f6|head -1` apei_inj $addr sleep 1 echo go > trigger sleep 2 rm -f trigger log -id=`pgrep core_recovery` +id=`pgrep victim` if [ X"$id" != X ]; then echo $id | xargs kill -9 > /dev/null 2>&1 invalid "The poisoned process can't be killed by kernel automatically. Test fails!" diff --git a/tools/victim/victim.c b/tools/victim/victim.c index a381909..a0435e5 100644 --- a/tools/victim/victim.c +++ b/tools/victim/victim.c @@ -1,49 +1,31 @@ /* - * Victim + * Set up to get zapped by a machine check (injected elsewhere) + * To use this test case please ensure your SUT(System Under Test) + * can support MCE/SRAR. * - * Victim workes under user context, which provides target memory chunk for - * error injection. It can be used for all kinds of error types, including - * Corrected error and Uncorrected error(IFU/DCU). + * This file is released under the GPLv2. * - * Here is an simple example for DCU: - * Mmap one page memory and returns starting address, and then translate - * virtual address to physical address. Caller like shell script can - * inject UC error (error type 0x10 in EINJ table) on returned physical - * address. Meanwhile, victim continues to read/write on returned memory - * space to trigger DCU happening ASAP. - * - * Copyright (C) 2015, Intel Corp. + * Copyright (C) 2012-2015 Intel corporation * * Author: - * Zhilong Liu <zhilongx.liu@intel.com> - * - * Date: - * 01/15 2015 - * - * History: Revision history - * None + * Tony Luck <tony.luck@intel.com> + * Gong Chen <gong.chen@intel.com> + * Wen Jin <wenx.jin@intel.com> */ + #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <fcntl.h> +#include <time.h> +#include <signal.h> #include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <getopt.h> -#include <string.h> -#include <time.h> - -/* - * Use below macros to make this a non-trivial sized function. - */ -#define PLUS10 (ifunc_ret++, ifunc_ret++, ifunc_ret++, ifunc_ret++, \ - ifunc_ret++, ifunc_ret++, ifunc_ret++, ifunc_ret++, \ - ifunc_ret++, ifunc_ret++) -#define PLUS100 (PLUS10, PLUS10, PLUS10, PLUS10, PLUS10, PLUS10, \ - PLUS10, PLUS10, PLUS10, PLUS10) -#define PLUS1000 (PLUS100, PLUS100, PLUS100, PLUS100, PLUS100, \ - PLUS100, PLUS100, PLUS100, PLUS100, PLUS100) - -static int pagesize; +#include <errno.h> /* * Definition of /proc/pid/pagemap @@ -55,6 +37,7 @@ static int pagesize; * Bit 62 page swapped * Bit 63 page present */ + struct pagemaps { unsigned long long pfn:55; unsigned long long pgshift:6; @@ -63,33 +46,34 @@ struct pagemaps { unsigned long long present:1; }; -/* Don't let compiler optimize away access to this */ -volatile int ifunc_ret; +static int pagesize; -int ifunc(void) +/* + * dummyfunc size should be less than one page after complied, + * otherwise, caller will not return from this function + */ +void dummyfunc(void) { - ifunc_ret = 0; - - PLUS1000; + int fatarray[64]; - return ifunc_ret; + fatarray[0] = 0xdeadbeaf; + fatarray[8] = 0xdeadbeaf; + fatarray[16] = 0xdeadbeaf; + fatarray[32] = 0xdeadbeaf; } /* * get information about address from /proc/{pid}/pagemap */ -unsigned long long vtop(unsigned long long addr) +unsigned long long vtop(unsigned long long addr, pid_t pid) { struct pagemaps pinfo; unsigned int pinfo_size = sizeof(pinfo); - long offset; + unsigned long long offset = addr / pagesize * pinfo_size; int fd, pgmask; char pagemapname[64]; - if (!pagesize) - pagesize = getpagesize(); - offset = addr / pagesize * pinfo_size; - sprintf(pagemapname, "/proc/%d/pagemap", getpid()); + sprintf(pagemapname, "/proc/%d/pagemap", pid); fd = open(pagemapname, O_RDONLY); if (fd == -1) { perror(pagemapname); @@ -101,113 +85,62 @@ unsigned long long vtop(unsigned long long addr) return 0; } close(fd); - if (!pinfo.present) - return ~0ull; - pgmask = (1 << pinfo.pgshift) - 1; - return (pinfo.pfn << pinfo.pgshift) | (addr & pgmask); + pgmask = pagesize - 1; + return (pinfo.pfn * pagesize) | (addr & pgmask); } static void usage(void) { printf( "victim [options]\n" -" -d|--data Inject data error(DCU error) under user context\n" -" -i|--instruction Inject instruction error(IFU error) under user context\n" -" -p|--pfa Inject memory CE consecutivelly under user context to trigger pfa\n" -" -h|--help Show this usage message\n" +" -a|--address vaddr=val, pid=val Translate process virtual address into physical address\n" +" -d|--data Inject data error(DCU error) under user context\n" +" -i|--instruction Inject instruction error(IFU error) under user context\n" +" -k|--kick 0/1 Kick off trigger. Auto(0), Manual(1, by default)\n" +" -h|--help Show this usage message\n" ); } static const struct option opts[] = { - { "data", 0, NULL, 'd' }, - { "instruction", 0, NULL, 'i' }, - { "pfa", 0, NULL, 'p' }, - { "help", 0, NULL, 'h' }, - { NULL, 0, NULL, 0 } + { "address" , 1, NULL, 'a' }, + { "data" , 0, NULL, 'd' }, + { "instruction" , 0, NULL, 'i' }, + { "help" , 0, NULL, 'h' }, + { "kick" , 1, NULL, 'k' }, + { NULL , 0, NULL, 0 } }; -void trigger_ifu(void) -{ - time_t now; - char answer[16]; - unsigned long long phys; - void (*f)(void) = (void (*)(void))ifunc; - - phys = vtop((unsigned long long)f); - printf("physical address of (%p) = 0x%llx\n" - "Hit any key to trigger error: ", f, phys); - fflush(stdout); - read(0, answer, 16); - now = time(NULL); - printf("Access time at %s\n", ctime(&now)); - while (1) - f(); -} - -void trigger_dcu(unsigned long long virt, unsigned long long phys) +int main(int argc, char **argv) { - int i; + unsigned long long virt, phys; long total; - time_t now; + char *buf; + int c, i; + int iflag = 0, dflag = 0; + int kick = 1; + pid_t pid; + enum { + I_VADDR = 0, + I_PID + }; + char *const token[] = { + [I_VADDR] = "vaddr", + [I_PID] = "pid", + NULL + }; + char *subopts; + char *subval; + char *svaddr; + char *spid; + int err; + int index; + const char *trigger = "./trigger_start"; + const char *trigger_flag = "trigger"; + int fd; + int count = 100; + char trigger_buf[16]; char answer[16]; - - printf("physical address of (0x%llx) = 0x%llx\n" - "Hit any key to trigger error: ", virt, phys); - fflush(stdout); - read(0, answer, 16); - now = time(NULL); - printf("Access time at %s\n", ctime(&now)); - while (1) { - for (i = 0; i < pagesize; i += sizeof(int)) - total += *(int *)(virt + i); - } -} - -/* - * test PFA when inject CE(0x8) consecutivelly - */ -void trigger_pfa(unsigned long long virt, unsigned long long phys) -{ - int i; - long total; - unsigned long long newphys; - - printf("physical address of (0x%llx) = 0x%llx\n", virt, phys); - fflush(stdout); - while (1) { - for (i = 0; i < pagesize; i += sizeof(int)) { - total += *(int *)(virt + i); - *(int *)(virt + i) = total; - } - - newphys = vtop(virt); - if (phys == newphys) { - for (i = 0; i < pagesize; i += sizeof(int)) { - total += *(int *)(virt + i); - *(int *)(virt + i) = i; - } - sleep(2); - newphys = vtop(virt); - if (phys != newphys) { - printf("Page was replaced. New phys addr = 0x%llx\n", - newphys); - fflush(stdout); - phys = newphys; - } - } else { - printf("Page was replaced. New phys addr = 0x%llx\n", - newphys); - fflush(stdout); - phys = newphys; - } - } -} - -int main(int argc, char **argv) -{ - int c; - char *p; - unsigned long long phys; + time_t now; if (argc <= 1) { usage(); @@ -215,27 +148,81 @@ int main(int argc, char **argv) } pagesize = getpagesize(); - /* only RD/WR permission needed */ - p = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (p == MAP_FAILED) { - perror("mmap"); - return 1; - } - /* make sure that kernel does allocate page */ - memset(p, '*', pagesize); - phys = vtop((unsigned long long)p); - while ((c = getopt_long(argc, argv, "diph", opts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "a:dihk:", opts, NULL)) != -1) { switch (c) { + case 'a': + err = 0; + subopts = optarg; + while (*subopts != '\0' && !err) { + index = getsubopt(&subopts, token, &subval); + switch (index) { + case I_VADDR: + if (subval != NULL) { + svaddr = subval; + break; + } else { + fprintf(stderr, + "miss value for %s\n", + token[I_VADDR]); + err++; + continue; + } + case I_PID: + if (subval != NULL) { + spid = subval; + break; + } else { + fprintf(stderr, + "miss value for %s\n", + token[I_PID]); + err++; + continue; + } + default: + err++; + break; + } + } + if (err > 0) { + usage(); + return 0; + } + errno = 0; + virt = strtoull(svaddr, NULL, 0); + if ((virt == 0 && svaddr[0] != '0') || errno != 0) { + fprintf(stderr, "Invalid virtual address: %s\n", + svaddr); + return 1; + } + errno = 0; + pid = strtoul(spid, NULL, 0); + if ((pid == 0 && spid[0] != '0') || errno != 0) { + fprintf(stderr, "Invalid process pid number: %s\n", + spid); + return 1; + } + phys = vtop(virt, pid); + printf("physical address of (%d,0x%llx) = 0x%llx\n", + pid, virt, phys); + return 0; case 'd': - trigger_dcu((unsigned long long)p, phys); + dflag = 1; break; case 'i': - trigger_ifu(); + iflag = 1; break; - case 'p': - trigger_pfa((unsigned long long)p, phys); + case 'k': + errno = 0; + kick = strtol(optarg, NULL, 0); + if ((kick == 0 && optarg[0] != '0') || errno != 0) { + fprintf(stderr, "Invalid parameter: %s\n", optarg); + return 1; + } + if (kick != 0 && kick != 1) { + fprintf(stderr, "Invalid parameter: %s\n", optarg); + return 1; + } break; case 'h': default: @@ -244,5 +231,69 @@ int main(int argc, char **argv) } } + buf = mmap(NULL, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANONYMOUS|MAP_PRIVATE|MAP_LOCKED, -1, 0); + + if (buf == MAP_FAILED) { + fprintf(stderr, "Can't get a single page of memory!\n"); + return 1; + } + memset(buf, '*', pagesize); + pid = getpid(); + phys = vtop((unsigned long long)buf, pid); + if (phys == 0) { + fprintf(stderr, "Can't get physical address of the page!\n"); + return 1; + } + + if (iflag) + memcpy(buf, (void *)dummyfunc, pagesize); + + printf("physical address of (0x%llx) = 0x%llx\n", + (unsigned long long)buf, phys); + fflush(stdout); + if (kick == 0) { + errno = 0; + if (unlink(trigger) < 0 && errno != ENOENT) { + fprintf(stderr, "fail to remove trigger file\n"); + return 1; + } + memset(trigger_buf, 0, sizeof(trigger_buf)); + while (count--) { + if ((fd = open(trigger, O_RDONLY)) < 0) { + sleep(1); + continue; + } + if (read(fd, trigger_buf, sizeof(trigger_buf)) > 0 && + strstr(trigger_buf, trigger_flag) != NULL) { + break; + } + sleep(1); + } + if (count == 0) { + fprintf(stderr, + "Timeout to get trigger flag file\n"); + return 1; + } + } else { + printf("Hit any key to trigger error: "); + fflush(stdout); + read(0, answer, 16); + now = time(NULL); + printf("Access time at %s\n", ctime(&now)); + } + if (iflag) { + void (*f)(void) = (void (*)(void))buf; + + while (1) f(); + } + + if (dflag) { + while (1) { + for (i = 0; i < pagesize; i += sizeof(int)) + total += *(int *)(buf + i); + } + } + return 0; } |