aboutsummaryrefslogtreecommitdiffstats
path: root/memattr.c
blob: 7464a69e9323fb6d77097accba9fd0b07add7e8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * 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 <fcntl.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 <sys/syscall.h>
#include <linux/futex.h>
#include "einj.h"

char *progname;
long pagesize;
int Sflag;

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 unsigned long long vtop(unsigned long long addr, pid_t pid);
#define DEV_NAME "/dev/pgprot_drv"
#define PAGE_SHIFT 12
static mpgprot_drv_ctx *ctx = NULL;

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_mem_uc(paddr, vaddr, 1);
		sleep(3);
		trigger_write(vaddr);
	}

	munmap(ctx, 4096);

	return 0;
}