Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
370 changes: 370 additions & 0 deletions KVM/qemu/deps/ras/victim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,370 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2015 Intel corporation
*
* 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.
*
* Author:
* Tony Luck <tony.luck@intel.com>
* Gong Chen <gong.chen@intel.com>
* Wen Jin <wenx.jin@intel.com>
*
* Copy from mce-test
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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 <errno.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, the bits definition is legacy.
* 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; /*legacy*/
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, pid_t pid)
{
struct pagemaps pinfo;
unsigned int pinfo_size = sizeof(pinfo);
unsigned long long offset = addr / pagesize * pinfo_size;
int fd, pgmask;
char pagemapname[64];

sprintf(pagemapname, "/proc/%d/pagemap", pid);
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 = pagesize - 1;
return (pinfo.pfn * pagesize) | (addr & pgmask);
}

static void usage(void)
{
int i;
static const char *const msg[] = {
"victim [options]",
"-a|--address vaddr=val, pid=val Translate process virtual address into physical address",
"-d|--data Inject data error(DCU error) under user context",
"-i|--instruction Inject instruction error(IFU error) under user context",
"-k|--kick 0/1 Kick off trigger. Auto(0), Manual(1, by default)",
"-p|--pfa test memory PFA(Predictive Failure Analysis) function",
"-h|--help Show this help message",
NULL,
};

for (i = 0; msg[i]; i++)
printf("%s\n", msg[i]);
}

static const struct option opts[] = {
{ "address", 1, NULL, 'a' },
{ "data", 0, NULL, 'd' },
{ "instruction", 0, NULL, 'i' },
{ "help", 0, NULL, 'h' },
{ "kick", 1, NULL, 'k' },
{ "pfa", 0, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};

static void pfa_helper(char *p, pid_t pid, unsigned long long old_phys)
{
int i;
int total;
unsigned long long new_phys;

for (;;) {
for (i = 0; i < pagesize; i += sizeof(int)) {
total += *(int *)(p + i);
*(int *)(p + i) = total;
}

new_phys = vtop((unsigned long long)p, pid);
if (old_phys == new_phys) {
for (i = 0; i < pagesize; i += sizeof(int)) {
total += *(int *)(p + i);
*(int *)(p + i) = i;
}
sleep(2);
new_phys = vtop((unsigned long long)p, pid);
if (old_phys != new_phys) {
printf("Page was replaced. New physical address = 0x%llx\n",
new_phys);
fflush(stdout);
old_phys = new_phys;
}
} else {
printf("Page was replaced. New physical address = 0x%llx\n", new_phys);
fflush(stdout);
old_phys = new_phys;
}
}
}

static int parse_addr_subopts(char *subopts, unsigned long long *virt,
pid_t *pid)
{
int err = 0;
int index;
enum {
I_VADDR = 0,
I_PID
};
char *const token[] = {
[I_VADDR] = "vaddr",
[I_PID] = "pid",
NULL
};
char *p = subopts;
char *subval;
char *svaddr;
char *spid;

while (*p != '\0' && !err) {
index = getsubopt(&p, token, &subval);
switch (index) {
case I_VADDR:
if (subval) {
svaddr = subval;
break;
}
fprintf(stderr,
"miss value for %s\n",
token[I_VADDR]);
err++;
continue;
case I_PID:
if (subval) {
spid = subval;
break;
}
fprintf(stderr,
"miss value for %s\n",
token[I_PID]);
err++;
continue;
default:
err++;
break;
}
}
if (err > 0) {
usage();
return 1;
}
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;
}
return 0;
}

/*
* The "SRAR DCU" test case failed on a CLX-AP server. It's root caused
* that the gcc v8.2.1 optimized out the access to the injected location.
*
* If keep "total" as a local, even mark it "volatile", the gcc v8.2.1
* still optimizes out the memory access. Therefore, move the "total" from
* being a local variable to a global to avoid such optimization.
*/
long total;

int main(int argc, char **argv)
{
unsigned long long virt, phys;
char *buf;
int c, i;
int iflag = 0, dflag = 0;
int kick = 1;
int pfa = 0;
pid_t pid;
const char *trigger = "./trigger_start";
const char *trigger_flag = "trigger";
int fd;
int count = 100;
char trigger_buf[16];
char answer[16];
time_t now;

if (argc <= 1) {
usage();
return 0;
}

pagesize = getpagesize();

while ((c = getopt_long(argc, argv, "a:dihk:p", opts, NULL)) != -1) {
switch (c) {
case 'a':
if (parse_addr_subopts(optarg, &virt, &pid) == 0) {
phys = vtop(virt, pid);
printf("physical address of (%d,0x%llx) = 0x%llx\n",
pid, virt, phys);
return 0;
}
return 1;
case 'd':
dflag = 1;
break;
case 'i':
iflag = 1;
break;
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 'p':
pfa = 1;
break;
case 'h':
default:
usage();
return 0;
}
}

/* The MAP_LOCKED flag should not be used here, because it will cause a failure
* in KVM mce-inject test. But to prevent the mapped buffer from being swapped out,
* the system under test should not run in a heavy load environment.
*/
buf = mmap(NULL, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_PRIVATE, -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 (pfa == 1)
pfa_helper(buf, pid, phys);

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--) {
fd = open(trigger, O_RDONLY);
if (fd < 0) {
sleep(1);
continue;
}
if (read(fd, trigger_buf, sizeof(trigger_buf)) > 0 &&
strstr(trigger_buf, trigger_flag)) {
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;
}
17 changes: 17 additions & 0 deletions KVM/qemu/ras.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
- ras:
no Windows
type = ras
virt_test_type = qemu
vm_accelerator = kvm
force_create_image = no
remove_image = no
start_vm = yes
kill_vm = yes
auto_cpu_model = "no"
cpu_model = host
test_dir = '/tmp'
source_file = 'victim.c'
exec_file = 'victim'
variants:
- srao:
error_type = "0x10"
Loading
Loading