blob: 88d5abdf073cdd22fe4efe2bb32715f818820429 [file] [log] [blame]
Puthikorn Voravootivat84d5afc2020-01-14 14:50:19 -08001
2// This is required on Mac OS X for getting PRI* macros #defined.
3#define __STDC_FORMAT_MACROS
4
5#include <assert.h>
6#include <fcntl.h>
7#include <inttypes.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/mount.h>
13#include <sys/mman.h>
14#include <sys/time.h>
15#include <sys/stat.h>
16#include <sys/wait.h>
17#include <time.h>
18#include <unistd.h>
19
20const size_t mem_size = (1 << 30)/4;
21const int toggles = 540000;
22
23const uint64_t pte_pattern = 0xb7d02580000003C5;
24char *g_mem;
25
26// Pick a random page in the memory region.
27uint8_t* pick_addr(uint8_t* area_base, uint64_t mem_size) {
28 size_t offset = (rand() << 12) % mem_size;
29 return area_base + offset;
30}
31
32class Timer {
33 struct timeval start_time_;
34
35 public:
36 Timer() {
37 // Note that we use gettimeofday() (with microsecond resolution)
38 // rather than clock_gettime() (with nanosecond resolution) so
39 // that this works on Mac OS X, because OS X doesn't provide
40 // clock_gettime() and we don't really need nanosecond resolution.
41 int rc = gettimeofday(&start_time_, NULL);
42 assert(rc == 0);
43 }
44
45 double get_diff() {
46 struct timeval end_time;
47 int rc = gettimeofday(&end_time, NULL);
48 assert(rc == 0);
49 return (end_time.tv_sec - start_time_.tv_sec
50 + (double) (end_time.tv_usec - start_time_.tv_usec) / 1e6);
51 }
52
53 void print_iters(uint64_t iterations) {
54 double total_time = get_diff();
55 double iter_time = total_time / iterations;
56 printf("%.3fns,%g,%" PRIu64,
57 iter_time * 1e9, total_time, iterations);
58 }
59};
60
61uint64_t get_physical_address(uint64_t virtual_address) {
62 int fd = open("/proc/self/pagemap", O_RDONLY);
63 assert(fd >=0);
64
65 off_t pos = lseek(fd, (virtual_address / 0x1000) * 8, SEEK_SET);
66 assert(pos >= 0);
67 uint64_t value;
68 int got = read(fd, &value, 8);
69
70 close(fd);
71 assert(got == 8);
72 return ((value & ((1ULL << 54)-1)) * 0x1000) |
73 (virtual_address & 0xFFF);
74}
75
76static int sigint = 0;
77static void sigint_handler(int signum) {
78 sigint = 1;
79}
80static int sigquit = 0;
81static void sigquit_handler(int signum) {
82 sigint = 1;
83 sigquit = 1;
84}
85
86static void toggle(int iterations, int addr_count) {
87 Timer t;
88 for (int j = 0; j < iterations; j++) {
89 volatile uint32_t *addrs[addr_count];
90 for (int a = 0; a < addr_count; a++) {
91 addrs[a] = (uint32_t *) pick_addr((uint8_t*)g_mem, mem_size);
92 //printf(" Hammering virtual address %16lx, physical address %16lx\n",
93 //(uint64_t)addrs[a], get_physical_address((uint64_t)addrs[a]));
94 }
95
96 // TODO(wad) try the approach from github.com/CMU-SAFARI/rowhammer
97 // as it may be faster.
98 uint32_t sum = 0;
99 for (int i = 0; i < toggles; i++) {
100 for (int a = 0; a < addr_count; a++)
101 sum += *addrs[a] + 1;
102 for (int a = 0; a < addr_count; a++)
103 asm volatile("clflush (%0)" : : "r" (addrs[a]) : "memory");
104 }
105 if (sigint) {
106 sigint = 0;
107 break;
108 }
109 }
110 t.print_iters(iterations * addr_count * toggles);
111}
112
113void main_prog() {
114 g_mem = (char *) mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
115 MAP_ANON | MAP_PRIVATE, -1, 0);
116 assert(g_mem != MAP_FAILED);
117
118 // printf("clear\n");
119
120 //memset(g_mem, 0xff, mem_size);
121
122 // Fill memory with pattern that resembles page tables entries.
123 // c5 03 00 00 80 25 d0 b7
124 for (uint32_t i = 0; i < mem_size; i += 8) {
125 uint64_t* ptr = (uint64_t*)(&g_mem[i]);
126 *ptr = pte_pattern;
127 }
128
129 Timer t;
130 int iter = 0;
131 for (;;) {
132 printf("%d,%.2fs,", iter++, t.get_diff());
133 fflush(stdout);
134 toggle(3000, 4);
135
136 Timer check_timer;
137 // printf("check\n");
138 uint64_t *end = (uint64_t *) (g_mem + mem_size);
139 uint64_t *ptr;
140 int errors = 0;
141 for (ptr = (uint64_t *) g_mem; ptr < end; ptr++) {
142 uint64_t got = *ptr;
143 if (got != pte_pattern) {
144 fprintf(stderr, "error at %p (%16lx): got 0x%" PRIx64 "\n", ptr, get_physical_address((uint64_t)ptr), got);
145 fprintf(stderr, "after %.2fs\n", t.get_diff());
146 errors++;
147 }
148 }
149 printf(",%fs", check_timer.get_diff());
150 fflush(stdout);
151 if (errors) {
152 printf(",%d\n", errors);
153 fflush(stdout);
154 exit(1);
155 }
156 printf(",0\n");
157 fflush(stdout);
158 if (sigquit)
159 break;
160 }
161}
162
163
164int main(int argc, char **argv) {
165 // In case we are running as PID 1, we fork() a subprocess to run
166 // the test in. Otherwise, if process 1 exits or crashes, this will
167 // cause a kernel panic (which can cause a reboot or just obscure
168 // log output and prevent console scrollback from working).
169 // Output should look like:
170 // [iteration #],[relative start offset in sec],[itertime in ns],[total time in s],[iteration count],[check time in s],[error count]
171 signal(SIGINT, &sigint_handler);
172 signal(SIGQUIT, &sigquit_handler);
173 int pid = fork();
174 if (pid == 0) {
175 main_prog();
176 _exit(1);
177 }
178
179 int status;
180 int sec = argc == 2 ? atoi(argv[1]) : 60*60;
181 while (sec--) {
182 if (sigint) {
183 sigint = 0;
184 kill(pid, SIGINT);
185 if (sigquit)
186 break;
187 }
188 if (waitpid(pid, &status, WNOHANG) == pid) {
189 printf("** exited with status %i (0x%x)\n", status, status);
190 exit(status);
191 }
192 sleep(1);
193 }
194 kill(pid, SIGQUIT);
195 sleep(1);
196 kill(pid, SIGKILL);
197 // Let init reap.
198 return 0;
199}