1 
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 
20 const size_t mem_size = (1 << 30)/4;
21 const int toggles = 540000;
22 
23 const uint64_t pte_pattern = 0xb7d02580000003C5;
24 char *g_mem;
25 
26 // Pick a random page in the memory region.
pick_addr(uint8_t * area_base,uint64_t mem_size)27 uint8_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 
32 class Timer {
33   struct timeval start_time_;
34 
35  public:
Timer()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 
get_diff()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 
print_iters(uint64_t iterations)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 
get_physical_address(uint64_t virtual_address)61 uint64_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 
76 static int sigint = 0;
sigint_handler(int signum)77 static void sigint_handler(int signum) {
78   sigint = 1;
79 }
80 static int sigquit = 0;
sigquit_handler(int signum)81 static void sigquit_handler(int signum) {
82   sigint = 1;
83   sigquit = 1;
84 }
85 
toggle(int iterations,int addr_count)86 static 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((uint64_t) iterations * addr_count * toggles);
111 }
112 
main_prog()113 void 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 
main(int argc,char ** argv)164 int 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 }
200