blob: 9906a26d500c73d8a3b0490823074fb3ebab003e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Intel Corporation
* Author: Tony Luck
*/
#include "einj.h"
struct error_type error_types[] = {
{ 0x00000001, "Processor Correctable",},
{ 0x00000002, "Processor Uncorrectable non-fatal",},
{ 0x00000004, "Processor Uncorrectable fatal",},
{ 0x00000008, "Memory Correctable",},
{ 0x00000010, "Memory Uncorrectable non-fatal",},
{ 0x00000020, "Memory Uncorrectable fatal",},
{ 0x00000040, "PCI Express Correctable",},
{ 0x00000080, "PCI Express Uncorrectable non-fatal",},
{ 0x00000100, "PCI Express Uncorrectable fatal",},
{ 0x00000200, "Platform Correctabl",},
{ 0x00000400, "Platform Uncorrectable non-fatal",},
{ 0x00000800, "Platform Uncorrectable fatal",},
{ -1, NULL }
};
int check_errortype_available(char *file, unsigned long long val)
{
FILE *fp;
int ret = -1;
unsigned long long available_error_type;
if (strcmp(file, EINJ_ETYPE) != 0)
return 0;
fp = fopen(EINJ_ETYPE_AVAILABLE, "r");
if (!fp) {
fprintf(stderr, "%s: cannot open '%s'\n", progname, file);
exit(1);
}
while (fscanf(fp, "%llx%*[^\n]", &available_error_type) == 1) {
if (val == available_error_type) {
ret = 0;
break;
}
}
fclose(fp);
return ret;
}
void wfile(char *file, unsigned long long val)
{
FILE *fp;
if (check_errortype_available(file, val) != 0) {
fprintf(stderr, "%s: no support for error type: 0x%llx\n", progname, val);
exit(1);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
if (!strcmp(file, EINJ_FLAGS))
return;
#endif
fp = fopen(file, "w");
if (fp == NULL) {
fprintf(stderr, "%s: cannot open '%s'\n", progname, file);
exit(1);
}
fprintf(fp, "0x%llx\n", val);
if (fclose(fp) == EOF) {
fprintf(stderr, "%s: write error on '%s'\n", progname, file);
exit(1);
}
}
int is_einj_support(void)
{
if (access("/sys/firmware/acpi/tables/EINJ", R_OK) == -1) {
fprintf(stderr, "%s: Error injection not supported, check your BIOS settings\n", progname);
return 0;
}
if (access(EINJ_NOTRIGGER, R_OK|W_OK) == -1) {
fprintf(stderr, "%s: Is the einj.ko module loaded?\n", progname);
return 0;
}
return 1;
}
int is_privileged(void)
{
if (getuid() != 0) {
fprintf(stderr, "%s: must be root to run error injection tests\n", progname);
return 0;
}
return 1;
}
#if defined(__aarch64__)
#define DEFAULT_CACHE_LINE_SIZE 64
static size_t get_cache_line_size(void)
{
static size_t cachelinesize;
FILE *file = NULL;
if (cachelinesize)
return cachelinesize;
file = fopen("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", "r");
if (file && fscanf(file, "%zu", &cachelinesize) == 1)
fclose(file);
else
cachelinesize = DEFAULT_CACHE_LINE_SIZE;
return cachelinesize;
}
static void clear_cache(void *ptr, size_t size)
{
uintptr_t start = (uintptr_t)ptr;
uintptr_t end = start + size;
size_t cachelinesize;
if (!ptr || size == 0)
return;
cachelinesize = get_cache_line_size();
start &= ~(cachelinesize - 1);
while (start < end) {
asm volatile("dc civac, %0" : : "r"(start) : "memory");
start += cachelinesize;
}
asm volatile("dsb sy" : : : "memory");
sleep(1);
}
#else
static inline void clear_cache(void *ptr, size_t size) { }
#endif
void inject_mem_uc(unsigned long long addr, void *vaddr, int notrigger)
{
PRINT_INJECTING;
if (Sflag) {
vaddr = (void *)((long)vaddr & ~(pagesize - 1));
madvise(vaddr, pagesize, MADV_HWPOISON);
return;
}
wfile(EINJ_ETYPE, 0x10);
wfile(EINJ_ADDR, addr);
wfile(EINJ_MASK, ~0x0ul);
wfile(EINJ_FLAGS, 2);
wfile(EINJ_NOTRIGGER, notrigger);
wfile(EINJ_DOIT, 1);
clear_cache(vaddr, pagesize);
}
char *lookup_type_name(unsigned long long type)
{
struct error_type *t;
for (t = error_types; t->type_name; t++)
if (t->type == type)
return t->type_name;
fprintf(stderr, "%s: unknown type '%x'\n", progname, type);
exit(1);
}