aboutsummaryrefslogtreecommitdiffstats
path: root/memattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'memattr.c')
-rw-r--r--memattr.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/memattr.c b/memattr.c
new file mode 100644
index 0000000..1465c73
--- /dev/null
+++ b/memattr.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2022 Alibaba Corporation
+ * Author: Shuai Xue
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <setjmp.h>
+#include <signal.h>
+#define _GNU_SOURCE 1
+#define __USE_GNU 1
+#include <sched.h>
+#include <errno.h>
+#include <sys/syscall.h>
+#include <linux/futex.h>
+
+typedef struct
+{
+ int num;
+ /*
+ * Any unaligned access to memory region with any Device memory type
+ * attribute generates an Alignment fault. Thus, add a safe padding.
+ */
+ char pad[3];
+ long long int paddr;
+} mpgprot_drv_ctx;
+
+extern long long vtop(long long);
+#define DEV_NAME "/dev/pgprot_drv"
+#define PAGE_SHIFT 12
+static mpgprot_drv_ctx *ctx = NULL;
+static char *progname;
+
+#define EINJ_ETYPE "/sys/kernel/debug/apei/einj/error_type"
+#define EINJ_ETYPE_AVAILABLE "/sys/kernel/debug/apei/einj/available_error_type"
+#define EINJ_ADDR "/sys/kernel/debug/apei/einj/param1"
+#define EINJ_MASK "/sys/kernel/debug/apei/einj/param2"
+#define EINJ_APIC "/sys/kernel/debug/apei/einj/param3"
+#define EINJ_FLAGS "/sys/kernel/debug/apei/einj/flags"
+#define EINJ_NOTRIGGER "/sys/kernel/debug/apei/einj/notrigger"
+#define EINJ_DOIT "/sys/kernel/debug/apei/einj/error_inject"
+#define EINJ_VENDOR "/sys/kernel/debug/apei/einj/vendor"
+
+static int is_privileged(void)
+{
+ if (getuid() != 0) {
+ fprintf(stderr, "%s: must be root to run error injection tests\n", progname);
+ return 0;
+ }
+ return 1;
+}
+
+static void wfile(char *file, unsigned long long val)
+{
+ FILE *fp;
+
+ 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);
+ }
+}
+
+static void inject_uc(unsigned long long addr, void *vaddr, int notrigger)
+{
+ wfile(EINJ_ETYPE, 0x20);
+ wfile(EINJ_ADDR, addr);
+ wfile(EINJ_MASK, ~0x0ul);
+ wfile(EINJ_FLAGS, 2);
+ wfile(EINJ_NOTRIGGER, notrigger);
+ wfile(EINJ_DOIT, 1);
+}
+
+int trigger_write(char *addr)
+{
+ addr[0] = 0x69;
+ return 0;
+}
+
+#define ONE p = (char **)*p;
+#define FIVE ONE ONE ONE ONE ONE
+#define TEN FIVE FIVE
+#define FIFTY TEN TEN TEN TEN TEN
+#define HUNDRED FIFTY FIFTY
+
+static int poison = 0;
+static int bench = 0;
+int main(int argc, char *argv[])
+{
+ int kfd, c;
+ long long paddr;
+ void *vaddr;
+
+ progname = argv[0];
+ if (!is_privileged())
+ exit(1);
+ while ((c = getopt(argc, argv, "pb")) != -1)
+ switch (c)
+ {
+ case 'p':
+ poison = 1;
+ break;
+ case 'b':
+ bench = 1;
+ break;
+ }
+
+ kfd = open(DEV_NAME, O_RDWR | O_NDELAY);
+ if (kfd < 0)
+ {
+ printf("open file %s error: Is the pgprot_drv.ko module loaded?\n", DEV_NAME);
+ return -1;
+ }
+
+ vaddr = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, kfd, 0);
+ if (vaddr == MAP_FAILED)
+ {
+ printf("allocate mem fail %d!!!\n", 4096);
+ exit(1);
+ }
+
+ ctx = (mpgprot_drv_ctx *)vaddr;
+ printf("check ctx: vaddr = %p, num %d, paddr %llx\n", vaddr, ctx->num, ctx->paddr);
+
+ if (bench)
+ {
+ struct timeval tv1, tv2;
+ int memsize = 4096;
+ int stride = 128;
+ int size = memsize / stride;
+ unsigned *indices = malloc(size * sizeof(int));
+ int i, count, tmp;
+ struct timezone tz;
+ char *mem = vaddr;
+ unsigned long sec, usec;
+
+ for (i = 0; i < size; i++)
+ indices[i] = i;
+
+ // trick 2: fill mem with pointer references
+ for (i = 0; i < size - 1; i++)
+ *(char **)&mem[indices[i] * stride] = (char *)&mem[indices[i + 1] * stride];
+ *(char **)&mem[indices[size - 1] * stride] = (char *)&mem[indices[0] * stride];
+
+ register char **p = (char **)mem;
+ tmp = count / 100;
+
+ gettimeofday(&tv1, &tz);
+ for (i = 0; i < tmp; ++i)
+ {
+ HUNDRED;
+ }
+ gettimeofday(&tv2, &tz);
+
+ if (tv2.tv_usec < tv1.tv_usec)
+ {
+ usec = 1000000 + tv2.tv_usec - tv1.tv_usec;
+ sec = tv2.tv_sec - tv1.tv_sec - 1;
+ }
+ else
+ {
+ usec = tv2.tv_usec - tv1.tv_usec;
+ sec = tv2.tv_sec - tv1.tv_sec;
+ }
+
+ /* touch pointer p to prevent compiler optimization */
+ char **touch = p;
+ printf("Buffer size: %ld KB, stride %d, time %d.%06d s, latency %.2f ns\n",
+ memsize / 1024, stride, sec, usec, (sec * 1000000 + usec) * 1000.0 / (tmp * 100));
+ }
+
+ if (poison)
+ {
+ /* pick from kernel */
+ long long int paddr = ctx->paddr;
+ printf("vaddr = %p paddr = %llx\n", vaddr, paddr);
+ inject_uc(paddr, vaddr, 1);
+ sleep(3);
+ trigger_write(vaddr);
+ }
+
+ munmap(ctx, 4096);
+
+ return 0;
+}