initial commit
This commit is contained in:
commit
82af0dce4a
11
README.md
Normal file
11
README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# TRRespass
|
||||
|
||||
### ./drama
|
||||
|
||||
Inside the `drama` folder you can find a tool that helps you reverse engineer the DRAM memory mappings used by the memory controller.
|
||||
Read the README in the folder for more details
|
||||
|
||||
### ./hammersuite
|
||||
|
||||
Inside the `hammersuite` folder you can find the fuzzer we used.
|
||||
Again, read the README in the folder for more details
|
58
drama/Makefile
Normal file
58
drama/Makefile
Normal file
@ -0,0 +1,58 @@
|
||||
SDIR=src
|
||||
IDIR=$(SDIR)/include
|
||||
LDIR=lib
|
||||
BUILD=obj
|
||||
ODIR=src/.obj
|
||||
|
||||
CFLAGS=-I$(IDIR) #-ggdb
|
||||
# CXX=g++
|
||||
LDFLAGS=
|
||||
|
||||
OUT=tester
|
||||
|
||||
LDEPS=
|
||||
|
||||
GB_PAGE=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
|
||||
HUGEPAGE=/mnt/huge
|
||||
|
||||
all: $(OUT)
|
||||
.PHONY: clean
|
||||
|
||||
|
||||
SOURCES := $(wildcard $(SDIR)/*.c)
|
||||
OBJECTS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(SOURCES))
|
||||
|
||||
|
||||
$(ODIR)/%.o: $(SDIR)/%.c
|
||||
mkdir -p $(ODIR)
|
||||
$(CXX) -o $@ -c $< $(CFLAGS) $(LDFLAGS) $(LDEPS)
|
||||
|
||||
|
||||
$(OUT): $(OBJECTS)
|
||||
mkdir -p $(BUILD)
|
||||
$(CXX) -o $(BUILD)/$@ $^ $(CFLAGS) $(LDFLAGS) $(LDEPS)
|
||||
chmod +x $(BUILD)/$@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
rm -rf $(ODIR)
|
||||
|
||||
setup:
|
||||
echo "Mounting hugetlbfs"
|
||||
echo 1 | sudo tee -a $(GB_PAGE)
|
||||
@if ! [ -d $(HUGEPAGE) ]; then\
|
||||
sudo mkdir $(HUGEPAGE);\
|
||||
sudo mount -t hugetlbfs -o pagesize=1G none $(HUGEPAGE);\
|
||||
sudo chown pit:pit $(HUGEPAGE);\
|
||||
fi
|
||||
|
||||
|
||||
teardown:
|
||||
@if [ -d $(HUGEPAGE) ]; then\
|
||||
sudo umount -f $(HUGEPAGE);\
|
||||
sudo rm -r $(HUGEPAGE);\
|
||||
fi
|
||||
echo 0 | sudo tee -a $(GB_PAGE)
|
||||
|
||||
run:
|
||||
sudo $(BUILD)/$(OUT)
|
37
drama/README.md
Normal file
37
drama/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# DRAMA
|
||||
|
||||
This tool can be used to reverse engineer the DRAM mapping functions of the test system.
|
||||
It carries out two main tasks:
|
||||
|
||||
- Recovering the bank conflicts functions
|
||||
- Recovering the row address function
|
||||
|
||||
The tool is a bit hackish since it makes some strong assumptions on the bits used for different purposes.
|
||||
For instance it assumes that the bits from the physical address used to select the row are among the high bits.
|
||||
This seems to be a valid assumption on all the Intel consumer platforms we tested.
|
||||
However on server platforms and AMD ryzen machines it doesn't yield great results.
|
||||
Once recovered the functions these can be used in `hammersuite` as explained in the the other README.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]
|
||||
-h = this help message
|
||||
-s sets = number of expected sets (default: 32)
|
||||
-r rounds = number of rounds per tuple (default: 1000)
|
||||
-t threshold = time threshold for conflicts (default: 340)
|
||||
-o o_file = output file for mem profiling (default: access.csv)
|
||||
--mem mem_size = allocation size (default: 5368709120)
|
||||
-v = verbose
|
||||
```
|
||||
|
||||
We recommend running it as verbose (`-v`) to get more insights on what's going on.
|
||||
Otherwise it will simply output to stdout first the bank conflicts functions and then the row address mask.
|
||||
|
||||
**Number of sets:**
|
||||
|
||||
- The number of expected sets is defined by the memory configuration. For instance in a common dual-rank, single-channel configuration you would expect 32 banks (i.e., sets) in total. You can pass any value you want to the script. If this value is unknown 16 is usually a safe bet.
|
||||
|
||||
**Time threshold:**
|
||||
|
||||
- You can identify the time threshold by running the tool the first time with `-o` and plotting the results with the histogram.py script available in the repo. Once you know the threshold you can dinamycally pass it to the binary.
|
6
drama/hugepage.sh
Executable file
6
drama/hugepage.sh
Executable file
@ -0,0 +1,6 @@
|
||||
gbFile="/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages"
|
||||
|
||||
mkdir /mnt/huge
|
||||
mount -t hugetlbfs -o pagesize=1G none /mnt/huge
|
||||
su -c 'echo 1 >'$gbFile
|
||||
cat $gbFile
|
14
drama/src/include/rev-mc.h
Normal file
14
drama/src/include/rev-mc.h
Normal file
@ -0,0 +1,14 @@
|
||||
#include "utils.h"
|
||||
#include "unistd.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
char* v_addr;
|
||||
uint64_t p_addr;
|
||||
} addr_tuple;
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Functions
|
||||
|
||||
|
||||
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char* o_file, uint64_t flags);
|
95
drama/src/include/utils.h
Normal file
95
drama/src/include/utils.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define BIT(x) (1ULL<<(x))
|
||||
#define KB(x) ((x)<<10ULL)
|
||||
#define MB(x) ((x)<<20ULL)
|
||||
#define GB(x) ((x)<<30ULL)
|
||||
#define CL_SHIFT 6
|
||||
#define CL_SIZE 64
|
||||
|
||||
|
||||
#define F_CLEAR 0L
|
||||
#define F_VERBOSE BIT(0)
|
||||
#define F_EXPORT BIT(1)
|
||||
|
||||
#define MEM_SHIFT (30L)
|
||||
#define MEM_MASK 0b11111ULL << MEM_SHIFT
|
||||
#define F_ALLOC_HUGE BIT(MEM_SHIFT)
|
||||
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT(MEM_SHIFT+1)
|
||||
#define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT(MEM_SHIFT+2)
|
||||
#define F_POPULATE BIT(MEM_SHIFT+3)
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Static functions
|
||||
|
||||
static inline __attribute__((always_inline)) void clflush(volatile void *p)
|
||||
{
|
||||
asm volatile("clflush (%0)\n"
|
||||
:: "r" (p) : "memory");
|
||||
}
|
||||
|
||||
|
||||
static inline __attribute__((always_inline)) void mfence()
|
||||
{
|
||||
asm volatile ("mfence" : : : "memory");
|
||||
}
|
||||
|
||||
|
||||
static inline __attribute__((always_inline)) void lfence()
|
||||
{
|
||||
asm volatile ("lfence" : : : "memory");
|
||||
}
|
||||
|
||||
|
||||
static inline __attribute__((always_inline)) uint64_t rdtscp(void)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
asm volatile("rdtscp\n"
|
||||
: "=a" (lo), "=d" (hi)
|
||||
:: "%rcx");
|
||||
return (hi << 32) | lo;
|
||||
}
|
||||
|
||||
|
||||
static inline __attribute__((always_inline)) uint64_t rdtsc(void)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
asm volatile("rdtsc\n"
|
||||
: "=a" (lo), "=d" (hi)
|
||||
:: "%rcx");
|
||||
return (hi << 32) | lo;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Memory alloc
|
||||
|
||||
|
||||
typedef struct {
|
||||
char* buffer;
|
||||
uint64_t size;
|
||||
uint64_t flags;
|
||||
} mem_buff_t;
|
||||
|
||||
|
||||
int alloc_buffer(mem_buff_t* mem);
|
||||
|
||||
int free_buffer(mem_buff_t* mem);
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Helpers
|
||||
int gt(const void * a, const void * b);
|
||||
|
||||
double mean(uint64_t* vals, size_t size);
|
||||
|
||||
uint64_t median(uint64_t* vals, size_t size);
|
||||
|
||||
char* bit_string(uint64_t val);
|
||||
|
134
drama/src/main.c
Normal file
134
drama/src/main.c
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
#include "stdio.h"
|
||||
// #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "rev-mc.h"
|
||||
|
||||
|
||||
#define SETS_std (2*16) // 1rk-1ch
|
||||
#define ROUNDS_std 1000
|
||||
#define THRESHOLD_std 340
|
||||
#define MEM_SIZE_std GB(5L)
|
||||
#define O_FILE_std "access.csv"
|
||||
|
||||
|
||||
#define FIELDS "base,probe,rounds,time"
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
// GLOBALS
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
void print_usage() {
|
||||
fprintf(stderr, "[ LOG ] - Usage ./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]\n");
|
||||
fprintf(stderr, " -h = this help message\n");
|
||||
fprintf(stderr, " -s sets = number of expected sets (default: %d)\n", SETS_std);
|
||||
fprintf(stderr, " -r rounds = number of rounds per tuple (default: %d)\n", ROUNDS_std);
|
||||
fprintf(stderr, " -t threshold = time threshold for conflicts (default: %d)\n", THRESHOLD_std);
|
||||
fprintf(stderr, " -o o_file = output file for mem profiling (default: %s)\n", O_FILE_std);
|
||||
fprintf(stderr, " --mem mem_size = allocation size (default: %ld)\n", (uint64_t) MEM_SIZE_std);
|
||||
fprintf(stderr, " -v = verbose\n\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
uint64_t flags = 0ULL;
|
||||
size_t sets_cnt = SETS_std;
|
||||
size_t rounds = ROUNDS_std;
|
||||
size_t m_size = MEM_SIZE_std;
|
||||
size_t threshold = THRESHOLD_std;
|
||||
char* o_file = (char*) O_FILE_std;
|
||||
|
||||
flags |= F_POPULATE;
|
||||
|
||||
if(geteuid() != 0) {
|
||||
fprintf(stderr, "[ERROR] - You need to run as root to access pagemap!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int this_option_optind = optind ? optind : 1;
|
||||
int option_index = 0;
|
||||
static struct option long_options[] =
|
||||
{
|
||||
/* These options set a flag. */
|
||||
{"mem", required_argument, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int arg = getopt_long (argc, argv, "o:s:r:t:hv",
|
||||
long_options, &option_index);
|
||||
|
||||
if (arg == -1)
|
||||
break;
|
||||
|
||||
switch(arg) {
|
||||
case 0:
|
||||
switch (option_index){
|
||||
case 0:
|
||||
m_size = atoi(optarg); // TODO proper parsing of this
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
o_file = (char*) malloc(sizeof(char)*strlen(optarg));
|
||||
strncpy(o_file, optarg, strlen(optarg));
|
||||
flags |= F_EXPORT;
|
||||
break;
|
||||
case 's':
|
||||
sets_cnt = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
rounds = atoi(optarg);
|
||||
break;
|
||||
case 't':
|
||||
threshold = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
flags |= F_VERBOSE;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_usage();
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
rev_mc(sets_cnt, threshold, rounds, m_size, o_file, flags);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*I'm refactoring the set struct to add also timing in there. */
|
501
drama/src/rev-mc.c
Normal file
501
drama/src/rev-mc.c
Normal file
@ -0,0 +1,501 @@
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
|
||||
#include "rev-mc.h"
|
||||
|
||||
#define BOOL_XOR(a,b) ((a) != (b))
|
||||
#define O_HEADER "base,probe,time\n"
|
||||
#define ALIGN_TO(X, Y) ((X) & (~((1LL<<(Y))-1LL))) // Mask out the lower Y bits
|
||||
#define LS_BITMASK(X) ((1LL<<(X))-1LL) // Mask only the lower X bits
|
||||
|
||||
#define SET_SIZE 40 // elements per set
|
||||
#define VALID_THRESH 0.75f
|
||||
#define SET_THRESH 0.95f
|
||||
#define BITSET_SIZE 256 // bitset used to exploit bitwise operations
|
||||
#define ROW_SET_CNT 5
|
||||
|
||||
// from https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c
|
||||
#define verbose_printerr(fmt, ...) \
|
||||
do { if (flags & F_VERBOSE) { fprintf(stderr, fmt, ##__VA_ARGS__); } } while(0)
|
||||
|
||||
|
||||
|
||||
typedef std::vector<addr_tuple> set_t;
|
||||
|
||||
//-------------------------------------------
|
||||
bool is_in(char* val, std::vector<char*> arr);
|
||||
bool found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size);
|
||||
void filter_sets(std::vector<set_t>& sets, size_t set_size);
|
||||
void print_sets(std::vector<set_t> sets);
|
||||
void verify_sets(std::vector<set_t>& sets, uint64_t threshold, size_t rounds);
|
||||
|
||||
//-------------------------------------------
|
||||
uint64_t time_tuple(volatile char* a1, volatile char* a2, size_t rounds) {
|
||||
|
||||
uint64_t* time_vals = (uint64_t*) calloc(rounds, sizeof(uint64_t));
|
||||
uint64_t t0;
|
||||
sched_yield();
|
||||
for (size_t i = 0; i < rounds; i++) {
|
||||
mfence();
|
||||
t0 = rdtscp();
|
||||
*a1;
|
||||
*a2;
|
||||
time_vals[i] = rdtscp() - t0;
|
||||
lfence();
|
||||
clflush(a1);
|
||||
clflush(a2);
|
||||
|
||||
}
|
||||
|
||||
uint64_t mdn = median(time_vals, rounds);
|
||||
free(time_vals);
|
||||
return mdn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
char* get_rnd_addr(char* base, size_t m_size, size_t align) {
|
||||
return (char*) ALIGN_TO((uint64_t) base, (uint64_t) align) + ALIGN_TO(rand() % m_size, (uint64_t) align);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
uint64_t get_pfn(uint64_t entry) {
|
||||
return ((entry) & 0x3fffffffffffff);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
uint64_t get_phys_addr(uint64_t v_addr)
|
||||
{
|
||||
uint64_t entry;
|
||||
uint64_t offset = (v_addr/4096) * sizeof(entry);
|
||||
uint64_t pfn;
|
||||
int fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
assert(fd >= 0);
|
||||
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
|
||||
close(fd);
|
||||
assert(bytes_read == 8);
|
||||
assert(entry & (1ULL << 63));
|
||||
pfn = get_pfn(entry);
|
||||
assert(pfn != 0);
|
||||
return (pfn*4096) | (v_addr & 4095);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
addr_tuple gen_addr_tuple(char* v_addr) {
|
||||
return (addr_tuple) { v_addr, get_phys_addr((uint64_t) v_addr)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
// https://www.cs.umd.edu/~gasarch/TOPICS/factoring/fastgauss.pdf
|
||||
// gaussian elimination in GF2
|
||||
|
||||
std::vector<uint64_t> reduce_masks(std::vector<uint64_t> masks) {
|
||||
|
||||
size_t height, width, height_t, width_t;
|
||||
|
||||
height = masks.size();
|
||||
width = 0;
|
||||
for (auto m:masks) {
|
||||
uint64_t max_one = 64 - __builtin_clzl(m);
|
||||
width = (max_one > width)? max_one:width;
|
||||
}
|
||||
|
||||
height_t = width;
|
||||
width_t = height;
|
||||
|
||||
std::vector<std::vector<bool>> mtx(height, std::vector<bool>(width));
|
||||
std::vector<std::vector<bool>> mtx_t(height_t, std::vector<bool>(width_t));
|
||||
std::vector<uint64_t> filtered_masks;
|
||||
|
||||
for (size_t i =0; i<height;i++) {
|
||||
for (size_t j=0; j<width; j++) {
|
||||
mtx[i][width - j - 1] = (masks[i] & (1ULL<<(j)));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i =0; i<height;i++) {
|
||||
for (size_t j=0; j<width; j++) {
|
||||
mtx_t[j][i] = mtx[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
int64_t pvt_col = 0;
|
||||
|
||||
while (pvt_col < width_t) {
|
||||
for (uint64_t row = 0; row < height_t; row++) {
|
||||
if (mtx_t[row][pvt_col]) {
|
||||
filtered_masks.push_back(masks[pvt_col]);
|
||||
for (size_t c=0; c<width_t; c++) {
|
||||
if (c == pvt_col)
|
||||
continue;
|
||||
if (!(mtx_t[row][c]))
|
||||
continue;
|
||||
|
||||
// column sum
|
||||
for (size_t r=0; r<height_t; r++) {
|
||||
mtx_t[r][c] = BOOL_XOR(mtx_t[r][c], mtx_t[r][pvt_col]);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pvt_col++;
|
||||
}
|
||||
|
||||
return filtered_masks;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
|
||||
uint64_t next_bit_permutation(uint64_t v) {
|
||||
uint64_t t = v | (v - 1);
|
||||
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzl(v) + 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
std::vector<uint64_t> find_functions(std::vector<set_t> sets, size_t max_fn_bits, size_t msb, uint64_t flags) {
|
||||
|
||||
std::vector<uint64_t> masks;
|
||||
verbose_printerr("~~~~~~~~~~ Candidate functions ~~~~~~~~~~\n");
|
||||
|
||||
for (size_t bits = 1L; bits <= max_fn_bits; bits++) {
|
||||
uint64_t fn_mask = ((1L<<(bits))-1); // avoid the first 6 bits since they are the cacheline bits
|
||||
uint64_t last_mask = (fn_mask<<(msb-bits));
|
||||
fn_mask <<= CL_SHIFT;
|
||||
verbose_printerr("[ LOG ] - #Bits: %ld \n", bits);
|
||||
while (fn_mask != last_mask) {
|
||||
if (fn_mask & LS_BITMASK(6)){
|
||||
fn_mask = next_bit_permutation(fn_mask);
|
||||
continue;
|
||||
}
|
||||
for (size_t idx = 0; idx<sets.size(); idx++) {
|
||||
set_t curr_set = sets[idx];
|
||||
size_t inner_cnt = 0;
|
||||
for (size_t i = 1; i < curr_set.size(); i++) {
|
||||
uint64_t res_base = __builtin_parityl(curr_set[0].p_addr & fn_mask);
|
||||
uint64_t res_probe = __builtin_parityl(curr_set[i].p_addr & fn_mask);
|
||||
if (res_base != res_probe) {
|
||||
goto next_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose_printerr("\t Candidate: 0x%0lx \t\t bits: %s\n", fn_mask, bit_string(fn_mask));
|
||||
masks.push_back(fn_mask);
|
||||
|
||||
next_mask:
|
||||
fn_mask = next_bit_permutation(fn_mask);
|
||||
}
|
||||
}
|
||||
verbose_printerr("~~~~~~~~~~ Found Functions ~~~~~~~~~~\n");
|
||||
masks = reduce_masks(masks);
|
||||
if (flags & F_VERBOSE) {
|
||||
for (auto m: masks) {
|
||||
fprintf(stderr, "\t Valid Function: 0x%0lx \t\t bits: %s\n", m, bit_string(m));
|
||||
}
|
||||
}
|
||||
for (auto m: masks) {
|
||||
fprintf(stdout, "0x%lx\n", m);
|
||||
}
|
||||
return masks;
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::vector<int> find_set_bits(uint64_t val) {
|
||||
std::vector<int> set_bits;
|
||||
for (int i = 0; i<64; i++) {
|
||||
if (!(val & (1ULL << i)))
|
||||
continue;
|
||||
|
||||
set_bits.push_back(i);
|
||||
}
|
||||
return set_bits;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
std::vector<uint8_t> get_dram_fn(uint64_t addr, std::vector<uint64_t> fn_masks) {
|
||||
std::vector<uint8_t> addr_dram;
|
||||
for (auto fn:fn_masks) {
|
||||
addr_dram.push_back(__builtin_parityl( addr & fn));
|
||||
}
|
||||
return addr_dram;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
/*
|
||||
It currently finds some of the interesting bits for the row addressing.
|
||||
@TODO still need to figure out which bits are used for the row addressing and which
|
||||
are from the bank selection. This is currently done manually
|
||||
*/
|
||||
uint64_t find_row_mask(std::vector<set_t>& sets, std::vector<uint64_t> fn_masks, mem_buff_t mem, uint64_t threshold, uint64_t flags) {
|
||||
|
||||
|
||||
|
||||
addr_tuple base_addr = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
|
||||
std::vector<set_t> same_row_sets;
|
||||
|
||||
verbose_printerr("~~~~~~~~~~ Looking for row bits ~~~~~~~~~~\n");
|
||||
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
verbose_printerr("[LOG] - Set #%d\n", i);
|
||||
addr_tuple base_addr = sets[i][0];
|
||||
std::vector<uint8_t> base_dram = get_dram_fn((uint64_t)base_addr.p_addr, fn_masks);
|
||||
same_row_sets.push_back({base_addr});
|
||||
uint64_t cnt = 0;
|
||||
while (cnt < ROW_SET_CNT) {
|
||||
|
||||
addr_tuple tmp = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
|
||||
if (get_dram_fn((uint64_t) tmp.p_addr, fn_masks) != base_dram)
|
||||
continue;
|
||||
|
||||
uint64_t time = time_tuple((volatile char*)base_addr.v_addr, (volatile char*)tmp.v_addr, 1000);
|
||||
|
||||
if (time > threshold)
|
||||
continue;
|
||||
|
||||
|
||||
verbose_printerr("[LOG] - %lx - %lx\t Time: %ld <== GOTCHA\n", base_addr.p_addr, tmp.p_addr, time);
|
||||
|
||||
same_row_sets[i].push_back(tmp);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint64_t row_mask = LS_BITMASK(16); // use 16 bits for the row
|
||||
uint64_t last_mask = (row_mask<<(40-16));
|
||||
row_mask <<= CL_SHIFT; // skip the lowest 6 bits since they're used for CL addressing
|
||||
|
||||
while (row_mask < last_mask) {
|
||||
if (row_mask & LS_BITMASK(CL_SHIFT)){
|
||||
row_mask = next_bit_permutation(row_mask);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto addr_pool:same_row_sets) {
|
||||
addr_tuple base_addr = addr_pool[0];
|
||||
for (int i = 1; i < addr_pool.size(); i++) {
|
||||
addr_tuple tmp = addr_pool[i];
|
||||
if ((tmp.p_addr & row_mask) != (base_addr.p_addr & row_mask)) {
|
||||
goto next_mask;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
next_mask:
|
||||
row_mask = next_bit_permutation(row_mask);
|
||||
}
|
||||
|
||||
// super hackish way to recover the real row mask
|
||||
for (auto m:fn_masks) {
|
||||
uint64_t lsb = (1<<(__builtin_ctzl(m)+1));
|
||||
if (lsb & row_mask) {
|
||||
row_mask ^= (1<<__builtin_ctzl(m));
|
||||
}
|
||||
}
|
||||
verbose_printerr("[LOG] - Row mask: 0x%0lx \t\t bits: %s\n", row_mask, bit_string(row_mask));
|
||||
printf("0x%lx\n", row_mask);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char* o_file, uint64_t flags) {
|
||||
|
||||
time_t t;
|
||||
|
||||
int o_fd = 0;
|
||||
int huge_fd = 0;
|
||||
std::vector<set_t> sets;
|
||||
std::vector<char*> used_addr;
|
||||
std::vector<uint64_t> fn_masks;
|
||||
|
||||
srand((unsigned) time(&t));
|
||||
|
||||
if (flags & F_EXPORT) {
|
||||
if (o_file == NULL) {
|
||||
fprintf(stderr, "[ERROR] - Missing export file name\n");
|
||||
exit(1);
|
||||
}
|
||||
if((o_fd = open(o_file, O_CREAT|O_RDWR)) == -1) {
|
||||
perror("[ERROR] - Unable to create export file");
|
||||
exit(1);
|
||||
}
|
||||
dprintf(o_fd, O_HEADER);
|
||||
}
|
||||
|
||||
mem_buff_t mem = {
|
||||
.buffer = NULL,
|
||||
.size = m_size,
|
||||
.flags = flags ,
|
||||
};
|
||||
|
||||
alloc_buffer(&mem);
|
||||
|
||||
|
||||
while (!found_enough(sets, sets_cnt, SET_SIZE)) {
|
||||
char* rnd_addr = get_rnd_addr(mem.buffer, mem.size, CL_SHIFT);
|
||||
if (is_in(rnd_addr, used_addr))
|
||||
continue;
|
||||
|
||||
used_addr.push_back(rnd_addr);
|
||||
|
||||
addr_tuple tp = gen_addr_tuple(rnd_addr);
|
||||
bool found_set = false;
|
||||
for (size_t idx = 0; idx < sets.size(); idx++) {
|
||||
uint64_t time = 0;
|
||||
addr_tuple tmp = sets[idx][0];
|
||||
time = time_tuple((volatile char*) tmp.v_addr, (volatile char*)tp.v_addr, rounds);
|
||||
if (flags & F_EXPORT) {
|
||||
dprintf(o_fd, "%lx,%lx,%ld\n",(uint64_t) tp.v_addr, (uint64_t) tmp.v_addr,time);
|
||||
}
|
||||
if (time > threshold) {
|
||||
verbose_printerr("[LOG] - [%ld] Set: %03ld -\t %lx - %lx\t Time: %ld\n", used_addr.size(), idx, tp.p_addr, tmp.p_addr, time);
|
||||
sets[idx].push_back(tp);
|
||||
found_set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_set) {
|
||||
sets.push_back({tp});
|
||||
verbose_printerr( "[LOG] - Set: %03ld -\t %p <== NEW!!\n", sets.size(), tp.v_addr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
filter_sets(sets, SET_SIZE);
|
||||
|
||||
#ifdef DEBUG_SETS
|
||||
fprintf(stderr, "[ LOG ] - Cleansing sets. This may take a while... stay put\n");
|
||||
verify_sets(sets, threshold, rounds);
|
||||
fprintf(stderr, "[ LOG ] - Done\n");
|
||||
#endif
|
||||
|
||||
if (flags & F_VERBOSE) {
|
||||
print_sets(sets);
|
||||
}
|
||||
|
||||
fn_masks = find_functions(sets, 6, 30, flags);
|
||||
uint64_t row_mask = find_row_mask(sets, fn_masks, mem, threshold, flags);
|
||||
|
||||
free_buffer(&mem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fin.
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Helpers
|
||||
|
||||
bool is_in(char* val, std::vector<char*> arr) {
|
||||
for (auto v: arr) {
|
||||
if (val == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
bool found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size) {
|
||||
|
||||
size_t found_sets = 0;
|
||||
|
||||
for (int i =0; i < sets.size(); i++) {
|
||||
set_t curr_set = sets[i];
|
||||
if (curr_set.size() > set_size) {
|
||||
found_sets += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_sets > set_cnt) {
|
||||
fprintf(stderr, "[ERROR] - Found too many sets. Is %ld the correct number of sets?\n", set_cnt);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return (found_sets >= (set_cnt * SET_THRESH)) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
void filter_sets(std::vector<set_t>& sets, size_t set_size) {
|
||||
|
||||
for (auto s = sets.begin(); s < sets.end(); s++) {
|
||||
if (s->size() < set_size) {
|
||||
sets.erase(s);
|
||||
s -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void print_sets(std::vector<set_t> sets) {
|
||||
|
||||
for (int idx = 0; idx < sets.size(); idx++) {
|
||||
fprintf(stderr, "[LOG] - Set: %d\tSize: %ld\n", idx, sets[idx].size());
|
||||
for (auto tmp: sets[idx]) {
|
||||
fprintf(stderr, "\tv_addr:%p - p_addr:%p\n", tmp.v_addr, (void*) tmp.p_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SETS
|
||||
|
||||
void verify_sets(std::vector<set_t>& sets, uint64_t threshold, size_t rounds) {
|
||||
|
||||
for (auto s: sets) {
|
||||
// test every address against all the addresses in the set
|
||||
for (auto tp_base = s.begin(); tp_base < s.end(); tp_base++) {
|
||||
uint64_t conflicts = 0;
|
||||
for (auto tp_probe = s.begin(); tp_probe < s.end(); tp_probe++) {
|
||||
if (tp_base == tp_probe)
|
||||
continue;
|
||||
|
||||
uint64_t time = time_tuple((volatile char*) tp_base->v_addr,(volatile char*) tp_probe->v_addr, rounds);
|
||||
if (time>threshold){
|
||||
conflicts += 1;
|
||||
}
|
||||
}
|
||||
if (!(conflicts > VALID_THRESH*s.size())) {
|
||||
fprintf(stderr, "[ LOG ] - Removing: %p\n", tp_base->v_addr);
|
||||
s.erase(tp_base--); // reset the iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
87
drama/src/utils.c
Normal file
87
drama/src/utils.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
// Memory alloc
|
||||
|
||||
int alloc_buffer(mem_buff_t* mem) {
|
||||
if (mem->buffer != NULL) {
|
||||
fprintf(stderr, "[ERROR] - Memory already allocated\n");
|
||||
}
|
||||
|
||||
uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE | MAP_ANONYMOUS;
|
||||
|
||||
mem->buffer = (char*) mmap(NULL, mem->size, PROT_READ | PROT_WRITE, alloc_flags, -1, 0);
|
||||
if (mem->buffer == MAP_FAILED) {
|
||||
perror("[ERROR] - mmap() failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (mem->flags & F_VERBOSE) {
|
||||
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
|
||||
fprintf(stderr, "[ MEM ] - Size: %ld\n", mem->size);
|
||||
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int free_buffer(mem_buff_t* mem) {
|
||||
return munmap(mem->buffer, mem->size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
// Helpers
|
||||
|
||||
double mean(uint64_t* vals, size_t size) {
|
||||
uint64_t avg = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
avg += vals[i];
|
||||
}
|
||||
return ((double)avg) / size;
|
||||
}
|
||||
|
||||
int gt(const void * a, const void * b) {
|
||||
return ( *(int*)a - *(int*)b );
|
||||
}
|
||||
|
||||
uint64_t median(uint64_t* vals, size_t size) {
|
||||
qsort(vals, size, sizeof(uint64_t), gt);
|
||||
return ((size%2)==0) ? vals[size/2] : (vals[(size_t)size/2]+vals[((size_t)size/2+1)])/2;
|
||||
}
|
||||
|
||||
|
||||
char* bit_string(uint64_t val) {
|
||||
static char bit_str[256];
|
||||
char itoa_str[8];
|
||||
strcpy(bit_str, "");
|
||||
for (int shift = 0; shift < 64; shift++) {
|
||||
if ((val >> shift) & 1) {
|
||||
if (strcmp(bit_str, "") != 0) {
|
||||
strcat(bit_str, "+ ");
|
||||
}
|
||||
sprintf(itoa_str, "%d ", shift);
|
||||
strcat(bit_str, itoa_str);
|
||||
}
|
||||
}
|
||||
|
||||
return bit_str;
|
||||
}
|
1
hammersuite/.gitignore
vendored
Normal file
1
hammersuite/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.log
|
59
hammersuite/Makefile
Normal file
59
hammersuite/Makefile
Normal file
@ -0,0 +1,59 @@
|
||||
SDIR=src
|
||||
IDIR=$(SDIR)/include
|
||||
LDIR=lib
|
||||
BUILD=obj
|
||||
ODIR=src/.obj
|
||||
DATA_DIR=$(PWD)/data/
|
||||
|
||||
CFLAGS=-I$(IDIR) -msse4.2 -ggdb -DDATA_DIR=\"$(DATA_DIR)\"
|
||||
# CXX=g++
|
||||
LDFLAGS=
|
||||
|
||||
OUT=tester
|
||||
|
||||
LDEPS=
|
||||
|
||||
GB_PAGE=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
|
||||
HUGEPAGE=/mnt/huge
|
||||
|
||||
all: $(OUT)
|
||||
.PHONY: clean
|
||||
|
||||
|
||||
SOURCES := $(wildcard $(SDIR)/*.c)
|
||||
OBJECTS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(SOURCES))
|
||||
|
||||
|
||||
$(ODIR)/%.o: $(SDIR)/%.c
|
||||
mkdir -p $(ODIR)
|
||||
$(CXX) -o $@ -c $< $(CFLAGS) $(LDFLAGS) $(LDEPS)
|
||||
|
||||
|
||||
$(OUT): $(OBJECTS)
|
||||
mkdir -p $(BUILD)
|
||||
$(CXX) -o $(BUILD)/$@ $^ $(CFLAGS) $(LDFLAGS) $(LDEPS)
|
||||
chmod +x $(BUILD)/$@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
rm -rf $(ODIR)
|
||||
|
||||
setup:
|
||||
echo "Mounting hugetlbfs"
|
||||
echo 2 | sudo tee -a $(GB_PAGE)
|
||||
@if ! [ -d $(HUGEPAGE) ]; then\
|
||||
sudo mkdir $(HUGEPAGE);\
|
||||
sudo mount -t hugetlbfs -o pagesize=1G none $(HUGEPAGE);\
|
||||
sudo chown pit:pit $(HUGEPAGE);\
|
||||
fi
|
||||
|
||||
|
||||
teardown:
|
||||
@if [ -d $(HUGEPAGE) ]; then\
|
||||
sudo umount -f $(HUGEPAGE);\
|
||||
sudo rm -r $(HUGEPAGE);\
|
||||
fi
|
||||
echo 0 | sudo tee -a $(GB_PAGE)
|
||||
|
||||
run:
|
||||
sudo $(BUILD)/$(OUT)
|
72
hammersuite/README.md
Normal file
72
hammersuite/README.md
Normal file
@ -0,0 +1,72 @@
|
||||
# TRRespass
|
||||
|
||||
## About
|
||||
TRRespass code implements several tests to reveal the presence of the Rowhammer vulnerability.
|
||||
Since several defenses may be in place in the memory controller and/or at DRAM level, TRRespass tests for non-conventional hammering patterns that may be able to bypass defenses.
|
||||
|
||||
|
||||
## Requirements
|
||||
### Physical to DRAM mapping functions
|
||||
The functions resolving the mapping of a physical address into a DRAM address are kept in the following structures:
|
||||
|
||||
`include/types.h:`
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint64_t lst[HASH_FN_CNT];
|
||||
uint64_t len;
|
||||
} AddrFns;
|
||||
|
||||
typedef struct {
|
||||
AddrFns h_fns;
|
||||
uint64_t row_mask;
|
||||
uint64_t col_mask;
|
||||
} DRAMLayout
|
||||
```
|
||||
|
||||
The mapping functions must be defined in ```main.c```:
|
||||
```c
|
||||
DRAMLayout g_mem_layout = {{{0x4080,0x48000,0x90000,0x120000,0x1b300}, 5}, row_mask, ROW_SIZE-1};
|
||||
```
|
||||
|
||||
AMD publicly documents mapping functions in th "BIOS and Kernel Developer’s Guide (BKDG)"
|
||||
Contrariwise, Intel does not. We provide a tool to retrieve the mapping functions, based on the techniques described in [1]. It's a bit hackish but it works.
|
||||
The tool is available in the folder ./drama (read the README in the folder).
|
||||
|
||||
### Huge pages support
|
||||
1GB Huge Page support is required to gain physically continuis memory and perform templating.
|
||||
|
||||
## Usage
|
||||
Commands must be run with `sudo` privileges.
|
||||
|
||||
- Full options list
|
||||
|
||||
```
|
||||
sudo ./obj/test --help
|
||||
```
|
||||
|
||||
- Common usage
|
||||
|
||||
```
|
||||
sudo ./obj/tester -r 1000000 -v -V ff -T 00 --no-overwrite -o DIMM00
|
||||
```
|
||||
|
||||
1. By default double-sided RH is tested.
|
||||
2. `-r` indicates the number of accesses for each target row.
|
||||
3. The options `-V `and `-T` specify the victim row(s) and the target rows data pattern. By default the data pattern is randomly generated.
|
||||
4. The `-o` argument allow to specify a prefix for the output file. `--no-overwrite` avoids to overwrite an output file with the same name if present.
|
||||
5. Testing for non-conventional hammering patterns (i.e., black-box fuzzing)
|
||||
|
||||
```
|
||||
sudo ./obj/tester -v --fuzzing
|
||||
```
|
||||
|
||||
This will test the RH vulnerability against randomly generated hammering patterns.
|
||||
|
||||
At the moment the tool exports the results in files we call Fliptables (the export choice is currently hardcoded as a #define). You can use `hammerstats.py` in the `../py` folder to print out statistics about the number of bit flips.
|
||||
The format is not so human friendly but it was helping us to print out statistics using some pre-existing toolchains we had.
|
||||
|
||||
|
||||
#### References
|
||||
|
||||
[1] "DRAMA: Exploiting DRAM Addressing for Cross-CPU Attacks", Usenix Sec 16, Pessl et al.
|
6
hammersuite/hugepage.sh
Executable file
6
hammersuite/hugepage.sh
Executable file
@ -0,0 +1,6 @@
|
||||
gbFile="/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages"
|
||||
|
||||
mkdir /mnt/huge
|
||||
mount -t hugetlbfs -o pagesize=1G none /mnt/huge
|
||||
su -c 'echo 1 >'$gbFile
|
||||
cat $gbFile
|
19
hammersuite/performance.sh
Executable file
19
hammersuite/performance.sh
Executable file
@ -0,0 +1,19 @@
|
||||
if [ "$1" == "" ]; then
|
||||
echo "Expected argument (powersave/ performance)"
|
||||
exit
|
||||
fi
|
||||
|
||||
max_proc=$(cat /proc/cpuinfo | grep processor | tail -n 1 | cut -d ":" -f 2)
|
||||
mode=$1
|
||||
|
||||
echo Max proc: $max_proc
|
||||
echo Setting mode: $mode
|
||||
|
||||
for i in $(seq 0 $max_proc)
|
||||
do
|
||||
echo $mode | sudo tee -a /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor > /dev/null
|
||||
echo cat /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor = $(cat /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor)
|
||||
done
|
||||
|
||||
|
||||
|
78
hammersuite/src/addr-mapper.c
Normal file
78
hammersuite/src/addr-mapper.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "addr-mapper.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static size_t g_rmap_len = 0;
|
||||
static size_t g_base_row = 0;
|
||||
static size_t g_bks = 0;
|
||||
static size_t g_rows = 0;
|
||||
|
||||
RowMap get_row_map(ADDRMapper * mapper, DRAMAddr * d_addr)
|
||||
{
|
||||
size_t idx =
|
||||
(d_addr->row - g_base_row) * get_banks_cnt() + d_addr->bank;
|
||||
assert(idx < g_bks * g_rows);
|
||||
return mapper->row_maps[idx];
|
||||
}
|
||||
|
||||
DRAM_pte get_dram_pte(ADDRMapper * mapper, DRAMAddr * d_addr)
|
||||
{
|
||||
RowMap rmap = get_row_map(mapper, d_addr);
|
||||
return rmap.lst[(d_addr->col) >> 6];
|
||||
}
|
||||
|
||||
RowMap gen_row_map(DRAMAddr d_src, MemoryBuffer * mem)
|
||||
{
|
||||
RowMap rmap;
|
||||
DRAM_pte *dst = (DRAM_pte *) malloc(sizeof(DRAM_pte) * g_rmap_len);
|
||||
d_src.col = 0;
|
||||
|
||||
for (size_t col = 0; col < g_rmap_len; col++, d_src.col += (1 << 6)) {
|
||||
dst[col].d_addr = d_src;
|
||||
dst[col].v_addr = phys_2_virt(dram_2_phys(d_src), mem);
|
||||
}
|
||||
rmap.lst = dst;
|
||||
rmap.len = g_rmap_len;
|
||||
return rmap;
|
||||
|
||||
}
|
||||
|
||||
size_t rmap_idx(size_t bk, size_t row)
|
||||
{
|
||||
return row * get_banks_cnt() + bk;
|
||||
}
|
||||
|
||||
void init_addr_mapper(ADDRMapper * mapper, MemoryBuffer * mem,
|
||||
DRAMAddr * d_base, size_t h_rows)
|
||||
{
|
||||
mapper->row_maps =
|
||||
(RowMap *) malloc(sizeof(RowMap) * h_rows * get_banks_cnt());
|
||||
mapper->base_row = d_base->row;
|
||||
g_base_row = d_base->row;
|
||||
g_bks = get_banks_cnt();
|
||||
g_rows = h_rows;
|
||||
// set global rmap_len
|
||||
g_rmap_len = ROW_SIZE / CL_SIZE;
|
||||
|
||||
// create ptes list for every
|
||||
DRAMAddr d_tmp = {.bank = 0,.row = 0,.col = 0 };
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
d_tmp.bank = bk;
|
||||
for (size_t row = 0; row < h_rows; row++) {
|
||||
d_tmp.row = g_base_row + row;
|
||||
mapper->row_maps[rmap_idx(bk, row)] =
|
||||
gen_row_map(d_tmp, mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tear_down_addr_mapper(ADDRMapper * mapper)
|
||||
{
|
||||
for (int i = 1; i < g_rows * g_bks; i++) {
|
||||
free(mapper->row_maps[i].lst);
|
||||
}
|
||||
free(mapper->row_maps);
|
||||
}
|
74
hammersuite/src/allocator.c
Normal file
74
hammersuite/src/allocator.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include "allocator.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
int alloc_buffer(MemoryBuffer * mem)
|
||||
{
|
||||
if (mem->buffer != NULL) {
|
||||
fprintf(stderr, "[ERROR] - Memory already allocated\n");
|
||||
}
|
||||
|
||||
if (mem->align < _SC_PAGE_SIZE) {
|
||||
mem->align = 0;
|
||||
}
|
||||
|
||||
uint64_t alloc_size = mem->align ? mem->size + mem->align : mem->size;
|
||||
uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE;
|
||||
|
||||
if (mem->flags & F_ALLOC_HUGE) {
|
||||
if (mem->fd == 0) {
|
||||
fprintf(stderr,
|
||||
"[ERROR] - Missing file descriptor to allocate hugepage\n");
|
||||
exit(1);
|
||||
}
|
||||
alloc_flags |=
|
||||
(mem->flags & F_ALLOC_HUGE_1G) ? MAP_ANONYMOUS | MAP_HUGETLB
|
||||
| (30 << MAP_HUGE_SHIFT)
|
||||
: (mem->flags & F_ALLOC_HUGE_2M) ? MAP_ANONYMOUS |
|
||||
MAP_HUGETLB | (21 << MAP_HUGE_SHIFT)
|
||||
: MAP_ANONYMOUS;
|
||||
} else {
|
||||
mem->fd = -1;
|
||||
alloc_flags |= MAP_ANONYMOUS;
|
||||
}
|
||||
mem->buffer = (char *)mmap(NULL, mem->size, PROT_READ | PROT_WRITE,
|
||||
alloc_flags, mem->fd, 0);
|
||||
if (mem->buffer == MAP_FAILED) {
|
||||
perror("[ERROR] - mmap() failed");
|
||||
exit(1);
|
||||
}
|
||||
if (mem->align) {
|
||||
size_t error = (uint64_t) mem->buffer % mem->align;
|
||||
size_t left = error ? mem->align - error : 0;
|
||||
munmap(mem->buffer, left);
|
||||
mem->buffer += left;
|
||||
assert((uint64_t) mem->buffer % mem->align == 0);
|
||||
}
|
||||
|
||||
if (mem->flags & F_VERBOSE) {
|
||||
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
|
||||
fprintf(stderr, "[ MEM ] - Size: %ld\n", alloc_size);
|
||||
fprintf(stderr, "[ MEM ] - Alignment: %ld\n", mem->align);
|
||||
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int free_buffer(MemoryBuffer * mem)
|
||||
{
|
||||
free(mem->physmap);
|
||||
return munmap(mem->buffer, mem->size);
|
||||
}
|
131
hammersuite/src/dram-address.c
Normal file
131
hammersuite/src/dram-address.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include "dram-address.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define DEBUG_REVERSE_FN 1
|
||||
|
||||
extern DRAMLayout g_mem_layout;
|
||||
|
||||
uint64_t get_dram_row(physaddr_t p_addr)
|
||||
{
|
||||
return (p_addr & g_mem_layout.
|
||||
row_mask) >> __builtin_ctzl(g_mem_layout.row_mask);
|
||||
}
|
||||
|
||||
uint64_t get_dram_col(physaddr_t p_addr)
|
||||
{
|
||||
return (p_addr & g_mem_layout.
|
||||
col_mask) >> __builtin_ctzl(g_mem_layout.col_mask);
|
||||
}
|
||||
|
||||
DRAMAddr phys_2_dram(physaddr_t p_addr)
|
||||
{
|
||||
|
||||
DRAMAddr res = { 0, 0, 0 };
|
||||
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
|
||||
res.bank |=
|
||||
(__builtin_parityl(p_addr & g_mem_layout.h_fns.lst[i]) <<
|
||||
i);
|
||||
}
|
||||
|
||||
res.row = get_dram_row(p_addr);
|
||||
res.col = get_dram_col(p_addr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
physaddr_t dram_2_phys(DRAMAddr d_addr)
|
||||
{
|
||||
physaddr_t p_addr = 0;
|
||||
uint64_t col_val = 0;
|
||||
|
||||
p_addr = (d_addr.row << __builtin_ctzl(g_mem_layout.row_mask)); // set row bits
|
||||
p_addr |= (d_addr.col << __builtin_ctzl(g_mem_layout.col_mask)); // set col bits
|
||||
|
||||
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
|
||||
uint64_t masked_addr = p_addr & g_mem_layout.h_fns.lst[i];
|
||||
// if the address already respects the h_fn then just move to the next func
|
||||
if (__builtin_parity(masked_addr) == ((d_addr.bank >> i) & 1L)) {
|
||||
continue;
|
||||
}
|
||||
// else flip a bit of the address so that the address respects the dram h_fn
|
||||
// that is get only bits not affecting the row.
|
||||
uint64_t h_lsb = __builtin_ctzl((g_mem_layout.h_fns.lst[i]) &
|
||||
~(g_mem_layout.col_mask) &
|
||||
~(g_mem_layout.row_mask));
|
||||
p_addr ^= 1 << h_lsb;
|
||||
}
|
||||
|
||||
#if DEBUG_REVERSE_FN
|
||||
int correct = 1;
|
||||
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
|
||||
|
||||
if (__builtin_parity(p_addr & g_mem_layout.h_fns.lst[i]) !=
|
||||
((d_addr.bank >> i) & 1L)) {
|
||||
correct = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d_addr.row != ((p_addr &
|
||||
g_mem_layout.row_mask) >>
|
||||
__builtin_ctzl(g_mem_layout.row_mask)))
|
||||
correct = 0;
|
||||
if (!correct)
|
||||
fprintf(stderr,
|
||||
"[DEBUG] - Mapping function for 0x%lx not respected\n",
|
||||
p_addr);
|
||||
|
||||
#endif
|
||||
|
||||
return p_addr;
|
||||
}
|
||||
|
||||
void set_global_dram_layout(DRAMLayout & mem_layout)
|
||||
{
|
||||
g_mem_layout = mem_layout;
|
||||
}
|
||||
|
||||
DRAMLayout *get_dram_layout()
|
||||
{
|
||||
return &g_mem_layout;
|
||||
}
|
||||
|
||||
bool d_addr_eq(DRAMAddr * d1, DRAMAddr * d2)
|
||||
{
|
||||
return (d1->bank == d2->bank) && (d1->row == d2->row)
|
||||
&& (d1->col == d2->col);
|
||||
}
|
||||
|
||||
bool d_addr_eq_row(DRAMAddr * d1, DRAMAddr * d2)
|
||||
{
|
||||
return (d1->bank == d2->bank) && (d1->row == d2->row);
|
||||
}
|
||||
|
||||
uint64_t get_banks_cnt()
|
||||
{
|
||||
return 1 << g_mem_layout.h_fns.len;
|
||||
|
||||
}
|
||||
|
||||
char *dram_2_str(DRAMAddr * d_addr)
|
||||
{
|
||||
static char ret_str[1024];
|
||||
sprintf(ret_str, "DRAM(bk: %ld (%s), row: %08ld, col: %08ld)",
|
||||
d_addr->bank, int_2_bin(d_addr->bank), d_addr->row,
|
||||
d_addr->col);
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
char *dramLayout_2_str(DRAMLayout * mem_layout)
|
||||
{
|
||||
static char ret_str[1024];
|
||||
sprintf(ret_str, "{0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx} - 0x%lx\n",
|
||||
mem_layout->h_fns.lst[0], mem_layout->h_fns.lst[1],
|
||||
mem_layout->h_fns.lst[2], mem_layout->h_fns.lst[3],
|
||||
mem_layout->h_fns.lst[4], mem_layout->h_fns.lst[5],
|
||||
mem_layout->row_mask);
|
||||
return ret_str;
|
||||
}
|
943
hammersuite/src/hammer-suite.c
Normal file
943
hammersuite/src/hammer-suite.c
Normal file
@ -0,0 +1,943 @@
|
||||
#include "include/hammer-suite.h"
|
||||
|
||||
#include "include/memory.h"
|
||||
#include "include/utils.h"
|
||||
#include "include/allocator.h"
|
||||
#include "include/dram-address.h"
|
||||
#include "include/addr-mapper.h"
|
||||
#include "include/params.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#define REFRESH_VAL "stdrefi"
|
||||
#define OUT_HEAD "f_og, f_new, vict_addr, aggr_addr\n"
|
||||
|
||||
#define ROW_FIELD 1
|
||||
#define COL_FIELD 1<<1
|
||||
#define BK_FIELD 1<<2
|
||||
#define P_FIELD 1<<3
|
||||
#define ALL_FIELDS (ROW_FIELD | COL_FIELD | BK_FIELD)
|
||||
#define FLIPTABLE
|
||||
|
||||
/*
|
||||
h_patt = hammer pattern (e.g., DOUBLE_SIDED)
|
||||
d_patt = data pattern (e.g., RANDOM)
|
||||
vict_addr = DRAMAddr for the bit flip in format bkXX.XrXXXX.cXXX
|
||||
f_og = byte original value
|
||||
f_new = byte after bit flip
|
||||
f_mask = bitmask of the bit flip
|
||||
base_row = initial row being hammered *in this round*
|
||||
row_cnt = number of rows being hammered *in this round*
|
||||
t_refi = t_refi
|
||||
h_rounds = number of hammering rounds
|
||||
aggr_addr = addresses of aggressor rows in format bkXX.rXXXX.cXX
|
||||
|
||||
*/
|
||||
|
||||
#define SHADOW_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE)
|
||||
#define DEBUG
|
||||
|
||||
#define NOP asm volatile ("NOP":::);
|
||||
#define NOP10 NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP
|
||||
#define NOP100 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10
|
||||
#define NOP1000 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100
|
||||
|
||||
extern ProfileParams *p;
|
||||
|
||||
int g_bk;
|
||||
FILE *out_fd = NULL;
|
||||
static uint64_t CL_SEED = 0x7bc661612e71168c;
|
||||
|
||||
static inline __attribute((always_inline))
|
||||
char *cl_rand_gen(DRAMAddr * d_addr)
|
||||
{
|
||||
static uint64_t cl_buff[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cl_buff[i] =
|
||||
__builtin_ia32_crc32di(CL_SEED,
|
||||
(d_addr->row + d_addr->bank +
|
||||
(d_addr->col + i*8)));
|
||||
}
|
||||
return (char *)cl_buff;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
DRAMAddr *d_lst;
|
||||
size_t len;
|
||||
size_t rounds;
|
||||
} HammerPattern;
|
||||
|
||||
typedef struct {
|
||||
DRAMAddr d_vict;
|
||||
uint8_t f_og;
|
||||
uint8_t f_new;
|
||||
HammerPattern *h_patt;
|
||||
} FlipVal;
|
||||
|
||||
typedef struct {
|
||||
MemoryBuffer *mem;
|
||||
SessionConfig *cfg;
|
||||
DRAMAddr d_base; // base address for hammering
|
||||
ADDRMapper *mapper; // dram mapper
|
||||
|
||||
int (*hammer_test) (void *self);
|
||||
} HammerSuite;
|
||||
|
||||
char *dAddr_2_str(DRAMAddr d_addr, uint8_t fields)
|
||||
{
|
||||
static char ret_str[64];
|
||||
char tmp_str[10];
|
||||
bool first = true;
|
||||
memset(ret_str, 0x00, 64);
|
||||
if (fields & ROW_FIELD) {
|
||||
first = false;
|
||||
sprintf(tmp_str, "r%05ld", d_addr.row);
|
||||
strcat(ret_str, tmp_str);
|
||||
}
|
||||
if (fields & BK_FIELD) {
|
||||
if (!first) {
|
||||
strcat(ret_str, ".");
|
||||
}
|
||||
sprintf(tmp_str, "bk%02ld", d_addr.bank);
|
||||
strcat(ret_str, tmp_str);
|
||||
first = false;
|
||||
}
|
||||
if (fields & COL_FIELD) {
|
||||
if (!first) {
|
||||
strcat(ret_str, ".");
|
||||
}
|
||||
sprintf(tmp_str, "col%04ld", d_addr.col);
|
||||
strcat(ret_str, tmp_str);
|
||||
first = false;
|
||||
}
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
char *hPatt_2_str(HammerPattern * h_patt, int fields)
|
||||
{
|
||||
static char patt_str[256];
|
||||
char *dAddr_str;
|
||||
|
||||
memset(patt_str, 0x00, 256);
|
||||
|
||||
for (int i = 0; i < h_patt->len; i++) {
|
||||
dAddr_str = dAddr_2_str(h_patt->d_lst[i], fields);
|
||||
strcat(patt_str, dAddr_str);
|
||||
if (i + 1 != h_patt->len) {
|
||||
strcat(patt_str, "/");
|
||||
}
|
||||
|
||||
}
|
||||
return patt_str;
|
||||
}
|
||||
|
||||
void print_start_attack(HammerPattern *h_patt)
|
||||
{
|
||||
fprintf(out_fd, "%s : ", hPatt_2_str(h_patt, ROW_FIELD | BK_FIELD));
|
||||
fflush(out_fd);
|
||||
}
|
||||
|
||||
void print_end_attack()
|
||||
{
|
||||
fprintf(out_fd, "\n");
|
||||
fflush(out_fd);
|
||||
}
|
||||
|
||||
void export_flip(FlipVal * flip)
|
||||
{
|
||||
if (p->g_flags & F_VERBOSE) {
|
||||
fprintf(stdout, "[FLIP] - (%02x => %02x)\t vict: %s \taggr: %s \n",
|
||||
flip->f_og, flip->f_new, dAddr_2_str(flip->d_vict, ALL_FIELDS),
|
||||
hPatt_2_str(flip->h_patt, ROW_FIELD | BK_FIELD));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#ifdef FLIPTABLE
|
||||
fprintf(out_fd, "%02x,%02x,%s ", flip->f_og, flip->f_new,
|
||||
dAddr_2_str(flip->d_vict, ALL_FIELDS));
|
||||
#else
|
||||
fprintf(out_fd, "%02x,%02x,%s,%s\n", flip->f_og, flip->f_new,
|
||||
dAddr_2_str(flip->d_vict, ALL_FIELDS), hPatt_2_str(flip->h_patt,
|
||||
ROW_FIELD | BK_FIELD | P_FIELD));
|
||||
#endif
|
||||
fflush(out_fd);
|
||||
}
|
||||
|
||||
void export_cfg(HammerSuite * suite)
|
||||
{
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
|
||||
if (p->g_flags & F_VERBOSE) {
|
||||
fprintf(stdout,
|
||||
"Config: { h_cfg: %s, d_cfg: %s, h_rows: %ld, h_rounds: %ld, base: %s}\n",
|
||||
config_str[cfg->h_cfg], data_str[cfg->d_cfg],
|
||||
cfg->h_rows, cfg->h_rounds, dAddr_2_str(suite->d_base,
|
||||
ROW_FIELD |
|
||||
BK_FIELD));
|
||||
}
|
||||
|
||||
fprintf(out_fd,
|
||||
"# { h_cfg: %s, d_cfg: %s, h_rows: %ld, h_rounds: %ld, base: %s}\n",
|
||||
config_str[cfg->h_cfg], data_str[cfg->d_cfg], cfg->h_rows,
|
||||
cfg->h_rounds, dAddr_2_str(suite->d_base,
|
||||
ROW_FIELD | BK_FIELD));
|
||||
fflush(out_fd);
|
||||
}
|
||||
|
||||
void swap(char **lst, int i, int j)
|
||||
{
|
||||
char *tmp = lst[i];
|
||||
lst[i] = lst[j];
|
||||
lst[j] = tmp;
|
||||
}
|
||||
|
||||
int random_int(int min, int max)
|
||||
{
|
||||
int number = min + rand() % (max - min);
|
||||
return number;
|
||||
}
|
||||
|
||||
uint64_t hammer_it(HammerPattern* patt, MemoryBuffer* mem) {
|
||||
|
||||
char** v_lst = (char**) malloc(sizeof(char*)*patt->len);
|
||||
for (size_t i = 0; i < patt->len; i++) {
|
||||
v_lst[i] = phys_2_virt(dram_2_phys(patt->d_lst[i]), mem);
|
||||
}
|
||||
|
||||
sched_yield();
|
||||
if (p->threshold > 0) {
|
||||
uint64_t t0 = 0, t1 = 0;
|
||||
// Threshold value depends on your system
|
||||
while (abs((int64_t) t1 - (int64_t) t0) < p->threshold) {
|
||||
t0 = rdtscp();
|
||||
*(volatile char *)v_lst[0];
|
||||
clflushopt(v_lst[0]);
|
||||
t1 = rdtscp();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint64_t cl0, cl1;
|
||||
cl0 = realtime_now();
|
||||
for ( int i = 0; i < patt->rounds; i++) {
|
||||
mfence();
|
||||
for (size_t j = 0; j < patt->len; j++) {
|
||||
*(volatile char*) v_lst[j];
|
||||
}
|
||||
for (size_t j = 0; j < patt->len; j++) {
|
||||
clflushopt(v_lst[j]);
|
||||
}
|
||||
}
|
||||
cl1 = realtime_now();
|
||||
|
||||
free(v_lst);
|
||||
return (cl1-cl0) / 1000000;
|
||||
|
||||
}
|
||||
|
||||
void __test_fill_random(char *addr, size_t size)
|
||||
{
|
||||
int fd;
|
||||
if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
|
||||
perror("[ERROR] - Unable to open /dev/urandom");
|
||||
exit(1);
|
||||
}
|
||||
if (read(fd, addr, size) == -1) {
|
||||
perror("[ERROR] - Unable to read /dev/urandom");
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
// DRAMAddr needs to be a copy in order to leave intact the original address
|
||||
void fill_stripe(DRAMAddr d_addr, uint8_t val, ADDRMapper * mapper)
|
||||
{
|
||||
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
|
||||
d_addr.col = col;
|
||||
DRAM_pte d_pte = get_dram_pte(mapper, &d_addr);
|
||||
memset(d_pte.v_addr, val, CL_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void fill_row(HammerSuite * suite, DRAMAddr * d_addr, HammerData data_patt)
|
||||
{
|
||||
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
|
||||
fill_stripe(*d_addr, (uint8_t) * p->tpat, suite->mapper);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data_patt) {
|
||||
case RANDOM:
|
||||
// rows are already filled for random data patt
|
||||
break;
|
||||
case ONE_TO_ZERO:
|
||||
fill_stripe(*d_addr, 0x00, suite->mapper);
|
||||
break;
|
||||
case ZERO_TO_ONE:
|
||||
fill_stripe(*d_addr, 0xff, suite->mapper);
|
||||
break;
|
||||
default:
|
||||
// fprintf(stderr, "[ERROR] - Wrong data pattern %d\n", data_patt);
|
||||
// exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cl_rand_fill(DRAM_pte * pte)
|
||||
{
|
||||
char *rand_data = cl_rand_gen(&pte->d_addr);
|
||||
memcpy(pte->v_addr, rand_data, CL_SIZE);
|
||||
}
|
||||
|
||||
uint64_t cl_rand_comp(DRAM_pte * pte)
|
||||
{
|
||||
char *rand_data = cl_rand_gen(&pte->d_addr);
|
||||
uint64_t res = 0;
|
||||
for (int i = 0; i < CL_SIZE; i++) {
|
||||
if (*(pte->v_addr + i) != rand_data[i]) {
|
||||
res |= 1UL<<i;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void init_random(HammerSuite * suite)
|
||||
{
|
||||
int fd;
|
||||
if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
|
||||
perror("[ERROR] - Unable to open /dev/urandom");
|
||||
exit(1);
|
||||
}
|
||||
if (CL_SEED == 0) {
|
||||
if (read(fd, &CL_SEED, sizeof(CL_SEED)) == -1) {
|
||||
perror("[ERROR] - Unable to read /dev/urandom");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// fprintf(out_fd,"#seed: %lx\n", CL_SEED);
|
||||
close(fd);
|
||||
ADDRMapper *mapper = suite->mapper;
|
||||
DRAMAddr d_tmp;
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
d_tmp.bank = bk;
|
||||
for (size_t row = 0; row < suite->cfg->h_rows; row++) {
|
||||
d_tmp.row = suite->mapper->base_row + row;
|
||||
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
|
||||
d_tmp.col = col;
|
||||
DRAM_pte d_pte = get_dram_pte(mapper, &d_tmp);
|
||||
cl_rand_fill(&d_pte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_stripe(HammerSuite * suite, uint8_t val)
|
||||
{
|
||||
ADDRMapper *mapper = suite->mapper;
|
||||
DRAMAddr d_tmp;
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
d_tmp.bank = bk;
|
||||
for (size_t row = 0; row < suite->cfg->h_rows; row++) {
|
||||
d_tmp.row = suite->mapper->base_row + row;
|
||||
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
|
||||
d_tmp.col = col;
|
||||
DRAM_pte d_pte = get_dram_pte(mapper, &d_tmp);
|
||||
memset(d_pte.v_addr, val, CL_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_chunk(HammerSuite * suite)
|
||||
{
|
||||
|
||||
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
|
||||
init_stripe(suite, (uint8_t) * p->vpat);
|
||||
return;
|
||||
}
|
||||
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
switch (cfg->d_cfg) {
|
||||
case RANDOM:
|
||||
init_random(suite);
|
||||
break;
|
||||
case ONE_TO_ZERO:
|
||||
init_stripe(suite, 0xff);
|
||||
break;
|
||||
case ZERO_TO_ONE:
|
||||
init_stripe(suite, 0x00);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] - Wrong data pattern %d\n",
|
||||
cfg->d_cfg);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void scan_random(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows)
|
||||
{
|
||||
ADDRMapper *mapper = suite->mapper;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
|
||||
DRAMAddr d_tmp;
|
||||
FlipVal flip;
|
||||
|
||||
d_tmp.bank = h_patt->d_lst[0].bank;
|
||||
|
||||
for (size_t row = 0; row < cfg->h_rows; row++) {
|
||||
d_tmp.row = suite->mapper->base_row + row;
|
||||
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
|
||||
d_tmp.col = col;
|
||||
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
|
||||
clflush(pte.v_addr);
|
||||
cpuid();
|
||||
uint64_t res = cl_rand_comp(&pte);
|
||||
if (res) {
|
||||
char *rand_data = cl_rand_gen(&pte.d_addr);
|
||||
for (int off = 0; off < CL_SIZE; off++) {
|
||||
if (!((res >> off) & 1))
|
||||
continue;
|
||||
d_tmp.col += off;
|
||||
|
||||
flip.d_vict = d_tmp;
|
||||
flip.f_og = (uint8_t) rand_data[off];
|
||||
flip.f_new = *(uint8_t *) (pte.v_addr + off);
|
||||
flip.h_patt = h_patt;
|
||||
assert(flip.f_og != flip.f_new);
|
||||
export_flip(&flip);
|
||||
|
||||
}
|
||||
memcpy((char *)(pte.v_addr), rand_data, CL_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int find_flip(HammerSuite * suite, HammerPattern * h_patt, FlipVal *orig)
|
||||
{
|
||||
ADDRMapper *mapper = suite->mapper;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
|
||||
DRAMAddr d_tmp;
|
||||
FlipVal flip;;
|
||||
|
||||
d_tmp.bank = orig->d_vict.bank;
|
||||
d_tmp.row = orig->d_vict.row;
|
||||
d_tmp.col = orig->d_vict.col;
|
||||
|
||||
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
|
||||
clflush(pte.v_addr);
|
||||
cpuid();
|
||||
int off = cl_rand_comp(&pte);
|
||||
if (off != -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool in_hPatt(DRAMAddr * d_addr, HammerPattern * h_patt)
|
||||
{
|
||||
for (int i = 0; i < h_patt->len; i++) {
|
||||
if (d_addr_eq_row(&h_patt->d_lst[i], d_addr))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t cl_stripe_cmp(DRAM_pte * pte, uint8_t val)
|
||||
{
|
||||
uint64_t res = 0;
|
||||
#ifdef POINTER_CHAISING
|
||||
for (int_t i = 8; i < CL_SIZE; i++) {
|
||||
#else
|
||||
for (int i = 0; i < CL_SIZE; i++) {
|
||||
#endif
|
||||
if (*(uint8_t*) (pte->v_addr + i) != val) {
|
||||
res |= 1UL<<i;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void scan_stripe(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows,
|
||||
uint8_t val)
|
||||
{
|
||||
ADDRMapper *mapper = suite->mapper;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
|
||||
DRAMAddr d_tmp;
|
||||
FlipVal flip;
|
||||
|
||||
d_tmp.bank = h_patt->d_lst[0].bank;
|
||||
uint8_t t_val = val;
|
||||
|
||||
for (size_t row = 0; row < cfg->h_rows; row++) {
|
||||
d_tmp.row = suite->mapper->base_row + row;
|
||||
t_val = val;
|
||||
if (in_hPatt(&d_tmp, h_patt))
|
||||
if (p->tpat != (void *)NULL && p->vpat != (void *)NULL)
|
||||
t_val = (uint8_t) * p->tpat;
|
||||
else
|
||||
t_val ^= 0xff;
|
||||
|
||||
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
|
||||
d_tmp.col = col;
|
||||
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
|
||||
clflush(pte.v_addr);
|
||||
cpuid();
|
||||
|
||||
uint64_t res = cl_stripe_cmp(&pte, t_val);
|
||||
if (res) {
|
||||
for (int off = 0; off < CL_SIZE; off++) {
|
||||
if (!((res >> off) & 1))
|
||||
continue;
|
||||
d_tmp.col += off;
|
||||
|
||||
flip.d_vict = d_tmp;
|
||||
flip.f_og = (uint8_t) t_val;
|
||||
flip.f_new = *(uint8_t *) (pte.v_addr + off);
|
||||
flip.h_patt = h_patt;
|
||||
export_flip(&flip);
|
||||
memset(pte.v_addr + off, t_val, 1);
|
||||
}
|
||||
memset((char *)(pte.v_addr), t_val, CL_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO adj_rows should tell how many rows to scan out of the bank. Not currently used
|
||||
void scan_rows(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows)
|
||||
{
|
||||
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
|
||||
scan_stripe(suite, h_patt, adj_rows, (uint8_t) * p->vpat);
|
||||
return;
|
||||
}
|
||||
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
switch (cfg->d_cfg) {
|
||||
case RANDOM:
|
||||
// rows are already filled for random data patt
|
||||
scan_random(suite, h_patt, adj_rows);
|
||||
break;
|
||||
case ONE_TO_ZERO:
|
||||
scan_stripe(suite, h_patt, adj_rows, 0xff);
|
||||
break;
|
||||
case ZERO_TO_ONE:
|
||||
scan_stripe(suite, h_patt, adj_rows, 0x00);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "[ERROR] - Wrong data pattern %d\n",
|
||||
cfg->d_cfg);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int free_triple_sided_test(HammerSuite * suite)
|
||||
{
|
||||
MemoryBuffer *mem = suite->mem;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
|
||||
DRAMAddr d_base = suite->d_base;
|
||||
d_base.col = 0;
|
||||
HammerPattern h_patt;
|
||||
|
||||
h_patt.len = 3;
|
||||
h_patt.rounds = cfg->h_rounds;
|
||||
|
||||
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
|
||||
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
|
||||
|
||||
init_chunk(suite);
|
||||
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
|
||||
|
||||
h_patt.d_lst[0] = d_base;
|
||||
for (int r0 = 1; r0 < cfg->h_rows; r0++) {
|
||||
for (int r1 = r0; r1 < cfg->h_rows; r1++) {
|
||||
if (r0 == r1)
|
||||
continue;
|
||||
|
||||
h_patt.d_lst[1].row = h_patt.d_lst[0].row + r0;
|
||||
h_patt.d_lst[2].row = h_patt.d_lst[0].row + r1;
|
||||
h_patt.d_lst[0].bank = 0;
|
||||
h_patt.d_lst[1].bank = 0;
|
||||
h_patt.d_lst[2].bank = 0;
|
||||
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
h_patt.d_lst[0].bank = bk;
|
||||
h_patt.d_lst[1].bank = bk;
|
||||
h_patt.d_lst[2].bank = bk;
|
||||
// fill all the aggressor rows
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
|
||||
}
|
||||
uint64_t time = hammer_it(&h_patt, mem);
|
||||
fprintf(stderr, "%ld ", time);
|
||||
|
||||
scan_rows(suite, &h_patt, 0);
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], (HammerData) ((int)cfg->
|
||||
d_cfg ^ (int) REVERSE));
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
free(h_patt.d_lst);
|
||||
}
|
||||
|
||||
int assisted_double_sided_test(HammerSuite * suite)
|
||||
{
|
||||
MemoryBuffer *mem = suite->mem;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
DRAMAddr d_base = suite->d_base;
|
||||
d_base.col = 0;
|
||||
|
||||
HammerPattern h_patt;
|
||||
|
||||
h_patt.len = 3;
|
||||
h_patt.rounds = cfg->h_rounds;
|
||||
|
||||
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
|
||||
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
|
||||
|
||||
init_chunk(suite);
|
||||
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
|
||||
h_patt.d_lst[0] = d_base;
|
||||
|
||||
for (int r0 = 1; r0 < cfg->h_rows; r0++) {
|
||||
h_patt.d_lst[1].row = d_base.row + r0;
|
||||
h_patt.d_lst[2].row = h_patt.d_lst[1].row + 2;
|
||||
h_patt.d_lst[0].row =
|
||||
d_base.row + get_rnd_int(0, cfg->h_rows - 1);
|
||||
while (h_patt.d_lst[0].row == h_patt.d_lst[1].row
|
||||
|| h_patt.d_lst[0].row == h_patt.d_lst[2].row)
|
||||
h_patt.d_lst[0].row =
|
||||
d_base.row + get_rnd_int(0, cfg->h_rows - 1);
|
||||
|
||||
if (h_patt.d_lst[2].row >= d_base.row + cfg->h_rows)
|
||||
break;
|
||||
|
||||
h_patt.d_lst[0].bank = 0;
|
||||
h_patt.d_lst[1].bank = 0;
|
||||
h_patt.d_lst[2].bank = 0;
|
||||
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
h_patt.d_lst[0].bank = bk;
|
||||
h_patt.d_lst[1].bank = bk;
|
||||
h_patt.d_lst[2].bank = bk;
|
||||
// fill all the aggressor rows
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
|
||||
// fprintf(stderr, "d_addr: %s\n", dram_2_str(&h_patt.d_lst[idx]));
|
||||
}
|
||||
// fprintf(stderr, "d_addr: %s\n", dram_2_str(&h_patt.d_lst[idx]));
|
||||
uint64_t time = hammer_it(&h_patt, mem);
|
||||
fprintf(stderr, "%ld ", time);
|
||||
|
||||
scan_rows(suite, &h_patt, 0);
|
||||
for (int idx = 0; idx<3; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], (HammerData) ((int)cfg->d_cfg ^ (int)REVERSE));
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
free(h_patt.d_lst);
|
||||
}
|
||||
|
||||
int n_sided_test(HammerSuite * suite)
|
||||
{
|
||||
MemoryBuffer *mem = suite->mem;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
DRAMAddr d_base = suite->d_base;
|
||||
d_base.col = 0;
|
||||
/* d_base.row = 20480; */
|
||||
/* d_base.row = 16400; */
|
||||
HammerPattern h_patt;
|
||||
|
||||
h_patt.len = cfg->aggr_n;
|
||||
h_patt.rounds = cfg->h_rounds;
|
||||
|
||||
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
|
||||
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
|
||||
|
||||
init_chunk(suite);
|
||||
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
|
||||
h_patt.d_lst[0] = d_base;
|
||||
|
||||
const int mem_to_hammer = 256 << 20;
|
||||
const int n_rows = mem_to_hammer / ((8<<10) * get_banks_cnt());
|
||||
fprintf(stderr, "Hammering %d rows per bank\n", n_rows);
|
||||
for (int r0 = 1; r0 < n_rows; r0++) {
|
||||
h_patt.d_lst[0].row = d_base.row + r0;
|
||||
int k = 1;
|
||||
for (; k < cfg->aggr_n; k++) {
|
||||
h_patt.d_lst[k].row = h_patt.d_lst[k - 1].row + 2;
|
||||
h_patt.d_lst[k].bank = 0;
|
||||
}
|
||||
if (h_patt.d_lst[k - 1].row >= d_base.row + cfg->h_rows)
|
||||
break;
|
||||
|
||||
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
|
||||
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
|
||||
|
||||
for (int s = 0; s < cfg->aggr_n; s++) {
|
||||
h_patt.d_lst[s].bank = bk;
|
||||
}
|
||||
#ifdef FLIPTABLE
|
||||
print_start_attack(&h_patt);
|
||||
#endif
|
||||
// fill all the aggressor rows
|
||||
for (int idx = 0; idx < cfg->aggr_n; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
|
||||
}
|
||||
|
||||
uint64_t time = hammer_it(&h_patt, mem);
|
||||
fprintf(stderr, "%ld ", time);
|
||||
|
||||
scan_rows(suite, &h_patt, 0);
|
||||
for (int idx = 0; idx<h_patt.len; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], (HammerData)
|
||||
((int)cfg->d_cfg ^ (int)REVERSE));
|
||||
#ifdef FLIPTABLE
|
||||
print_end_attack();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
free(h_patt.d_lst);
|
||||
}
|
||||
|
||||
void fuzz(HammerSuite *suite, int d, int v)
|
||||
{
|
||||
int i;
|
||||
HammerPattern h_patt;
|
||||
SessionConfig *cfg = suite->cfg;
|
||||
h_patt.rounds = cfg->h_rounds;
|
||||
h_patt.len = cfg->aggr_n;
|
||||
|
||||
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
|
||||
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
|
||||
|
||||
init_chunk(suite);
|
||||
int offset = random_int(1, 32);
|
||||
|
||||
h_patt.d_lst[0] = suite->d_base;
|
||||
h_patt.d_lst[0].row = suite->d_base.row + offset;
|
||||
|
||||
h_patt.d_lst[1] = suite->d_base;
|
||||
h_patt.d_lst[1].row = h_patt.d_lst[0].row + v + 1;
|
||||
for (i = 2; i < h_patt.len-1; i+=2) {
|
||||
h_patt.d_lst[i] = suite->d_base;
|
||||
h_patt.d_lst[i].row = h_patt.d_lst[i-1].row + d + 1;
|
||||
h_patt.d_lst[i+1] = suite->d_base;
|
||||
h_patt.d_lst[i+1].row = h_patt.d_lst[i].row + v + 1;
|
||||
}
|
||||
if (h_patt.len % 2) {
|
||||
h_patt.d_lst[h_patt.len-1] = suite->d_base;
|
||||
h_patt.d_lst[h_patt.len-1].row = h_patt.d_lst[h_patt.len-2].row + d + 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
|
||||
for (int bk = 0; bk < get_banks_cnt(); bk++)
|
||||
{
|
||||
for (int idx = 0; idx < h_patt.len; idx++) {
|
||||
h_patt.d_lst[idx].bank = bk;
|
||||
}
|
||||
#ifdef FLIPTABLE
|
||||
print_start_attack(&h_patt);
|
||||
#endif
|
||||
for (int idx = 0; idx < h_patt.len; idx++)
|
||||
fill_row(suite, &h_patt.d_lst[idx], suite->cfg->d_cfg);
|
||||
|
||||
uint64_t time = hammer_it(&h_patt, suite->mem);
|
||||
fprintf(stderr, "%lu ",time);
|
||||
|
||||
scan_rows(suite, &h_patt, 0);
|
||||
for (int idx = 0; idx<h_patt.len; idx++) {
|
||||
fill_row(suite, &h_patt.d_lst[idx], (HammerData)
|
||||
((int)suite->cfg->d_cfg ^ (int)REVERSE));
|
||||
}
|
||||
|
||||
#ifdef FLIPTABLE
|
||||
print_end_attack();
|
||||
#endif
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
free(h_patt.d_lst);
|
||||
}
|
||||
|
||||
void create_dir(const char* dir_name)
|
||||
{
|
||||
struct stat st = {0};
|
||||
if (stat(dir_name, &st) == -1) {
|
||||
mkdir(dir_name, 0777);
|
||||
}
|
||||
}
|
||||
|
||||
void fuzzing_session(SessionConfig * cfg, MemoryBuffer * mem)
|
||||
{
|
||||
int d, v, aggrs;
|
||||
|
||||
srand(CL_SEED);
|
||||
DRAMAddr d_base = phys_2_dram(virt_2_phys(mem->buffer, mem));
|
||||
fprintf(stdout, "[INFO] d_base.row:%lu\n", d_base.row);
|
||||
|
||||
/* Init FILES */
|
||||
create_dir(DATA_DIR);
|
||||
char *out_name = (char *)malloc(500);
|
||||
char rows_str[10];
|
||||
strcpy(out_name, DATA_DIR);
|
||||
strcat(out_name, p->g_out_prefix);
|
||||
strcat(out_name, ".");
|
||||
strcat(out_name, "fuzzing");
|
||||
strcat(out_name, ".");
|
||||
sprintf(rows_str, "%08ld", d_base.row);
|
||||
strcat(out_name, rows_str);
|
||||
strcat(out_name, ".");
|
||||
sprintf(rows_str, "%ld", cfg->h_rounds);
|
||||
strcat(out_name, rows_str);
|
||||
strcat(out_name, ".");
|
||||
strcat(out_name, REFRESH_VAL);
|
||||
strcat(out_name, ".csv");
|
||||
if (p->g_flags & F_NO_OVERWRITE) {
|
||||
int cnt = 0;
|
||||
char *tmp_name = (char *)malloc(500);
|
||||
strncpy(tmp_name, out_name, strlen(out_name));
|
||||
while (access(tmp_name, F_OK) != -1) {
|
||||
cnt++;
|
||||
sprintf(tmp_name, "%s.%02d", out_name, cnt);
|
||||
}
|
||||
strncpy(out_name, tmp_name, strlen(tmp_name));
|
||||
free(tmp_name);
|
||||
}
|
||||
out_fd = fopen(out_name, "w+");
|
||||
assert(out_fd != NULL);
|
||||
|
||||
HammerSuite *suite = (HammerSuite *) malloc(sizeof(HammerSuite));
|
||||
suite->mem = mem;
|
||||
suite->cfg = cfg;
|
||||
suite->d_base = d_base;
|
||||
suite->mapper = (ADDRMapper *) malloc(sizeof(ADDRMapper));
|
||||
init_addr_mapper(suite->mapper, mem, &suite->d_base, cfg->h_rows);
|
||||
|
||||
while(1) {
|
||||
cfg->aggr_n = random_int(2, 32);
|
||||
d = random_int(0, 16);
|
||||
v = random_int(1, 4);
|
||||
fuzz(suite, d, v);
|
||||
}
|
||||
}
|
||||
|
||||
void hammer_session(SessionConfig * cfg, MemoryBuffer * memory)
|
||||
{
|
||||
MemoryBuffer mem = *memory;
|
||||
|
||||
DRAMAddr d_base = phys_2_dram(virt_2_phys(mem.buffer, &mem));
|
||||
d_base.row += cfg->base_off;
|
||||
|
||||
create_dir(DATA_DIR);
|
||||
char *out_name = (char *)malloc(500);
|
||||
char rows_str[10];
|
||||
strcpy(out_name, DATA_DIR);
|
||||
strcat(out_name, p->g_out_prefix);
|
||||
strcat(out_name, ".");
|
||||
|
||||
if (cfg->h_cfg) {
|
||||
char config[15];
|
||||
sprintf(config, config_str[cfg->h_cfg], cfg->aggr_n);
|
||||
strcat(out_name, config);
|
||||
} else {
|
||||
strcat(out_name, config_str[cfg->h_cfg]);
|
||||
}
|
||||
strcat(out_name, ".");
|
||||
sprintf(rows_str, "%08ld", d_base.row);
|
||||
strcat(out_name, rows_str);
|
||||
strcat(out_name, ".");
|
||||
sprintf(rows_str, "%03ld", cfg->h_rows);
|
||||
strcat(out_name, rows_str);
|
||||
strcat(out_name, ".");
|
||||
sprintf(rows_str, "%ld", cfg->h_rounds);
|
||||
strcat(out_name, rows_str);
|
||||
strcat(out_name, ".");
|
||||
strcat(out_name, REFRESH_VAL);
|
||||
strcat(out_name, ".csv");
|
||||
if (p->g_flags & F_NO_OVERWRITE) {
|
||||
int cnt = 0;
|
||||
char *tmp_name = (char *)malloc(500);
|
||||
strncpy(tmp_name, out_name, strlen(out_name));
|
||||
while (access(tmp_name, F_OK) != -1) {
|
||||
cnt++;
|
||||
sprintf(tmp_name, "%s.%02d", out_name, cnt);
|
||||
}
|
||||
strncpy(out_name, tmp_name, strlen(tmp_name));
|
||||
free(tmp_name);
|
||||
}
|
||||
out_fd = fopen(out_name, "w+");
|
||||
|
||||
fprintf(stderr,
|
||||
"[LOG] - Hammer session! access pattern: %s\t data pattern: %s\n",
|
||||
config_str[cfg->h_cfg], data_str[cfg->d_cfg]);
|
||||
fprintf(stderr, "[LOG] - File: %s\n", out_name);
|
||||
|
||||
HammerSuite *suite = (HammerSuite *) malloc(sizeof(HammerSuite));
|
||||
suite->cfg = cfg;
|
||||
suite->mem = &mem;
|
||||
suite->d_base = d_base;
|
||||
suite->mapper = (ADDRMapper *) malloc(sizeof(ADDRMapper));
|
||||
init_addr_mapper(suite->mapper, &mem, &suite->d_base, cfg->h_rows);
|
||||
|
||||
#ifndef FLIPTABLE
|
||||
export_cfg(suite); // export the configuration of the experiment to file.
|
||||
fprintf(out_fd, OUT_HEAD);
|
||||
#endif
|
||||
|
||||
switch (cfg->h_cfg) {
|
||||
case ASSISTED_DOUBLE_SIDED:
|
||||
{
|
||||
suite->hammer_test =
|
||||
(int (*)(void *))assisted_double_sided_test;
|
||||
break;
|
||||
}
|
||||
case FREE_TRIPLE_SIDED:
|
||||
{
|
||||
suite->hammer_test =
|
||||
(int (*)(void *))free_triple_sided_test;
|
||||
break;
|
||||
}
|
||||
case N_SIDED:
|
||||
{
|
||||
assert(cfg->aggr_n > 1);
|
||||
suite->hammer_test = (int (*)(void *))n_sided_test;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
suite->hammer_test = (int (*)(void *))n_sided_test;
|
||||
}
|
||||
}
|
||||
|
||||
suite->hammer_test(suite);
|
||||
fclose(out_fd);
|
||||
tear_down_addr_mapper(suite->mapper);
|
||||
free(suite);
|
||||
}
|
26
hammersuite/src/include/addr-mapper.h
Normal file
26
hammersuite/src/include/addr-mapper.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "memory.h"
|
||||
#include "types.h"
|
||||
#include "dram-address.h"
|
||||
|
||||
typedef struct {
|
||||
DRAMAddr d_addr;
|
||||
char *v_addr;
|
||||
} DRAM_pte;
|
||||
|
||||
typedef struct {
|
||||
DRAM_pte *lst;
|
||||
size_t len;
|
||||
} RowMap;
|
||||
|
||||
typedef struct {
|
||||
size_t base_row; // used as an offset
|
||||
RowMap *row_maps;
|
||||
} ADDRMapper;
|
||||
|
||||
void init_addr_mapper(ADDRMapper * mapper, MemoryBuffer * mem,
|
||||
DRAMAddr * d_base, size_t h_rows);
|
||||
RowMap get_row_map(ADDRMapper * mapper, DRAMAddr * d_addr);
|
||||
DRAM_pte get_dram_pte(ADDRMapper * mapper, DRAMAddr * d_addr);
|
||||
void tear_down_addr_mapper(ADDRMapper * mapper);
|
6
hammersuite/src/include/allocator.h
Normal file
6
hammersuite/src/include/allocator.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
int alloc_buffer(MemoryBuffer * mem);
|
||||
int free_buffer(MemoryBuffer * mem);
|
33
hammersuite/src/include/dram-address.h
Normal file
33
hammersuite/src/include/dram-address.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define HASH_FN_CNT 6
|
||||
|
||||
typedef struct {
|
||||
uint64_t lst[HASH_FN_CNT];
|
||||
uint64_t len;
|
||||
} AddrFns;
|
||||
|
||||
typedef struct {
|
||||
AddrFns h_fns;
|
||||
uint64_t row_mask;
|
||||
uint64_t col_mask;
|
||||
} DRAMLayout;
|
||||
|
||||
typedef struct {
|
||||
/* bank is a simplified addressing of <ch,dimm,rk,bg,bk>
|
||||
where all this will eventually map to a specific bank */
|
||||
uint64_t bank;
|
||||
uint64_t row;
|
||||
uint64_t col;
|
||||
} DRAMAddr;
|
||||
|
||||
physaddr_t dram_2_phys(DRAMAddr d_addr);
|
||||
DRAMAddr phys_2_dram(physaddr_t p_addr);
|
||||
char *dram_2_str(DRAMAddr * d_addr);
|
||||
char *dramLayout_2_str(DRAMLayout * mem_layout);
|
||||
DRAMLayout *get_dram_layout();
|
||||
uint64_t get_banks_cnt();
|
||||
bool d_addr_eq(DRAMAddr * d1, DRAMAddr * d2);
|
||||
bool d_addr_eq_row(DRAMAddr * d1, DRAMAddr * d2);
|
8
hammersuite/src/include/hammer-suite.h
Normal file
8
hammersuite/src/include/hammer-suite.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void hammer_session(SessionConfig * cfg, MemoryBuffer * memory);
|
||||
void fuzzing_session(SessionConfig * cfg, MemoryBuffer * memory);
|
8
hammersuite/src/include/memory.h
Normal file
8
hammersuite/src/include/memory.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <stddef.h>
|
||||
|
||||
void set_physmap(MemoryBuffer * mem);
|
||||
physaddr_t virt_2_phys(char *v_addr, MemoryBuffer * mem);
|
||||
char *phys_2_virt(physaddr_t p_addr, MemoryBuffer * mem);
|
42
hammersuite/src/include/params.h
Normal file
42
hammersuite/src/include/params.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Vrije Universiteit Amsterdam
|
||||
*
|
||||
* This program is licensed under the GPL2+.
|
||||
*/
|
||||
|
||||
#ifndef PARAMS_H
|
||||
#define PARAMS_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ROUNDS_std 1000000
|
||||
#define HUGETLB_std "/mnt/huge/buff"
|
||||
#define CONFIG_NAME_std "tmp/s_cfg.bin"
|
||||
#define O_FILE_std "DIMM00"
|
||||
#define ALLOC_SIZE 1<<30
|
||||
#define ALIGN_std 2<<20
|
||||
#define PATT_LEN 1024
|
||||
#define AGGR_std 2
|
||||
#define HUGE_YES
|
||||
|
||||
typedef struct ProfileParams {
|
||||
uint64_t g_flags = 0;
|
||||
char *g_out_prefix;
|
||||
char *tpat = (char *)NULL;
|
||||
char *vpat = (char *)NULL;
|
||||
int threshold = 0;
|
||||
int fuzzing = 0; // start fuzzing!!
|
||||
size_t m_size = ALLOC_SIZE;
|
||||
size_t m_align = ALIGN_std;
|
||||
size_t rounds = ROUNDS_std;
|
||||
size_t base_off = 0;
|
||||
char *huge_file = (char *)HUGETLB_std;
|
||||
int huge_fd;
|
||||
char *conf_file = (char *)CONFIG_NAME_std;
|
||||
int aggr = AGGR_std;
|
||||
} ProfileParams;
|
||||
|
||||
int process_argv(int argc, char *argv[], ProfileParams *params);
|
||||
|
||||
#endif /* params.h */
|
54
hammersuite/src/include/types.h
Normal file
54
hammersuite/src/include/types.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define STRIPE_SHIFT 0 // not needed anymore
|
||||
#define O2Z (0b01 << STRIPE_SHIFT) // ONE_TO_ZERO
|
||||
#define Z2O (0b10 << STRIPE_SHIFT) // ZERO_TO_ONE
|
||||
#define REVERSE_VAL (O2Z ^ Z2O) // if you xor REVERSE with one of the stripe val it will give you the opposite
|
||||
|
||||
static const char *config_str[] =
|
||||
{ "assisted-dbl", "free-triple", "%i_sided"};
|
||||
static const char *data_str[] = { "random", "i2o", "o2i" };
|
||||
|
||||
typedef enum {
|
||||
ASSISTED_DOUBLE_SIDED,
|
||||
FREE_TRIPLE_SIDED,
|
||||
N_SIDED,
|
||||
} HammerConfig;
|
||||
|
||||
typedef enum {
|
||||
RANDOM,
|
||||
ONE_TO_ZERO = O2Z,
|
||||
ZERO_TO_ONE = Z2O,
|
||||
REVERSE = REVERSE_VAL
|
||||
} HammerData;
|
||||
|
||||
typedef uint64_t physaddr_t;
|
||||
|
||||
/* not necessarily page-aligned addresses.
|
||||
used only to keep track of virt<->phys mapping. */
|
||||
typedef struct {
|
||||
char *v_addr;
|
||||
physaddr_t p_addr;
|
||||
} pte_t;
|
||||
|
||||
typedef struct {
|
||||
HammerConfig h_cfg;
|
||||
HammerData d_cfg;
|
||||
size_t h_rows;
|
||||
size_t h_rounds;
|
||||
size_t base_off; // offset from the beginning of the contig chunk
|
||||
int aggr_n;
|
||||
} SessionConfig;
|
||||
|
||||
typedef struct {
|
||||
char *buffer; // base addr
|
||||
pte_t *physmap; // list of virt<->phys mapping for every page
|
||||
int fd; // fd in the case of mmap hugetlbfs
|
||||
uint64_t size; // in bytes
|
||||
uint64_t align;
|
||||
uint64_t flags; // from params
|
||||
} MemoryBuffer;
|
128
hammersuite/src/include/utils.h
Normal file
128
hammersuite/src/include/utils.h
Normal file
@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define BIT_SET(x) (1ULL<<(x))
|
||||
#define BIT_VAL(b,val) (((val) >> (b)) & 1)
|
||||
#define KB(x) ((x)<<10ULL)
|
||||
#define MB(x) ((x)<<20ULL)
|
||||
#define GB(x) ((x)<<30ULL)
|
||||
#define CL_SHIFT 6
|
||||
#define CL_SIZE 64
|
||||
#define PAGE_SIZE 4096
|
||||
#define ROW_SIZE (8<<10)
|
||||
|
||||
#define ALIGN_TO(X, Y) ((X) & (~((1LL<<(Y))-1LL))) // Mask out the lower Y bits
|
||||
#define LS_BITMASK(X) ((1LL<<(X))-1LL) // Mask only the lower X bits
|
||||
|
||||
// Flags
|
||||
#define F_CLEAR 0L
|
||||
#define F_VERBOSE BIT_SET(0)
|
||||
#define F_EXPORT BIT_SET(1)
|
||||
#define F_CONFIG BIT_SET(2)
|
||||
#define F_NO_OVERWRITE BIT_SET(3)
|
||||
#define MEM_SHIFT (30L)
|
||||
#define MEM_MASK 0b11111ULL << MEM_SHIFT
|
||||
#define F_ALLOC_HUGE BIT_SET(MEM_SHIFT)
|
||||
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT_SET(MEM_SHIFT+1)
|
||||
#define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT_SET(MEM_SHIFT+2)
|
||||
#define F_POPULATE BIT_SET(MEM_SHIFT+3)
|
||||
|
||||
#define NOT_FOUND ((void*) -1)
|
||||
#define NOT_OPENED -1
|
||||
|
||||
#define TIMESPEC_NSEC(ts) ((ts)->tv_sec * 1e9 + (ts)->tv_nsec)
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Static functions
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void clflush(volatile void *p)
|
||||
{
|
||||
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void clflushopt(volatile void *p)
|
||||
{
|
||||
#ifdef DDR3
|
||||
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
|
||||
#else
|
||||
asm volatile ("clflushopt (%0)\n"::"r" (p):"memory");
|
||||
#
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void cpuid()
|
||||
{
|
||||
asm volatile ("cpuid":::"rax", "rbx", "rcx", "rdx");
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void mfence()
|
||||
{
|
||||
asm volatile ("mfence":::"memory");
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void sfence()
|
||||
{
|
||||
asm volatile ("sfence":::"memory");
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
void lfence()
|
||||
{
|
||||
asm volatile ("lfence":::"memory");
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
uint64_t rdtscp(void)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
asm volatile ("rdtscp\n":"=a" (lo), "=d"(hi)
|
||||
::"%rcx");
|
||||
return (hi << 32) | lo;
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
uint64_t rdtsc(void)
|
||||
{
|
||||
uint64_t lo, hi;
|
||||
asm volatile ("rdtsc\n":"=a" (lo), "=d"(hi)
|
||||
::"%rcx");
|
||||
return (hi << 32) | lo;
|
||||
}
|
||||
|
||||
static inline __attribute__ ((always_inline))
|
||||
uint64_t realtime_now()
|
||||
{
|
||||
struct timespec now_ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now_ts);
|
||||
return TIMESPEC_NSEC(&now_ts);
|
||||
}
|
||||
|
||||
// void set_physmap(mem_buff_t* mem);
|
||||
|
||||
// pte_t get_pte(char* v_addr, mem_buff_t* mem);
|
||||
|
||||
// addr_tuple reverse_addr_tuple(uint64_t p_addr, mem_buff_t* mem);
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Helpers
|
||||
int gt(const void *a, const void *b);
|
||||
|
||||
double mean(uint64_t * vals, size_t size);
|
||||
|
||||
uint64_t median(uint64_t * vals, size_t size);
|
||||
|
||||
char *bit_string(uint64_t val);
|
||||
|
||||
char *int_2_bin(uint64_t val);
|
||||
|
||||
char *get_rnd_addr(char *base, size_t m_size, size_t align);
|
||||
|
||||
int get_rnd_int(int min, int max);
|
111
hammersuite/src/main.c
Normal file
111
hammersuite/src/main.c
Normal file
@ -0,0 +1,111 @@
|
||||
#include "stdio.h"
|
||||
// #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "include/utils.h"
|
||||
#include "include/types.h"
|
||||
#include "include/allocator.h"
|
||||
#include "include/memory.h"
|
||||
#include "include/dram-address.h"
|
||||
#include "include/hammer-suite.h"
|
||||
#include "include/params.h"
|
||||
|
||||
ProfileParams *p;
|
||||
|
||||
DRAMLayout g_mem_layout = {{{0x4080,0x48000,0x90000,0x120000,0x1b300}, 5}, 0xffffc0000, ROW_SIZE-1};
|
||||
|
||||
void read_config(SessionConfig * cfg, char *f_name)
|
||||
{
|
||||
FILE *fp = fopen(f_name, "rb");
|
||||
int p_size;
|
||||
size_t res;
|
||||
assert(fp != NULL);
|
||||
res = fread(cfg, sizeof(SessionConfig), 1, fp);
|
||||
assert(res == 1);
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
void gmem_dump()
|
||||
{
|
||||
FILE *fp = fopen("g_mem_dump.bin", "wb+");
|
||||
fwrite(&g_mem_layout, sizeof(DRAMLayout), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
#ifdef DEBUG
|
||||
DRAMLayout tmp;
|
||||
fp = fopen("g_mem_dump.bin", "rb");
|
||||
fread(&tmp, sizeof(DRAMLayout), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
assert(tmp->h_fns->len == g_mem_layout->h_fns->len);
|
||||
assert(tmp->bank == g_mem_layout->bank);
|
||||
assert(tmp->row == g_mem_layout->row);
|
||||
assert(tmp->col == g_mem_layout->col);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
srand(time(NULL));
|
||||
p = (ProfileParams*)malloc(sizeof(ProfileParams));
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "[ERROR] Memory allocation\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(process_argv(argc, argv, p) == -1) {
|
||||
free(p);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MemoryBuffer mem = {
|
||||
.buffer = NULL,
|
||||
.physmap = NULL,
|
||||
.fd = p->huge_fd,
|
||||
.size = p->m_size,
|
||||
.align = p->m_align,
|
||||
.flags = p->g_flags & MEM_MASK
|
||||
};
|
||||
|
||||
alloc_buffer(&mem);
|
||||
set_physmap(&mem);
|
||||
gmem_dump();
|
||||
|
||||
SessionConfig s_cfg;
|
||||
memset(&s_cfg, 0, sizeof(SessionConfig));
|
||||
if (p->g_flags & F_CONFIG) {
|
||||
read_config(&s_cfg, p->conf_file);
|
||||
} else {
|
||||
// HARDCODED values
|
||||
s_cfg.h_rows = PATT_LEN;
|
||||
s_cfg.h_rounds = p->rounds;
|
||||
s_cfg.h_cfg = N_SIDED;
|
||||
s_cfg.d_cfg = RANDOM;
|
||||
s_cfg.base_off = p->base_off;
|
||||
s_cfg.aggr_n = p->aggr;
|
||||
}
|
||||
|
||||
if (p->fuzzing) {
|
||||
fuzzing_session(&s_cfg, &mem);
|
||||
} else {
|
||||
hammer_session(&s_cfg, &mem);
|
||||
}
|
||||
|
||||
close(p->huge_fd);
|
||||
return 0;
|
||||
}
|
108
hammersuite/src/memory.c
Normal file
108
hammersuite/src/memory.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include "memory.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEF_RNG_LEN (8<<10)
|
||||
#define DEBUG
|
||||
#define DEBUG_LINE fprintf(stderr, "[DEBUG] - GOT HERE\n");
|
||||
|
||||
static physaddr_t base_phys = 0L;
|
||||
|
||||
uint64_t get_pfn(uint64_t entry)
|
||||
{
|
||||
return ((entry) & 0x7fffffffffffffff);
|
||||
}
|
||||
|
||||
physaddr_t get_physaddr(uint64_t v_addr, int pmap_fd)
|
||||
{
|
||||
uint64_t entry;
|
||||
uint64_t offset = (v_addr / 4096) * sizeof(entry);
|
||||
uint64_t pfn;
|
||||
bool to_open = false;
|
||||
// assert(fd >= 0);
|
||||
if (pmap_fd == NOT_OPENED) {
|
||||
pmap_fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
assert(pmap_fd >= 0);
|
||||
to_open = true;
|
||||
}
|
||||
// int rd = fread(&entry, sizeof(entry), 1 ,fp);
|
||||
int bytes_read = pread(pmap_fd, &entry, sizeof(entry), offset);
|
||||
|
||||
assert(bytes_read == 8);
|
||||
assert(entry & (1ULL << 63));
|
||||
|
||||
if (to_open) {
|
||||
close(pmap_fd);
|
||||
}
|
||||
|
||||
pfn = get_pfn(entry);
|
||||
assert(pfn != 0);
|
||||
return (pfn << 12) | (v_addr & 4095);
|
||||
}
|
||||
|
||||
int phys_cmp(const void *p1, const void *p2)
|
||||
{
|
||||
return ((pte_t *) p1)->p_addr - ((pte_t *) p2)->p_addr;
|
||||
}
|
||||
|
||||
// WARNING optimization works only with contiguous memory!!
|
||||
void set_physmap(MemoryBuffer * mem)
|
||||
{
|
||||
int l_size = mem->size / PAGE_SIZE;
|
||||
pte_t *physmap = (pte_t *) malloc(sizeof(pte_t) * l_size);
|
||||
int pmap_fd = open("/proc/self/pagemap", O_RDONLY);
|
||||
assert(pmap_fd >= 0);
|
||||
|
||||
base_phys = get_physaddr((uint64_t) mem->buffer, pmap_fd);
|
||||
for (uint64_t tmp = (uint64_t) mem->buffer, idx = 0;
|
||||
tmp < (uint64_t) mem->buffer + mem->size; tmp += PAGE_SIZE) {
|
||||
pte_t tmp_pte = { (char *)tmp, get_physaddr(tmp, pmap_fd) };
|
||||
physmap[idx] = tmp_pte;
|
||||
idx++;
|
||||
}
|
||||
|
||||
qsort(physmap, mem->size / PAGE_SIZE, sizeof(pte_t), phys_cmp);
|
||||
close(pmap_fd);
|
||||
mem->physmap = physmap;
|
||||
}
|
||||
|
||||
physaddr_t virt_2_phys(char *v_addr, MemoryBuffer * mem)
|
||||
{
|
||||
for (int i = 0; i < mem->size / PAGE_SIZE; i++) {
|
||||
if (mem->physmap[i].v_addr ==
|
||||
(char *)((uint64_t) v_addr & ~((uint64_t) (PAGE_SIZE - 1))))
|
||||
{
|
||||
|
||||
return mem->physmap[i].
|
||||
p_addr | ((uint64_t) v_addr &
|
||||
((uint64_t) PAGE_SIZE - 1));
|
||||
}
|
||||
}
|
||||
return (physaddr_t) NOT_FOUND;
|
||||
}
|
||||
|
||||
char *phys_2_virt(physaddr_t p_addr, MemoryBuffer * mem)
|
||||
{
|
||||
physaddr_t p_page = p_addr & ~(((uint64_t) PAGE_SIZE - 1));
|
||||
pte_t src_pte = {.v_addr = 0,.p_addr = p_page };
|
||||
pte_t *res_pte =
|
||||
(pte_t *) bsearch(&src_pte, mem->physmap, mem->size / PAGE_SIZE,
|
||||
sizeof(pte_t), phys_cmp);
|
||||
|
||||
if (res_pte == NULL)
|
||||
return (char *)NOT_FOUND;
|
||||
|
||||
return (char *)((uint64_t) res_pte->
|
||||
v_addr | ((uint64_t) p_addr &
|
||||
(((uint64_t) PAGE_SIZE - 1))));
|
||||
}
|
218
hammersuite/src/params.c
Normal file
218
hammersuite/src/params.c
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Andrei Tatar
|
||||
* Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "include/params.h"
|
||||
#include "include/utils.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
void print_usage(char *bin_name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[ HELP ] - Usage ./%s [-h] [-r rounds] [-a aggr] [-o o_file] [-v] [--mem mem_size] [--[huge/HUGE] f_name] [--conf f_name] [--align val] [--off val] [--no-overwrite] [--fuzzing]\n",
|
||||
bin_name);
|
||||
fprintf(stderr, "\t-h\t\t\t= this help message\n");
|
||||
fprintf(stderr, "\t-v\t\t\t= verbose\n\n");
|
||||
fprintf(stderr, "\t-r rounds\t\t= number of rounds per tuple\t\t\t(default: %d)\n", ROUNDS_std);
|
||||
fprintf(stderr, "\t-a --aggr\t\t= number of aggressors\t\t\t\t(default: %d)\n", AGGR_std);
|
||||
fprintf(stderr, "\t-o --o_file_prefix\t= prefix for output files\t\t\t(default: %s)\n", O_FILE_std);
|
||||
fprintf(stderr, "\t--mem mem_size\t\t= allocation size\t\t\t\t(default: %ld)\n",
|
||||
(uint64_t) ALLOC_SIZE);
|
||||
fprintf(stderr, "\t--huge f_name\t\t= hugetlbfs entry (1GB if HUGE)\t\t\t(default: %s)\n",
|
||||
HUGETLB_std);
|
||||
fprintf(stderr, "\t--conf f_name\t\t= SessionConfig file\t\t\t\t(default: %s)\n",
|
||||
CONFIG_NAME_std);
|
||||
fprintf(stderr, "\t--align val\t\t= alignment of the buffer\t\t\t(default: %ld)\n",
|
||||
(uint64_t) ALIGN_std);
|
||||
fprintf(stderr, "\t--off val\t\t= offset from first row\t\t\t\t(default: 0)\n");
|
||||
fprintf(stderr, "\t--no-overwrite\t\t= don't overwrite previous file\n");
|
||||
fprintf(stderr, "\t-V --victim-pattern\t= hex value for the victim patter\n");
|
||||
fprintf(stderr, "\t-T --target-pattern\t= hex value for the target pattern\n");
|
||||
fprintf(stderr, "\t-f --fuzzing\t\t= Start fuzzing (--aggr will be ignored)\n");
|
||||
fprintf(stderr, "\t-t --threshold\t\t= Align the hammering to refresh ops,\n\t\t\t\t looking at the memory latency in CPU cycles.\t(default: 0)\n");
|
||||
}
|
||||
|
||||
static int str2pat(const char *str, char **pat)
|
||||
{
|
||||
char *endp = NULL;
|
||||
char tmp[3];
|
||||
char *p;
|
||||
tmp[2] = '\0';
|
||||
size_t len = strlen(str);
|
||||
if (len % 2) {
|
||||
return EINVAL;
|
||||
}
|
||||
len /= 2;
|
||||
|
||||
p = (char *)malloc(len);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
tmp[0] = str[2 * i];
|
||||
tmp[1] = str[2 * i + 1];
|
||||
errno = 0;
|
||||
((uint8_t *) p)[i] = (uint8_t) strtol(tmp, &endp, 16);
|
||||
if (errno) {
|
||||
free(p);
|
||||
return errno;
|
||||
}
|
||||
if (*endp != '\0') {
|
||||
free(p);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
*pat = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_argv(int argc, char *argv[], ProfileParams *p)
|
||||
{
|
||||
|
||||
/* Default */
|
||||
p->g_flags = 0;
|
||||
p->tpat = (char *)NULL;
|
||||
p->vpat = (char *)NULL;
|
||||
p->threshold = 0;
|
||||
p->fuzzing = 0; // start fuzzing!!
|
||||
p->m_size = ALLOC_SIZE;
|
||||
p->m_align = ALIGN_std;
|
||||
p->rounds = ROUNDS_std;
|
||||
p->base_off = 0;
|
||||
p->huge_file = (char *)HUGETLB_std;
|
||||
p->conf_file = (char *)CONFIG_NAME_std;
|
||||
p->aggr = AGGR_std;
|
||||
|
||||
|
||||
const struct option long_options[] = {
|
||||
/* These options set a flag. */
|
||||
{"mem", required_argument, 0, 0},
|
||||
{"align", required_argument, 0, 0},
|
||||
{"huge", optional_argument, 0, 0},
|
||||
{"HUGE", optional_argument, 0, 0},
|
||||
{"conf", optional_argument, 0, 0},
|
||||
{"off", required_argument, 0, 0},
|
||||
{"no-overwrite", no_argument, 0, 0},
|
||||
{.name = "target-pattern",.has_arg = required_argument,.flag = NULL,.val='T'},
|
||||
{.name = "victim-pattern",.has_arg = required_argument,.flag = NULL,.val = 'V'},
|
||||
{.name = "aggr",.has_arg = required_argument,.flag = NULL,.val='a'},
|
||||
{.name = "fuzzing",.has_arg = no_argument,.flag = &p->fuzzing,.val = 1},
|
||||
{.name = "threshold",.has_arg = required_argument,.flag = NULL,.val = 't'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
p->g_out_prefix = (char *)O_FILE_std;
|
||||
p->g_flags |= F_POPULATE;
|
||||
|
||||
while (1) {
|
||||
int this_option_optind = optind ? optind : 1;
|
||||
int option_index = 0;
|
||||
int arg = getopt_long(argc, argv, "o:d:r:hvV:T:a:ft:",
|
||||
long_options, &option_index);
|
||||
|
||||
if (arg == -1)
|
||||
break;
|
||||
|
||||
switch (arg) {
|
||||
case 0:
|
||||
switch (option_index) {
|
||||
case 0:
|
||||
p->m_size = atoi(optarg);
|
||||
break;
|
||||
case 1:
|
||||
p->m_align = atoi(optarg);
|
||||
break;
|
||||
case 2:
|
||||
p->g_flags |= F_ALLOC_HUGE_2M;
|
||||
case 3:
|
||||
p->huge_file = (char *)malloc(sizeof(char) * strlen(optarg));
|
||||
strncpy(p->huge_file, optarg, strlen(optarg));
|
||||
p->g_flags |= F_ALLOC_HUGE_1G;
|
||||
break;
|
||||
case 4:
|
||||
p->g_flags |= F_CONFIG;
|
||||
if (!optarg)
|
||||
break;
|
||||
p->conf_file = (char *)malloc(sizeof(char) * strlen(optarg));
|
||||
strncpy(p->conf_file, optarg, strlen(optarg));
|
||||
break;
|
||||
case 5:
|
||||
p->base_off = atoi(optarg);
|
||||
break;
|
||||
case 6:
|
||||
p->g_flags |= F_NO_OVERWRITE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
p->g_out_prefix = (char *)malloc(sizeof(char) * strlen(optarg));
|
||||
strncpy(p->g_out_prefix, optarg, strlen(optarg));
|
||||
p->g_flags |= F_EXPORT;
|
||||
break;
|
||||
case 'r':
|
||||
p->rounds = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
p->g_flags |= F_VERBOSE;
|
||||
break;
|
||||
case 'a':
|
||||
p->aggr = atoi(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
if (str2pat(optarg, &(p->vpat))) {
|
||||
fprintf(stderr, "Invalid target fill pattern: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'V':
|
||||
if (str2pat(optarg, &(p->tpat))) {
|
||||
fprintf(stderr, "Invalid victim fill pattern: %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
p->fuzzing = 1;
|
||||
break;
|
||||
case 't':
|
||||
p->threshold = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef HUGE_YES
|
||||
p->g_flags |= F_ALLOC_HUGE_1G;
|
||||
#endif
|
||||
|
||||
if (p->g_flags & (F_ALLOC_HUGE_2M | F_ALLOC_HUGE_1G)) {
|
||||
if ((p->huge_fd = open(p->huge_file, O_CREAT | O_RDWR, 0755)) == -1) {
|
||||
perror("[ERROR] - Unable to open hugetlbfs");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
81
hammersuite/src/utils.c
Normal file
81
hammersuite/src/utils.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define PAGE_BITS 12
|
||||
|
||||
#define FLAGS (MAP_PRIVATE | MAP_POPULATE | MAP_HUGETLB | (30<<MAP_HUGE_SHIFT))
|
||||
|
||||
char *get_rnd_addr(char *base, size_t m_size, size_t align)
|
||||
{
|
||||
return (char *)((((uint64_t) base) + (rand() % m_size)) &
|
||||
(~((uint64_t) align - 1)));
|
||||
}
|
||||
|
||||
int get_rnd_int(int min, int max)
|
||||
{
|
||||
return rand() % (max + 1 - min) + min;
|
||||
}
|
||||
|
||||
double mean(uint64_t * vals, size_t size)
|
||||
{
|
||||
uint64_t avg = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
avg += vals[i];
|
||||
}
|
||||
return ((double)avg) / size;
|
||||
}
|
||||
|
||||
int gt(const void *a, const void *b)
|
||||
{
|
||||
return (*(int *)a - *(int *)b);
|
||||
}
|
||||
|
||||
uint64_t median(uint64_t * vals, size_t size)
|
||||
{
|
||||
qsort(vals, size, sizeof(uint64_t), gt);
|
||||
return ((size % 2) ==
|
||||
0) ? vals[size / 2] : (vals[(size_t) size / 2] +
|
||||
vals[((size_t) size / 2 + 1)]) / 2;
|
||||
}
|
||||
|
||||
char *bit_string(uint64_t val)
|
||||
{
|
||||
static char bit_str[256];
|
||||
char itoa_str[8];
|
||||
strcpy(bit_str, "");
|
||||
for (int shift = 0; shift < 64; shift++) {
|
||||
if ((val >> shift) & 1) {
|
||||
if (strcmp(bit_str, "") != 0) {
|
||||
strcat(bit_str, "+ ");
|
||||
}
|
||||
sprintf(itoa_str, "%d ", shift);
|
||||
strcat(bit_str, itoa_str);
|
||||
}
|
||||
}
|
||||
|
||||
return bit_str;
|
||||
}
|
||||
|
||||
char *int_2_bin(uint64_t val)
|
||||
{
|
||||
static char bit_str[256];
|
||||
char itoa_str[8];
|
||||
strcpy(bit_str, "0b");
|
||||
for (int shift = 64 - __builtin_clzl(val); shift >= 0; --shift) {
|
||||
sprintf(itoa_str, "%d", (int)(val >> shift) & 1);
|
||||
strcat(bit_str, itoa_str);
|
||||
}
|
||||
|
||||
return bit_str;
|
||||
}
|
29
py/hammerstats.py
Executable file
29
py/hammerstats.py
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
# Copyright (c) 2016 Andrei Tatar
|
||||
# Copyright (c) 2018 Vrije Universiteit Amsterdam
|
||||
#
|
||||
# This program is licensed under the GPL2+.
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
from hammertime import fliptable
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print("Missing arguments")
|
||||
print("usage: {} PROFILE_PATH [...]".format(sys.argv[0]))
|
||||
else:
|
||||
try:
|
||||
for fn in sys.argv[1:]:
|
||||
print('Stats for {}:'.format(fn))
|
||||
ft = fliptable.Fliptable.load_file(fn)
|
||||
natks = str(len(ft))
|
||||
print('Hammers: {}'.format(natks))
|
||||
flips = [len(x.flips) for x in ft if x.flips]
|
||||
print('w/flips: {{:{}d}}'.format(len(natks)).format(len(flips)))
|
||||
print('Total Bit Flips: {}'.format(sum(flips)))
|
||||
except KeyboardInterrupt:
|
||||
print('Interrupted, exiting...')
|
3
py/hammertime/__init__.py
Normal file
3
py/hammertime/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
__all__ = ['sim', 'fliptable', 'dramtrans']
|
10
py/hammertime/_native.c
Normal file
10
py/hammertime/_native.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include "stdint.h"
|
||||
|
||||
uint64_t parity(uint64_t v) {
|
||||
return __builtin_parityl(v);
|
||||
}
|
||||
|
||||
|
||||
uint64_t ctzl(uint64_t v) {
|
||||
return __builtin_ctzl(v);
|
||||
}
|
BIN
py/hammertime/_native.so
Executable file
BIN
py/hammertime/_native.so
Executable file
Binary file not shown.
171
py/hammertime/dramtrans.py
Normal file
171
py/hammertime/dramtrans.py
Normal file
@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import functools
|
||||
|
||||
|
||||
HASH_FN_CNT = 6
|
||||
_native = None
|
||||
|
||||
@functools.total_ordering
|
||||
class DRAMAddr(ctypes.Structure):
|
||||
_fields_ = [('bank', ctypes.c_uint64),
|
||||
('row', ctypes.c_uint64),
|
||||
('col', ctypes.c_uint64)]
|
||||
|
||||
def __str__(self):
|
||||
return 'b{0.bank:02d}.r{0.row:06d}.c{0.col:04d}'.format(self)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
# def __repr__(self):
|
||||
# return '{0}(b={1.bank},r={1.row},c={1.col})'.format(type(self).__name__, self)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, DRAMAddr):
|
||||
return self.numeric_value == other.numeric_value
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
if isinstance(other, DRAMAddr):
|
||||
return self.numeric_value < other.numeric_value
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self):
|
||||
return self.numeric_value
|
||||
|
||||
def __len__(self):
|
||||
return len(self._fields_)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return getattr(self, self._fields_[key][0])
|
||||
elif isinstance(key, slice):
|
||||
start, stop, step = key.indices(len(self._fields_))
|
||||
return tuple(getattr(self, self._fields_[k][0]) for k in range(start, stop, step))
|
||||
else:
|
||||
raise TypeError('{} object cannot be indexed by {}'.format(type(self).__name__, type(key).__name__))
|
||||
|
||||
def same_bank(self, other):
|
||||
return self.bank == other.bank
|
||||
|
||||
@property
|
||||
def numeric_value(self):
|
||||
return (self.col + (self.row << 16) + (self.bank << 32))
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, DRAMAddr):
|
||||
return type(self)(
|
||||
self.bank + other.bank,
|
||||
self.row + other.row,
|
||||
self.col + other.col
|
||||
)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, DRAMAddr):
|
||||
return type(self)(
|
||||
self.bank - other.bank,
|
||||
self.row - other.row,
|
||||
self.col - other.col
|
||||
)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class _AddrFns(ctypes.Structure):
|
||||
_fields_ = [("lst", ctypes.c_uint64* HASH_FN_CNT),
|
||||
("len", ctypes.c_uint64)]
|
||||
|
||||
|
||||
class _DRAMLayout(ctypes.Structure):
|
||||
_fields_ = [("h_fns", _AddrFns),
|
||||
("row_mask", ctypes.c_uint64),
|
||||
("col_mask", ctypes.c_uint64)]
|
||||
|
||||
def __init__(self, upack):
|
||||
self.h_fns.lst = (ctypes.c_uint64*6) (*[0x2040,0x44000,0x88000,0x110000,0x220000,0x00])
|
||||
self.h_fns.len = 5
|
||||
self.row_mask = 0xffffc0000
|
||||
self.col_mask = ((1<<13)-1)
|
||||
# self.h_fns.lst = upack[0:HASH_FN_CNT]
|
||||
# self.h_fns.len = upack[HASH_FN_CNT]
|
||||
# self.row_mask = upack[HASH_FN_CNT+1]
|
||||
# self.col_mask = upack[HASH_FN_CNT+2]
|
||||
|
||||
@property
|
||||
def num_banks(self):
|
||||
return 1<<self.AddrFns.len
|
||||
|
||||
def get_dram_row(self, p_addr):
|
||||
return (p_addr & self.row_mask) >> _native.ctzl(self.row_mask)
|
||||
|
||||
def get_dram_row(self, p_addr):
|
||||
return (p_addr & self.col_mask) >> _native.ctzl(self.col_mask)
|
||||
|
||||
|
||||
class MemorySystem(ctypes.Structure):
|
||||
_fields_ = [('mem_layout', _DRAMLayout)]
|
||||
|
||||
def __init__(self):
|
||||
if _native == None:
|
||||
load_native_funcs()
|
||||
|
||||
return super().__init__()
|
||||
|
||||
def load(self, s):
|
||||
DRAMLAyout_fmt = f"{HASH_FN_CNT}QQQQ"
|
||||
self.mem_layout = _DRAMLayout(struct.unpack(DRAMLAyout_fmt, s))
|
||||
|
||||
def load_file(self, fname):
|
||||
with open(fname, 'rb') as f:
|
||||
return self.load(f.read())
|
||||
|
||||
def num_banks(self):
|
||||
return self.mem_layout.num_banks
|
||||
|
||||
|
||||
def resolve(self, p_addr):
|
||||
mem = self.mem_layout
|
||||
d_addr = DRAMAddr(0,0,0)
|
||||
for i in range(mem.h_fns.len):
|
||||
d_addr.bank |= (_native.parity(p_addr & mem.h_fns.lst[i]) << i)
|
||||
d_addr.row = mem.get_dram_row(p_addr)
|
||||
d_addr.col = mem.get_dram_col(p_addr)
|
||||
|
||||
return d_addr
|
||||
|
||||
|
||||
def resolve_reverse(self, d_addr):
|
||||
mem = self.mem_layout
|
||||
p_addr = (d_addr.row << _native.ctzl(mem.row_mask))
|
||||
p_addr |= (d_addr.col << _native.ctzl(mem.col_mask))
|
||||
for i in range(mem.h_fns.len):
|
||||
masked_addr = p_addr & mem.h_fns.lst[i]
|
||||
if _native.parity(masked_addr) == ((d_addr.bank >> i) & 1):
|
||||
continue
|
||||
# else flip a bit of the address so that the address respects the dram h_fn
|
||||
# that is get only bits not affecting the row.
|
||||
h_lsb = _native.ctzl((mem.h_fns.lst[i]) & ~(mem.col_mask) & ~(mem.row_mask))
|
||||
p_addr ^= 1<<h_lsb
|
||||
|
||||
return p_addr
|
||||
|
||||
|
||||
|
||||
|
||||
def load_native_funcs():
|
||||
global _native
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
_native = ctypes.CDLL(os.path.join(path, "_native.so"))
|
||||
_native.parity.restype = ctypes.c_uint64
|
||||
_native.parity.argtypes= [ctypes.c_uint64]
|
||||
_native.ctzl.restype = ctypes.c_uint64
|
||||
_native.ctzl.argtypes= [ctypes.c_uint64]
|
285
py/hammertime/fliptable.py
Normal file
285
py/hammertime/fliptable.py
Normal file
@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
# Copyright (c) 2016 Andrei Tatar
|
||||
# Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Module providing utilities for working with profile output and fliptables."""
|
||||
|
||||
import re
|
||||
import pprint as pp
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
from hammertime.dramtrans import DRAMAddr
|
||||
|
||||
RE_DICT = re.compile("(?P<key>[-\d\w]+)\s*:\s*(?P<val>[-.\d\w]+)")
|
||||
#ADDR_RE = re.compile("r(?P<row>\d+)\.bk(?P<bk>\d+)(\.col(?P<col>\d+))?(\.p(?P<prob>\d+))?")
|
||||
ADDR_RE = re.compile("r(?P<row>\d+)\.bk(?P<bank>\d+)(\.col(?P<col>\d+))?")
|
||||
#ADDR_FMT = r'\((\w+)\s+(\w+)\s*(\w+)?\)'
|
||||
#ADDR_RE = re.compile(ADDR_FMT)
|
||||
# 87,c7,r16447.bk18.col3994
|
||||
BFLIP_FMT = r'(?P<exp>\w{2}),(?P<got>\w{2}),(?P<addr>[.\w\d]+)'
|
||||
BFLIP_RE = re.compile(BFLIP_FMT)
|
||||
ADDR_TRG_RE = re.compile("b(?P<bk>[\d\w]+)\.r(?P<row>[\d\w]+)")
|
||||
#VICT_FMT = r'{} (?:{}\s?)+'.format(ADDR_FMT, BFLIP_FMT)
|
||||
#VICT_RE = re.compile(VICT_FMT)
|
||||
|
||||
|
||||
class Corruption(namedtuple('Corruption', ['addr', 'got', 'exp'])):
|
||||
def __str__(self):
|
||||
return '({0.addr}|{0.got:02x}|{0.exp:02x})'.format(self)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def to_flips(self):
|
||||
flips = []
|
||||
pup = ~self.exp & self.got & 0xff
|
||||
pdn = self.exp & ~self.got & 0xff
|
||||
bit = 0
|
||||
while pup:
|
||||
mask = 1 << bit
|
||||
if (pup & mask):
|
||||
flips.append(Flip(self.addr, bit, True))
|
||||
pup &= ~mask
|
||||
bit += 1
|
||||
bit = 0
|
||||
while pdn:
|
||||
mask = 1 << bit
|
||||
if (pdn & mask):
|
||||
flips.append(Flip(self.addr, bit, False))
|
||||
pdn &= ~mask
|
||||
bit += 1
|
||||
return flips
|
||||
|
||||
|
||||
class Flip(namedtuple('Flip', ['addr', 'bit', 'pullup'])):
|
||||
def to_corruption(self, og=None):
|
||||
fmask = 1 << (self.bit % 8)
|
||||
if og is None:
|
||||
pat = 0 if self.pullup else 0xff
|
||||
else:
|
||||
pat = og
|
||||
val = pat | fmask if self.pullup else pat & ~fmask
|
||||
return Corruption(addr=self.addr, got=val, exp=pat)
|
||||
|
||||
def to_physmem(self, msys):
|
||||
return type(self)(msys.resolve_reverse(self.addr), self.bit, self.pullup)
|
||||
|
||||
|
||||
Diff = namedtuple('Diff', ['self_only', 'common', 'other_only'])
|
||||
|
||||
|
||||
class Attack(namedtuple('Attack', ['targets', 'flips'])):
|
||||
def diff(self, other):
|
||||
if not isinstance(other, type(self)):
|
||||
raise TypeError('Attack instance expected for diff')
|
||||
elif not self.targets == other.targets:
|
||||
raise ValueError('Cannot diff attacks with different targets')
|
||||
else:
|
||||
return Diff(
|
||||
type(self)(self.targets, self.flips - other.flips),
|
||||
type(self)(self.targets, self.flips & other.flips),
|
||||
type(self)(self.targets, other.flips - self.flips)
|
||||
)
|
||||
|
||||
def merge(self, other):
|
||||
if not isinstance(other, type(self)):
|
||||
raise TypeError('Attack instance expected for merge')
|
||||
elif not self.targets == other.targets:
|
||||
raise ValueError('Cannot merge attacks with different targets')
|
||||
else:
|
||||
return type(self)(self.targets, self.flips | other.flips)
|
||||
|
||||
def to_corruptions(self, pat=None):
|
||||
return ((x.addr, x.to_corruption(pat)) for x in self)
|
||||
|
||||
def to_physmem(self, msys):
|
||||
return type(self)(
|
||||
targets=tuple(msys.resolve_reverse(x) for x in self.targets),
|
||||
flips={x.to_physmem(msys) for x in self.flips}
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(sorted(self.flips))
|
||||
|
||||
@classmethod
|
||||
def __is_flip_far(cls, targets, victim):
|
||||
close = False
|
||||
for t in targets:
|
||||
if abs(t.row - victim.row) == 1:
|
||||
close = True
|
||||
if not close:
|
||||
print("Found flip far away from target row")
|
||||
print(targets)
|
||||
print(victim)
|
||||
|
||||
@classmethod
|
||||
def decode_line(cls, line, msys=None):
|
||||
def parse_addr(s):
|
||||
res = ADDR_RE.match(s)
|
||||
if res == None:
|
||||
raise Exception("Couldn't parse")
|
||||
return DRAMAddr(**({k:int(v) for (k,v) in res.groupdict().items() if v != None}))
|
||||
|
||||
def parse_addr_trg(s):
|
||||
res = ADDR_TRG_RE.match(s)
|
||||
if res == None:
|
||||
raise Exception("Couldn't parse")
|
||||
return DRAMAddr(**({k:int(v) for (k,v) in res.groupdict().items() if v != None}))
|
||||
|
||||
targ, vict, *o = line.split(':')
|
||||
flips = set()
|
||||
targets = [parse_addr(s) for s in line.split('/')]
|
||||
|
||||
for x in BFLIP_RE.finditer(vict):
|
||||
dct = x.groupdict()
|
||||
dct['addr'] = parse_addr(dct['addr'])
|
||||
dct['got'] = int(dct['got'], 16)
|
||||
dct['exp'] = int(dct['exp'], 16)
|
||||
cls.__is_flip_far(targets, dct['addr'])
|
||||
flips.update(Corruption(**(dct)).to_flips())
|
||||
|
||||
# vaddr = DRAMAddr(*(int(v, 16) for v in x.group(*range(1,4)) if v is not None))
|
||||
# vcorr = [Corruption(*(int(v, 16) for v in y.groups())) for y in BFLIP_RE.finditer(x.group(0))]
|
||||
# for corr in vcorr:
|
||||
# flips.update(corr.to_flips(vaddr, msys))
|
||||
return cls(
|
||||
targets= targets,
|
||||
flips=flips
|
||||
)
|
||||
|
||||
def encode(self, patterns=None):
|
||||
if patterns is None:
|
||||
patterns = [[0xff], [0]]
|
||||
corrs = [
|
||||
[x for x in self.to_corruptions(pat) if x[1].exp != x[1].got]
|
||||
for pat in patterns
|
||||
]
|
||||
tstr = ' '.join(str(x) for x in self.targets) + ' : '
|
||||
return '\n'.join(
|
||||
tstr +
|
||||
' '.join(' '.join(str(x) for x in c) for c in corr)
|
||||
for corr in corrs
|
||||
)
|
||||
|
||||
|
||||
def decode_lines(lineiter):
|
||||
curatk = None
|
||||
for line in lineiter:
|
||||
atk = Attack.decode_line(line)
|
||||
if curatk is None:
|
||||
curatk = atk
|
||||
else:
|
||||
try:
|
||||
curatk = curatk.merge(atk)
|
||||
except ValueError:
|
||||
yield curatk
|
||||
curatk = atk
|
||||
if curatk is not None:
|
||||
yield curatk
|
||||
|
||||
|
||||
class Parameters(namedtuple("Parameters", ['h_cfg', 'd_cfg', 'h_rows', 'h_rounds', 'd_base'])):
|
||||
""""""
|
||||
@classmethod
|
||||
def parse_params(cls, string):
|
||||
matches = RE_DICT.findall(string)
|
||||
res = {}
|
||||
for m in matches:
|
||||
res[m[0]] = m[1]
|
||||
|
||||
return cls(*res.values())
|
||||
|
||||
|
||||
|
||||
class Fliptable:
|
||||
def __init__(self, attacks):
|
||||
self.attacks=attacks
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
return self.attacks == other.attacks
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def __len__(self):
|
||||
return len(self.attacks)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.attacks)
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(atk.encode() for atk in self)
|
||||
|
||||
def __add__(self, other):
|
||||
return Fliptable(self.attacks + other.attacks)
|
||||
|
||||
def diff(self, other):
|
||||
if not isinstance(other, type(self)):
|
||||
raise ValueError('Fliptable required for diff')
|
||||
uself = []
|
||||
uother = []
|
||||
common = []
|
||||
|
||||
satks = iter(self)
|
||||
oatks = iter(other)
|
||||
sa = next(satks, None)
|
||||
oa = next(oatks, None)
|
||||
while sa is not None or oa is not None:
|
||||
# Degenerate cases
|
||||
if sa is None:
|
||||
uother.append(oa)
|
||||
uother.extend(oatks)
|
||||
break
|
||||
if oa is None:
|
||||
uself.append(sa)
|
||||
uself.extend(satks)
|
||||
break
|
||||
try:
|
||||
adiff = sa.diff(oa)
|
||||
if adiff.self_only.flips:
|
||||
uself.append(adiff.self_only)
|
||||
common.append(adiff.common)
|
||||
if adiff.other_only.flips:
|
||||
uother.append(adiff.other_only)
|
||||
sa = next(satks, None)
|
||||
oa = next(oatks, None)
|
||||
except ValueError:
|
||||
if sa.targets < oa.targets:
|
||||
uself.append(sa)
|
||||
sa = next(satks, None)
|
||||
elif sa.targets > oa.targets:
|
||||
uother.append(oa)
|
||||
oa = next(oatks, None)
|
||||
FT = type(self)
|
||||
return Diff(FT(uself), FT(common), FT(uother))
|
||||
|
||||
def to_physmem(self, msys):
|
||||
return type(self)([x.to_physmem(msys) for x in self])
|
||||
|
||||
@classmethod
|
||||
def load_file(cls, fname):
|
||||
with open(fname, 'r') as f:
|
||||
# params_line = f.readline().remove("#") # read the first line which contains the parameters of the flip table
|
||||
# params = Parameters.parse_params(params_line)
|
||||
# flips = pd.read_csv(f)
|
||||
# attacks = attacks_generator()
|
||||
return cls(list(decode_lines(f)))
|
||||
|
||||
def save_file(self, fname):
|
||||
with open(fname, 'w') as f:
|
||||
f.write(str(self))
|
137
py/hammertime/sim.py
Normal file
137
py/hammertime/sim.py
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
# Copyright (c) 2016 Andrei Tatar
|
||||
# Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import math
|
||||
import itertools
|
||||
import functools
|
||||
from collections import namedtuple
|
||||
|
||||
from hammertime import fliptable
|
||||
from hammertime import dramtrans
|
||||
import sys
|
||||
|
||||
scan_time = 60000 #ns to scan a row (terrible implementation)
|
||||
fill_time = 1500 #ns to fill a row
|
||||
|
||||
|
||||
class PageBitFlip(namedtuple('PageBitFlip', ['byte_offset', 'mask'])):
|
||||
"""Represents a byte with one or more flipped bits at a particular offset within a page"""
|
||||
|
||||
class VictimPage(namedtuple('VictimPage', ['pfn', 'pullups', 'pulldowns'])):
|
||||
"""Represents the results of one rowhammer attack on one particular physical page"""
|
||||
|
||||
|
||||
class ExploitModel:
|
||||
|
||||
def check_page(self, vpage):
|
||||
raise NotImplementedError()
|
||||
|
||||
def check_attack(self, attack):
|
||||
for vpage in attack:
|
||||
if self.check_page(vpage):
|
||||
yield vpage.pfn
|
||||
|
||||
def check_attacks(self, attacks):
|
||||
for atk in attacks:
|
||||
yield tuple(self.check_attack(atk))
|
||||
|
||||
|
||||
def _map_attack(atk, msys, pagesize=0x1000):
|
||||
pfnof = lambda x: x.addr // pagesize
|
||||
byteof = lambda x: x.addr % pagesize + x.bit // 8
|
||||
vict_pages = []
|
||||
for pfn, pflips in itertools.groupby(atk.to_physmem(msys), pfnof):
|
||||
ups = set()
|
||||
downs = set()
|
||||
for byte, iflips in itertools.groupby(pflips, byteof):
|
||||
bflips = list(iflips)
|
||||
pup = functools.reduce(lambda x,y: x | y, (1 << (x.bit % 8) for x in bflips if x.pullup), 0)
|
||||
pdn = functools.reduce(lambda x,y: x | y, (1 << (x.bit % 8) for x in bflips if not x.pullup), 0)
|
||||
if pup:
|
||||
ups.add(PageBitFlip(byte, pup))
|
||||
if pdn:
|
||||
downs.add(PageBitFlip(byte, pdn))
|
||||
if ups or downs:
|
||||
vict_pages.append(VictimPage(pfn, ups, downs))
|
||||
return vict_pages
|
||||
|
||||
|
||||
class BaseEstimator:
|
||||
def __init__(self):
|
||||
self.clear()
|
||||
|
||||
def iter_attacks(self):
|
||||
"""
|
||||
Return an iterator over possible attacks
|
||||
|
||||
An attack consists of a sequence of VictimPages that have bits flipped
|
||||
as part of a single Rowhammer attack.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def run_exploit(self, model):
|
||||
self.results = list(model.check_attacks(self.iter_attacks()))
|
||||
|
||||
def clear(self):
|
||||
self.results = []
|
||||
|
||||
def print_stats(self):
|
||||
if self.results:
|
||||
succ = sum(1 for x in self.results if x)
|
||||
npages = sum(len(x) for x in self.results if x)
|
||||
prop = succ / len(self.results)
|
||||
print('{} total attacks (over {} KiB), of which {} successful ({:5.1f} %)'.format(
|
||||
len(self.results), len(self.results) * 8, succ, 100.0 * prop
|
||||
))
|
||||
print('{} exploitable pages found'.format(npages))
|
||||
if prop != 0:
|
||||
mna = 1 / prop
|
||||
print('Minimum (contiguous) memory required: {} KiB'.format(math.ceil(mna) * 8))
|
||||
print('Mean number of attacks until successful: {:.1f}'.format(mna))
|
||||
print('Mean time to successful attack: {:.1f} seconds (assuming {:.1f}ms/attack)'.format(mna * self.atk_time * 10**-3, self.atk_time))
|
||||
|
||||
|
||||
|
||||
class FliptableEstimator(BaseEstimator):
|
||||
|
||||
def __init__(self, fliptbl, memsys, h_time):
|
||||
self.fliptbl = fliptbl
|
||||
self.msys = memsys
|
||||
super().__init__()
|
||||
self.atk_time = self.compute_atk_time(h_time)
|
||||
|
||||
def iter_attacks(self):
|
||||
for atk in self.fliptbl:
|
||||
yield _map_attack(atk, self.msys)
|
||||
|
||||
def compute_atk_time(self, h_time):
|
||||
n_tgts = len(self.fliptbl.attacks[0].targets)
|
||||
s_time = scan_time * n_tgts/2 * 3
|
||||
f_time = fill_time * n_tgts
|
||||
return (f_time + s_time + h_time) / 10**6 # results in ms
|
||||
|
||||
|
||||
@classmethod
|
||||
def main(cls, profile_file, msys_file, model, h_time):
|
||||
"""Set up an estimator, run an exploit and print out statistics"""
|
||||
ftbl = fliptable.Fliptable.load_file(profile_file)
|
||||
msys = dramtrans.MemorySystem()
|
||||
msys.load_file(msys_file)
|
||||
est = cls(ftbl, msys, h_time)
|
||||
est.run_exploit(model)
|
||||
est.print_stats()
|
26
py/histogram.py
Executable file
26
py/histogram.py
Executable file
@ -0,0 +1,26 @@
|
||||
import sys
|
||||
import csv
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
num_bins = 200
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python histogram.py inputFile")
|
||||
exit(1)
|
||||
|
||||
csvFile = open(sys.argv[1], "r")
|
||||
csvFile.readline() # skip first line
|
||||
|
||||
reader = csv.reader(csvFile, delimiter=',')
|
||||
x = []
|
||||
for row in reader:
|
||||
x.append(int(row[len(row) - 1]))
|
||||
|
||||
print(f'Number of point {len(x)}')
|
||||
fig, ax = plt.subplots()
|
||||
n, bins, patches = ax.hist(x, num_bins, density=False)
|
||||
ax.set_xlabel("Access time [ns]")
|
||||
ax.set_ylabel("proportion of cases")
|
||||
plt.show()
|
Loading…
x
Reference in New Issue
Block a user