blob: 706598cac2bf0bd2e36cd6893b77651116a65abe [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013 Intel Corporation
* Authors: Tony Luck
*
* This software may be redistributed and/or modified under the terms of
* the GNU General Public License ("GPL") version 2 only as published by the
* Free Software Foundation.
*/
/*
* Set up to get zapped by a machine check (injected elsewhere)
* recovery function reports physical address of new page - so
* we can inject to that and repeat over and over.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
extern unsigned long long vtop(unsigned long long addr, pid_t pid);
static int pagesize;
/*
* Older glibc headers don't have the si_addr_lsb field in the siginfo_t
* structure ... ugly hack to get it
*/
struct morebits {
void *addr;
short lsb;
};
char *buf;
unsigned long long phys;
int tried_recovery;
pid_t pid;
/*
* "Recover" from the error by allocating a new page and mapping
* it at the same virtual address as the page we lost. Fill with
* the same (trivial) contents.
*/
void recover(int sig, siginfo_t *si, void *v)
{
struct morebits *m = (struct morebits *)&si->si_addr;
char *newbuf;
tried_recovery = 1;
printf("recover: sig=%d si=%p v=%p\n", sig, si, v);
printf("Platform memory error at %p\n", si->si_addr);
printf("addr = %p lsb=%d\n", m->addr, m->lsb);
newbuf = mmap(buf, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (newbuf == MAP_FAILED) {
fprintf(stderr, "Can't get a single page of memory!\n");
exit(1);
}
if (newbuf != buf) {
fprintf(stderr, "Could not allocate at original virtual address\n");
exit(1);
}
buf = newbuf;
memset(buf, '*', pagesize);
phys = vtop((unsigned long long)buf, pid);
printf("Recovery allocated new page at physical 0x%llx\n", phys);
}
struct sigaction recover_act = {
.sa_sigaction = recover,
.sa_flags = SA_SIGINFO,
};
int consume_poison(void)
{
return *(volatile char *)buf;
}
int main(int argc, char **argv)
{
int i;
char reply[100];
pagesize = getpagesize();
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;
}
pid = getpid();
memset(buf, '*', pagesize);
phys = vtop((unsigned long long)buf, pid);
printf("vtop(%llx) = %llx\n", (unsigned long long)buf, phys);
printf("Use /sys/kernel/debug/apei/einj/... to inject\n");
printf("Then press <ENTER> to access:");
fflush(stdout);
sigaction(SIGBUS, &recover_act, NULL);
if (fgets(reply, sizeof reply, stdin) == NULL)
printf("\nI said 'Press <ENTER>'. Continuing anyway\n");
i = consume_poison();
if (tried_recovery == 0) {
fprintf(stderr, "%s: didn't trigger error\n", argv[0]);
return 1;
}
if (i != '*') {
fprintf(stderr, "%s: triggered error, but got bad data\n", argv[0]);
return 1;
}
printf("Successful recovery\n");
return 0;
}