-
Notifications
You must be signed in to change notification settings - Fork 5
/
sender.c
309 lines (262 loc) · 10.2 KB
/
sender.c
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#include "util.h"
/*
* Parses the arguments and flags of the program and initializes the struct config
* with those parameters (or the default ones if no custom flags are given).
*/
void init_config(struct config *config, int argc, char **argv) {
uint64_t pid = print_pid();
init_default(config, argc, argv);
if (config->channel == PrimeProbe) {
int L3_way_stride = ipow(2, LOG_CACHE_SETS_L3 + LOG_CACHE_LINESIZE);
uint64_t bsize = 8 * CACHE_WAYS_L3 * L3_way_stride;
// Allocate a buffer of the size of the LLC
// config->buffer = malloc((size_t) bsize);
config->buffer = allocate_buffer(bsize);
printf("buffer pointer addr %p\n", config->buffer);
// Initialize the buffer to be be the non-zero page
for (uint32_t i = 0; i < bsize; i += 64) {
*(config->buffer + i) = pid;
}
// Construct the addr_set by taking the addresses that have cache set index 0
uint32_t addr_set_size = 0;
for (int set_index = 0; set_index < CACHE_SETS_L3; set_index++) {
for (uint32_t line_index = 0; line_index < 8 * CACHE_WAYS_L3; line_index++) {
// a simple hash to shuffle the lines in physical address space
uint32_t stride_idx = (line_index * 167 + 13) % (8 * CACHE_WAYS_L3);
ADDR_PTR addr = (ADDR_PTR) (config->buffer + \
set_index * CACHE_LINESIZE + stride_idx * L3_way_stride);
// both of following function should work...L3 is a more restrict set
if (get_cache_slice_set_index(addr) == config->cache_region) {
// if (get_L3_cache_set_index(addr) == config->cache_region) {
append_string_to_linked_list(&config->addr_set, addr);
addr_set_size++;
}
}
}
printf("Found addr_set size of %u\n", addr_set_size);
}
if (config->channel == L1DPrimeProbe) {
int L1_way_stride = ipow(2, LOG_CACHE_SETS_L1 + LOG_CACHE_LINESIZE); // 4096
uint64_t bsize = 256 * CACHE_WAYS_L1 * L1_way_stride; // 64 * 8 * 4k = 2M
// Allocate a buffer twice the size of the L1 cache
config->buffer = allocate_buffer(bsize);
printf("buffer pointer addr %p\n", config->buffer);
// Initialize the buffer to be be the non-zero page
for (uint32_t i = 0; i < bsize; i += 64) {
*(config->buffer + i) = pid;
}
// Construct the addr_set by taking the addresses that have cache set index 0
// There will be at least one of such addresses in our buffer.
uint32_t addr_set_size = 0;
for (int i = 0; i < 256 * CACHE_WAYS_L1 * CACHE_SETS_L1; i++) {
ADDR_PTR addr = (ADDR_PTR) (config->buffer + CACHE_LINESIZE * i);
// both of following function should work...L3 is a more restrict set
if (get_cache_slice_set_index(addr) == config->cache_region) {
// if (get_L3_cache_set_index(addr) == config->cache_region) {
append_string_to_linked_list(&config->addr_set, addr);
addr_set_size++;
}
// restrict the probing set to CACHE_WAYS_L1 to aviod self eviction
if (addr_set_size >= 2 * (CACHE_WAYS_L1 + CACHE_WAYS_L2)) break;
}
printf("Found addr_set size of %u\n", addr_set_size);
}
if (config->channel == FlushReload) {
int inFile = open(config->shared_filename, O_RDONLY);
if (inFile == -1) {
fprintf(stderr, "ERROR: Failed to Open File\n");
exit(-1);
}
size_t size = 4096;
config->buffer = mmap(NULL, size, PROT_READ, MAP_SHARED, inFile, 0);
if (config->buffer == (void*) -1 ) {
fprintf(stderr, "ERROR: Failed to Map Address\n");
exit(-1);
}
ADDR_PTR addr = (ADDR_PTR) config->buffer + config->cache_region * 64;
append_string_to_linked_list(&config->addr_set, addr);
printf("File mapped at %p and monitoring line %lx\n", config->buffer, addr);
}
}
// sender function pointer
void (*send_bit)(bool, const struct config*);
void send_bit_fr(bool one, const struct config *config) {
uint64_t start_t = rdtsc();
if (one) {
ADDR_PTR addr = config->addr_set->addr;
while ((rdtsc() - start_t) < config->interval) {
clflush(addr);
}
} else {
start_t = rdtsc();
while (rdtsc() - start_t < config->interval) {}
}
}
/*
* Sends a bit to the receiver by repeatedly flushing the addresses of the addr_set
* for the clock length of config->interval when we are sending a one, or by doing nothing
* for the clock length of config->interval when we are sending a zero.
*/
void send_bit_pp(bool one, const struct config *config)
{
uint64_t start_t = get_time();
debug("time %lx\n", start_t);
if (one) {
// wait for receiver to prime the cache set
while (get_time() - start_t < config->prime_period) {}
// access
uint64_t access_count = 0;
struct Node *current = NULL;
uint64_t stopTime = start_t + config->prime_period + config->access_period;
// uint64_t stopTime = start_t + config->interval;
do {
current = config->addr_set;
while (current != NULL && current->next != NULL && get_time() < stopTime) {
// while (current != NULL && get_time() < stopTime) {
volatile uint64_t* addr1 = (uint64_t*) current->addr;
volatile uint64_t* addr2 = (uint64_t*) current->next->addr;
*addr1;
*addr2;
*addr1;
*addr2;
*addr1;
*addr2;
current = current->next;
access_count++;
}
} while (get_time() < stopTime);
debug("access count %lu time %lx\n", access_count, get_time() - start_t);
// wait for receiver to probe
while (get_time() - start_t < config->interval) {}
} else {
while (get_time() - start_t < config->interval) {}
}
}
uint8_t *generate_random_msg(uint32_t size) {
uint8_t *msg = (uint8_t *)malloc(sizeof(uint8_t) * size);
srand(time(NULL));
for(uint32_t i = 0; i < size; i++) {
int randomnum = rand();
msg[i] = (randomnum > RAND_MAX/2);
}
return msg;
}
void benchmark_send(struct config *config_p) {
FILE *senderSave = fopen("data/senderSave", "w+");
if (!senderSave) {
fprintf(stderr, "ERROR: cannot open file to save.\n"
"Check if data/ folder is created\n");
exit(-1);
}
uint32_t benchmarkSize = 8192;
uint8_t *randomMsg = generate_random_msg(benchmarkSize);
uint64_t start_t;
for (uint32_t i = 0; i < benchmarkSize; i++) {
// sync every 1024 bits
if ((i & 0x3ff) == 0) {
for (int j = 0; j < 10; j++) {
start_t = cc_sync();
send_bit(j % 2 == 0, config_p);
}
start_t = cc_sync();
send_bit(true, config_p);
start_t = cc_sync();
send_bit(true, config_p);
// Send the message bit by bit
debug("pilot signal sentt for round %u\r", i / 1024);
start_t = cc_sync();
}
send_bit(randomMsg[i], config_p);
}
if (randomMsg) {
for (uint32_t i = 0; i < benchmarkSize; i++) {
fprintf(senderSave, "%u %u\n", i, randomMsg[i]);
}
fclose(senderSave);
free(randomMsg);
}
}
int main(int argc, char **argv)
{
// Initialize config and local variables
struct config config;
init_config(&config, argc, argv);
if (config.channel == PrimeProbe || config.channel == L1DPrimeProbe) {
send_bit = send_bit_pp;
}
else if (config.channel == FlushReload) {
send_bit = send_bit_fr;
}
if (config.benchmark_mode) {
benchmark_send(&config);
exit(0);
}
uint64_t start_t, end_t;
int sending = 1;
printf("Please type a message (exit to stop).\n");
#if 0
char all_one_msg[129];
for (uint32_t i = 0; i < 120; i++)
all_one_msg[i] = '1';
for (uint32_t i = 120; i < 128; i++)
all_one_msg[i] = '1';
all_one_msg[128] = '\0';
#endif
while (sending) {
#if 0
char *msg = all_one_msg;
#else
// Get a message to send from the user
printf("< ");
char text_buf[128];
fgets(text_buf, sizeof(text_buf), stdin);
if (strcmp(text_buf, "exit\n") == 0) {
sending = 0;
}
char *msg = string_to_binary(text_buf);
#endif
// If we are in benchmark mode, start measuring the time
if (config.benchmark_mode) {
start_t = get_time();
}
// Send a '10101011' byte to let the receiver detect that
// I am about to send a start string and cc_sync
size_t msg_len = strlen(msg);
// cc_sync on clock edge
uint64_t start_t = cc_sync();
for (int i = 0; i < 10; i++) {
start_t = cc_sync();
// send_bit(i % 2 == 0, &config, start_t);
send_bit(i % 2 == 0, &config);
}
start_t = cc_sync();
// send_bit(true, &config, start_t);
send_bit(true, &config);
start_t = cc_sync();
// send_bit(true, &config, start_t);
send_bit(true, &config);
// Send the message bit by bit
start_t = cc_sync();
// TODO: for longer messages it is recommended to re-sync every X bits
for (uint32_t ind = 0; ind < msg_len; ind++) {
if (msg[ind] == '0') {
// send_bit(false, &config, start_t);
send_bit(false, &config);
} else {
// send_bit(true, &config, start_t);
send_bit(true, &config);
}
start_t += config.interval;
}
printf("message %s sent\n", msg);
// If we are in benchmark mode, finish measuring the
// time and print the bit rate.
// if (config.benchmark_mode) {
// end_t = get_time();
// printf("Bitrate: %.2f Bytes/second\n",
// ((double) strlen(text_buf)) / ((double) (end_t - start_t) / CLOCKS_PER_SEC));
// }
}
printf("Sender finished\n");
return 0;
}