From b7b5cbbfc37d2694b78e40f2e9e3e3b0df274fd3 Mon Sep 17 00:00:00 2001 From: Guillume DIDIER Date: Mon, 28 Jun 2021 16:26:02 +0200 Subject: [PATCH] Update the cache channel interface --- Cargo.lock | 1 + aes-t-tables/src/main.rs | 31 +- basic_timing_cache_channel/src/lib.rs | 38 ++- basic_timing_cache_channel/src/naive.rs | 13 +- cache_side_channel/src/lib.rs | 5 +- cache_side_channel/src/table_side_channel.rs | 65 ++--- covert_channels_benchmark/src/main.rs | 99 ++----- flush_flush/src/lib.rs | 3 +- flush_reload/src/lib.rs | 23 ++ flush_reload/src/naive.rs | 15 +- prefetcher_reverse/Cargo.toml | 1 + prefetcher_reverse/run-msr.sh | 7 + prefetcher_reverse/src/lib.rs | 281 +++++++++++++++++++ prefetcher_reverse/src/main.rs | 146 +--------- 14 files changed, 428 insertions(+), 300 deletions(-) create mode 100755 prefetcher_reverse/run-msr.sh create mode 100644 prefetcher_reverse/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index de78945..e5c9597 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,6 +356,7 @@ dependencies = [ "cache_side_channel", "cache_utils", "flush_flush", + "flush_reload", "nix", "rand", ] diff --git a/aes-t-tables/src/main.rs b/aes-t-tables/src/main.rs index d871c76..a689d39 100644 --- a/aes-t-tables/src/main.rs +++ b/aes-t-tables/src/main.rs @@ -30,26 +30,20 @@ fn main() { let te = TE_CITRON_VERT; - let mut side_channel_fr = NaiveFlushAndReload::new( - Threshold { - bucket_index: 220, - miss_faster_than_hit: false, - }, - NaiveFRPrimitives {}, - ); - let mut side_channel_naiveff = NaiveFlushAndFlush::new( - Threshold { - bucket_index: 202, - miss_faster_than_hit: true, - }, - FFPrimitives {}, - ); + let mut side_channel_naivefr = NaiveFlushAndReload::new(Threshold { + bucket_index: 220, + miss_faster_than_hit: false, + }); + let mut side_channel_naiveff = NaiveFlushAndFlush::new(Threshold { + bucket_index: 202, + miss_faster_than_hit: true, + }); for (index, key) in [KEY1, KEY2].iter().enumerate() { println!("AES attack with Naive F+R, key {}", index); unsafe { attack_t_tables_poc( - &mut side_channel_fr, + &mut side_channel_naivefr, AESTTableParams { num_encryptions: 1 << 12, key: *key, @@ -74,11 +68,8 @@ fn main() { }; println!("AES attack with Single F+F, key {}", index); { - let mut side_channel_ff = SingleFlushAndFlush::new( - FlushAndFlush::new_any_single_core(FFPrimitives {}) - .unwrap() - .0, - ); + let mut side_channel_ff = + SingleFlushAndFlush::new(FlushAndFlush::new_any_single_core().unwrap().0); unsafe { attack_t_tables_poc( &mut side_channel_ff, diff --git a/basic_timing_cache_channel/src/lib.rs b/basic_timing_cache_channel/src/lib.rs index 68ab0e9..acc0a98 100644 --- a/basic_timing_cache_channel/src/lib.rs +++ b/basic_timing_cache_channel/src/lib.rs @@ -36,8 +36,9 @@ use std::ptr::slice_from_raw_parts; pub mod naive; -pub trait TimingChannelPrimitives: Debug + Send + Sync { +pub trait TimingChannelPrimitives: Debug + Send + Sync + Default { unsafe fn attack(&self, addr: *const u8) -> u64; + const NEED_RESET: bool; } pub struct TopologyAwareTimingChannelHandle { @@ -79,7 +80,7 @@ unsafe impl Send for TopologyAwareTimingChann unsafe impl Sync for TopologyAwareTimingChannel {} impl TopologyAwareTimingChannel { - pub fn new(main_core: usize, helper_core: usize, t: T) -> Result { + pub fn new(main_core: usize, helper_core: usize) -> Result { if let Some(slicing) = get_cache_slicing(find_core_per_socket()) { if !slicing.can_hash() { return Err(TopologyAwareError::NoSlicing); @@ -92,7 +93,7 @@ impl TopologyAwareTimingChannel { main_core, helper_core, preferred_address: Default::default(), - t, + t: Default::default(), calibration_epoch: 0, }; Ok(ret) @@ -205,11 +206,12 @@ impl TopologyAwareTimingChannel { fn new_with_core_pairs( core_pairs: impl Iterator + Clone, - t: T, ) -> Result<(Self, usize, usize), TopologyAwareError> { let m = MMappedMemory::new(PAGE_LEN, false); let array: &[u8] = m.slice(); + let t = Default::default(); + let mut res = Self::calibration_for_core_pairs(&t, core_pairs, vec![array].into_iter())?; let mut best_error_rate = 1.0; @@ -223,13 +225,13 @@ impl TopologyAwareTimingChannel { best_error_rate = global_error_pred.error_rate(); } } - Self::new(best_av.attacker, best_av.victim, t) + Self::new(best_av.attacker, best_av.victim) .map(|this| (this, best_av.attacker, best_av.victim)) // Set no threshold as calibrated on local array that will get dropped. } - pub fn new_any_single_core(t: T) -> Result<(Self, CpuSet, usize), TopologyAwareError> { + pub fn new_any_single_core() -> Result<(Self, CpuSet, usize), TopologyAwareError> { // Generate core iterator let mut core_pairs: Vec<(usize, usize)> = Vec::new(); @@ -246,7 +248,7 @@ impl TopologyAwareTimingChannel { // Call out to private constructor that takes a core pair list, determines best and makes the choice. // The private constructor will set the correct affinity for main (attacker thread) - Self::new_with_core_pairs(core_pairs.into_iter(), t).map(|(channel, attacker, victim)| { + Self::new_with_core_pairs(core_pairs.into_iter()).map(|(channel, attacker, victim)| { assert_eq!(attacker, victim); (channel, old, attacker) }) @@ -254,7 +256,6 @@ impl TopologyAwareTimingChannel { pub fn new_any_two_core( distinct: bool, - t: T, ) -> Result<(Self, CpuSet, usize, usize), TopologyAwareError> { let old = sched_getaffinity(Pid::from_raw(0)).unwrap(); @@ -272,7 +273,7 @@ impl TopologyAwareTimingChannel { } } - Self::new_with_core_pairs(core_pairs.into_iter(), t).map(|(channel, attacker, victim)| { + Self::new_with_core_pairs(core_pairs.into_iter()).map(|(channel, attacker, victim)| { if distinct { assert_ne!(attacker, victim); } @@ -344,11 +345,15 @@ impl TopologyAwareTimingChannel { unsafe fn test_one_impl( &self, handle: &mut TopologyAwareTimingChannelHandle, + reset: bool, ) -> Result { if handle.calibration_epoch != self.calibration_epoch { return Err(SideChannelError::NeedRecalibration); } let time = unsafe { self.t.attack(handle.addr) }; + if T::NEED_RESET && reset { + unsafe { flush(handle.addr) }; + } if handle.threshold.is_hit(time) { Ok(CacheStatus::Hit) } else { @@ -360,12 +365,13 @@ impl TopologyAwareTimingChannel { &self, addresses: &mut Vec<&mut TopologyAwareTimingChannelHandle>, limit: u32, + reset: bool, ) -> Result, SideChannelError> { let mut result = Vec::new(); let mut tmp = Vec::new(); let mut i = 0; for addr in addresses { - let r = unsafe { self.test_one_impl(addr) }; + let r = unsafe { self.test_one_impl(addr, false) }; tmp.push((addr.to_const_u8_pointer(), r)); i += 1; if i == limit { @@ -381,6 +387,9 @@ impl TopologyAwareTimingChannel { return Err(e); } } + if T::NEED_RESET && reset { + unsafe { flush(addr) }; + } } Ok(result) } @@ -456,11 +465,12 @@ impl MultipleAddrCacheSideChannel for TopologyAwareT unsafe fn test<'a>( &mut self, addresses: &mut Vec<&'a mut Self::Handle>, + reset: bool, ) -> Result, SideChannelError> where Self::Handle: 'a, { - unsafe { self.test_impl(addresses, Self::MAX_ADDR) } + unsafe { self.test_impl(addresses, Self::MAX_ADDR, reset) } } unsafe fn prepare<'a>( @@ -558,12 +568,11 @@ impl CovertChannel for TopologyAwareTimingChannel } unsafe fn receive(&self, handle: &mut Self::CovertChannelHandle) -> Vec { - let r = unsafe { self.test_one_impl(&mut handle.0) }; + let r = unsafe { self.test_one_impl(&mut handle.0, false) }; // transmit does the reload / flush as needed. match r { Err(e) => panic!("{:?}", e), Ok(status) => { let received = status == CacheStatus::Hit; - //println!("Received {} on page {:p}", received, page); return vec![received]; } } @@ -672,8 +681,9 @@ impl SingleAddrCacheSideChannel for SingleChann unsafe fn test_single( &mut self, handle: &mut Self::Handle, + reset: bool, ) -> Result { - unsafe { self.inner.test_single(handle) } + unsafe { self.inner.test_single(handle, reset) } } unsafe fn prepare_single(&mut self, handle: &mut Self::Handle) -> Result<(), SideChannelError> { diff --git a/basic_timing_cache_channel/src/naive.rs b/basic_timing_cache_channel/src/naive.rs index afaa657..f2c8db5 100644 --- a/basic_timing_cache_channel/src/naive.rs +++ b/basic_timing_cache_channel/src/naive.rs @@ -36,13 +36,13 @@ unsafe impl Send for NaiveTimingChannel {} unsafe impl Sync for NaiveTimingChannel {} impl NaiveTimingChannel { - pub fn new(threshold: Threshold, t: T) -> Self { + pub fn new(threshold: Threshold) -> Self { Self { threshold, current: Default::default(), main_core: sched_getaffinity(Pid::from_raw(0)).unwrap(), helper_core: sched_getaffinity(Pid::from_raw(0)).unwrap(), - channel_primitive: t, + channel_primitive: Default::default(), } } @@ -68,9 +68,13 @@ impl NaiveTimingChannel { unsafe fn test_impl( &self, handle: &mut NaiveTimingChannelHandle, + reset: bool, ) -> Result { // This should be handled in prepare / unprepare let t = unsafe { self.channel_primitive.attack(handle.addr) }; + if T::NEED_RESET && reset { + unsafe { flush(handle.addr) }; + } if self.threshold.is_hit(t) { Ok(CacheStatus::Hit) } else { @@ -121,7 +125,7 @@ impl CovertChannel for NaiveTimingChan } unsafe fn receive(&self, handle: &mut Self::CovertChannelHandle) -> Vec { - let r = unsafe { self.test_impl(handle) }; + let r = unsafe { self.test_impl(handle, false) }; match r { Err(e) => panic!(), Ok(status) => match status { @@ -141,8 +145,9 @@ impl SingleAddrCacheSideChannel for NaiveTimingChann unsafe fn test_single( &mut self, handle: &mut Self::Handle, + reset: bool, ) -> Result { - unsafe { self.test_impl(handle) } + unsafe { self.test_impl(handle, reset) } } unsafe fn prepare_single(&mut self, handle: &mut Self::Handle) -> Result<(), SideChannelError> { diff --git a/cache_side_channel/src/lib.rs b/cache_side_channel/src/lib.rs index 2841bf8..fa98e2c 100644 --- a/cache_side_channel/src/lib.rs +++ b/cache_side_channel/src/lib.rs @@ -56,6 +56,7 @@ pub trait SingleAddrCacheSideChannel: CoreSpec + Debug { unsafe fn test_single( &mut self, handle: &mut Self::Handle, + reset: bool, ) -> Result; /// # Safety /// @@ -80,6 +81,7 @@ pub trait MultipleAddrCacheSideChannel: CoreSpec + Debug { unsafe fn test<'a, 'b, 'c>( &'a mut self, addresses: &'b mut Vec<&'c mut Self::Handle>, + reset: bool, ) -> Result, SideChannelError> where Self::Handle: 'c; @@ -110,9 +112,10 @@ impl SingleAddrCacheSideChannel for T { unsafe fn test_single( &mut self, handle: &mut Self::Handle, + reset: bool, ) -> Result { let mut handles = vec![handle]; - unsafe { self.test(&mut handles) }.map(|v| v[0].1) + unsafe { self.test(&mut handles, reset) }.map(|v| v[0].1) } unsafe fn prepare_single(&mut self, handle: &mut Self::Handle) -> Result<(), SideChannelError> { diff --git a/cache_side_channel/src/table_side_channel.rs b/cache_side_channel/src/table_side_channel.rs index 38e805a..abbfdaf 100644 --- a/cache_side_channel/src/table_side_channel.rs +++ b/cache_side_channel/src/table_side_channel.rs @@ -66,18 +66,18 @@ impl TableCacheSideChannel for T { for addr in addresses { let mut hit = 0; let mut miss = 0; + match unsafe { self.prepare_single(addr) } { + Ok(_) => {} + Err(e) => match e { + SideChannelError::NeedRecalibration => unimplemented!(), + SideChannelError::FatalError(e) => return Err(e), + SideChannelError::AddressNotReady(_addr) => panic!(), + SideChannelError::AddressNotCalibrated(_addr) => unimplemented!(), + }, + } for iteration in 0..100 { - match unsafe { self.prepare_single(addr) } { - Ok(_) => {} - Err(e) => match e { - SideChannelError::NeedRecalibration => unimplemented!(), - SideChannelError::FatalError(e) => return Err(e), - SideChannelError::AddressNotReady(_addr) => panic!(), - SideChannelError::AddressNotCalibrated(_addr) => unimplemented!(), - }, - } self.victim_single(victim); - let r = unsafe { self.test_single(addr) }; + let r = unsafe { self.test_single(addr, true) }; match r { Ok(status) => {} Err(e) => match e { @@ -90,17 +90,8 @@ impl TableCacheSideChannel for T { } } for _iteration in 0..num_iteration { - match unsafe { self.prepare_single(addr) } { - Ok(_) => {} - Err(e) => match e { - SideChannelError::NeedRecalibration => unimplemented!(), - SideChannelError::FatalError(e) => return Err(e), - SideChannelError::AddressNotReady(_addr) => panic!(), - SideChannelError::AddressNotCalibrated(_addr) => unimplemented!(), - }, - } self.victim_single(victim); - let r = unsafe { self.test_single(addr) }; + let r = unsafe { self.test_single(addr, true) }; match r { Ok(status) => match status { CacheStatus::Hit => { @@ -169,26 +160,26 @@ impl TableCacheSideChannel for T { for i in 0..100 { // TODO Warmup } - for i in 0..num_iteration { - match unsafe { MultipleAddrCacheSideChannel::prepare(self, &mut batch) } { - Ok(_) => {} - Err(e) => match e { - SideChannelError::NeedRecalibration => unimplemented!(), - SideChannelError::FatalError(e) => return Err(e), - SideChannelError::AddressNotReady(_addr) => panic!(), - SideChannelError::AddressNotCalibrated(addr) => { - eprintln!( - "Addr: {:p}\n\ + match unsafe { MultipleAddrCacheSideChannel::prepare(self, &mut batch) } { + Ok(_) => {} + Err(e) => match e { + SideChannelError::NeedRecalibration => unimplemented!(), + SideChannelError::FatalError(e) => return Err(e), + SideChannelError::AddressNotReady(_addr) => panic!(), + SideChannelError::AddressNotCalibrated(addr) => { + eprintln!( + "Addr: {:p}\n\ {:#?}", - addr, self - ); - unimplemented!() - } - }, - } + addr, self + ); + unimplemented!() + } + }, + } + for i in 0..num_iteration { MultipleAddrCacheSideChannel::victim(self, victim); - let r = unsafe { MultipleAddrCacheSideChannel::test(self, &mut batch) }; // Fixme error handling + let r = unsafe { MultipleAddrCacheSideChannel::test(self, &mut batch, true) }; // Fixme error handling match r { Err(e) => match e { SideChannelError::NeedRecalibration => { diff --git a/covert_channels_benchmark/src/main.rs b/covert_channels_benchmark/src/main.rs index 723b3a6..a570223 100644 --- a/covert_channels_benchmark/src/main.rs +++ b/covert_channels_benchmark/src/main.rs @@ -9,7 +9,8 @@ use cache_utils::calibration::Threshold; use covert_channels_evaluation::{benchmark_channel, CovertChannel, CovertChannelBenchmarkResult}; use flush_flush::naive::NaiveFlushAndFlush; use flush_flush::{FFPrimitives, FlushAndFlush, SingleFlushAndFlush}; -use flush_reload::naive::{NaiveFRPrimitives, NaiveFlushAndReload}; +use flush_reload::naive::NaiveFlushAndReload; +use flush_reload::FRPrimitives; use nix::sched::{sched_getaffinity, CpuSet}; use nix::unistd::Pid; @@ -138,13 +139,10 @@ fn main() { let naive_ff = run_benchmark( "Naive F+F", |i, j| { - let mut r = NaiveFlushAndFlush::new( - Threshold { - bucket_index: 202, - miss_faster_than_hit: true, - }, - FFPrimitives {}, - ); + let mut r = NaiveFlushAndFlush::new(Threshold { + bucket_index: 202, + miss_faster_than_hit: true, + }); r.set_cores(i, j); (r, i, j) }, @@ -153,16 +151,13 @@ fn main() { old, ); - let fr = run_benchmark( - "F+R", + let naive_fr = run_benchmark( + "Naive F+R", |i, j| { - let mut r = NaiveFlushAndReload::new( - Threshold { - bucket_index: 250, - miss_faster_than_hit: false, - }, - NaiveFRPrimitives {}, - ); + let mut r = NaiveFlushAndReload::new(Threshold { + bucket_index: 250, + miss_faster_than_hit: false, + }); r.set_cores(i, j); (r, i, j) }, @@ -174,7 +169,25 @@ fn main() { let ff = run_benchmark( "Better F+F", |i, j| { - let (mut r, i, j) = match FlushAndFlush::new_any_two_core(true, FFPrimitives {}) { + let (mut r, i, j) = match FlushAndFlush::new_any_two_core(true) { + Ok((channel, _old, main_core, helper_core)) => { + (channel, main_core, helper_core) + } + Err(e) => { + panic!("{:?}", e); + } + }; + (r, i, j) + }, + 1, + num_pages, + old, + ); + + let fr = run_benchmark( + "Better F+R", + |i, j| { + let (mut r, i, j) = match FlushAndFlush::new_any_two_core(true) { Ok((channel, _old, main_core, helper_core)) => { (channel, main_core, helper_core) } @@ -190,53 +203,3 @@ fn main() { ); } } -/* -fn main() { - for num_pages in 1..=32 { - /*println!("Benchmarking F+F"); - for _ in 0..16 { - // TODO Use the best possible ASV, not best possible AV - let (channel, old, receiver, sender) = match SingleFlushAndFlush::new_any_two_core(true) { - Err(e) => { - panic!("{:?}", e); - } - Ok(r) => r, - }; - - let r = benchmark_channel(channel, NUM_PAGES, NUM_BYTES); - println!("{:?}", r); - println!("C: {}, T: {}", r.capacity(), r.true_capacity()); - - }*/ - - let naive_ff = run_benchmark( - "Naive F+F", - || NaiveFlushAndFlush::from_threshold(202), - NUM_ITER << 4, - num_pages, - ); - - let better_ff = run_benchmark( - "Better F+F", - || { - match FlushAndFlush::new_any_two_core(true) { - Err(e) => { - panic!("{:?}", e); - } - Ok(r) => r, - } - .0 - }, - NUM_ITER, - num_pages, - ); - - let fr = run_benchmark( - "F+R", - || NaiveFlushAndReload::from_threshold(230), - NUM_ITER, - num_pages, - ); - } -} -*/ diff --git a/flush_flush/src/lib.rs b/flush_flush/src/lib.rs index 98f749f..e563f00 100644 --- a/flush_flush/src/lib.rs +++ b/flush_flush/src/lib.rs @@ -10,13 +10,14 @@ use basic_timing_cache_channel::{ use cache_side_channel::MultipleAddrCacheSideChannel; use cache_utils::calibration::only_flush; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct FFPrimitives {} impl TimingChannelPrimitives for FFPrimitives { unsafe fn attack(&self, addr: *const u8) -> u64 { unsafe { only_flush(addr) } } + const NEED_RESET: bool = false; } pub type FlushAndFlush = TopologyAwareTimingChannel; diff --git a/flush_reload/src/lib.rs b/flush_reload/src/lib.rs index 729d8b6..987b880 100644 --- a/flush_reload/src/lib.rs +++ b/flush_reload/src/lib.rs @@ -3,6 +3,29 @@ pub mod naive; +use basic_timing_cache_channel::{ + SingleChannel, TimingChannelPrimitives, TopologyAwareTimingChannel, +}; + +use cache_side_channel::MultipleAddrCacheSideChannel; +use cache_utils::calibration::only_reload; + +#[derive(Debug, Default)] +pub struct FRPrimitives {} + +impl TimingChannelPrimitives for FRPrimitives { + unsafe fn attack(&self, addr: *const u8) -> u64 { + unsafe { only_reload(addr) } + } + const NEED_RESET: bool = true; +} + +pub type FlushAndReload = TopologyAwareTimingChannel; + +pub type FRHandle = ::Handle; + +pub type SingleFlushAndReload = SingleChannel; + #[cfg(test)] mod tests { #[test] diff --git a/flush_reload/src/naive.rs b/flush_reload/src/naive.rs index 09f3a39..8db1ce5 100644 --- a/flush_reload/src/naive.rs +++ b/flush_reload/src/naive.rs @@ -1,15 +1,4 @@ +use crate::FRPrimitives; use basic_timing_cache_channel::naive::NaiveTimingChannel; -use basic_timing_cache_channel::TimingChannelPrimitives; -use cache_utils::calibration::only_reload; - -#[derive(Debug)] -pub struct NaiveFRPrimitives {} - -impl TimingChannelPrimitives for NaiveFRPrimitives { - unsafe fn attack(&self, addr: *const u8) -> u64 { - unsafe { only_reload(addr) } - } -} - -pub type NaiveFlushAndReload = NaiveTimingChannel; +pub type NaiveFlushAndReload = NaiveTimingChannel; diff --git a/prefetcher_reverse/Cargo.toml b/prefetcher_reverse/Cargo.toml index 68acd3f..f1dac60 100644 --- a/prefetcher_reverse/Cargo.toml +++ b/prefetcher_reverse/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" cache_utils = { path = "../cache_utils" } cache_side_channel = { path = "../cache_side_channel" } flush_flush = { path = "../flush_flush" } +flush_reload = { path = "../flush_reload" } basic_timing_cache_channel = { path = "../basic_timing_cache_channel" } nix = "0.20.0" rand = "0.8.3" diff --git a/prefetcher_reverse/run-msr.sh b/prefetcher_reverse/run-msr.sh new file mode 100755 index 0000000..fac60ab --- /dev/null +++ b/prefetcher_reverse/run-msr.sh @@ -0,0 +1,7 @@ +#!/bin/bash +PREFETCH_MSR=$1 +sudo wrmsr -a 0x1a4 $PREFETCH_MSR +sudo rdmsr -a 0x1a4 +cargo run --release > with-${PREFETCH_MSR}-prefetcher.log + + diff --git a/prefetcher_reverse/src/lib.rs b/prefetcher_reverse/src/lib.rs new file mode 100644 index 0000000..50f17c2 --- /dev/null +++ b/prefetcher_reverse/src/lib.rs @@ -0,0 +1,281 @@ +use crate::Probe::{Flush, Load}; +use basic_timing_cache_channel::{TopologyAwareError, TopologyAwareTimingChannel}; +use cache_side_channel::CacheStatus::Hit; +use cache_side_channel::{ + set_affinity, CacheStatus, CoreSpec, MultipleAddrCacheSideChannel, SingleAddrCacheSideChannel, +}; +use cache_utils::calibration::PAGE_LEN; +use cache_utils::mmap::MMappedMemory; +use flush_flush::{FFHandle, FFPrimitives, FlushAndFlush}; +use flush_reload::{FRHandle, FRPrimitives, FlushAndReload}; +use nix::sys::stat::stat; +use rand::seq::SliceRandom; +use std::iter::{Cycle, Peekable}; +use std::ops::Range; + +// NB these may need to be changed / dynamically measured. +pub const CACHE_LINE_LEN: usize = 64; +pub const PAGE_CACHELINE_LEN: usize = PAGE_LEN / CACHE_LINE_LEN; + +pub struct Prober { + pages: Vec>, + ff_handles: Vec>, + fr_handles: Vec>, + page_indexes: Peekable>>, + ff_channel: FlushAndFlush, + fr_channel: FlushAndReload, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Probe { + Load(usize), + Flush(usize), + FullFlush, +} + +pub struct ProbePattern { + pub pattern: Vec, + pub probe: Probe, +} + +enum ProberError { + NoMem(nix::Error), + TopologyError(TopologyAwareError), + Nix(nix::Error), +} + +/** +Result of running a probe pattern num_iteration times, +*/ + +pub enum ProbeResult { + Load(u32), + Flush(u32), + FullFlush(Vec), +} + +pub struct ProbePatternResult { + pub num_iteration: u32, + pub pattern_result: Vec, + pub probe_result: ProbeResult, +} + +struct DPRItem { + pattern_result: Vec, + probe_result: u32, +} + +struct DualProbeResult { + probe_offset: usize, + load: DPRItem, + flush: DPRItem, +} + +pub struct FullPageDualProbeResults { + num_iteration: u32, + results: Vec, +} + +struct SingleProbeResult { + probe_offset: usize, + pattern_result: Vec, + probe_result: u32, +} + +pub struct FullPageSingleProbeResult { + probe_type: Probe, + num_iteration: u32, + results: Vec, +} + +// Helper function +/** +This function is a helper that determine what is the maximum stride for a pattern of len accesses +starting at a given offset, both forward and backward. + +Special case for length 0. + */ +fn max_stride(offset: usize, len: usize) -> (isize, isize) { + if len == 0 { + (0, 0) + } else { + let min = -((offset / (len * CACHE_LINE_LEN)) as isize); + let max = ((PAGE_LEN - offset) / (len * CACHE_LINE_LEN)) as isize; + (min, max) + } +} + +impl Prober { + fn new(num_pages: usize) -> Result { + let mut vec = Vec::new(); + let mut handles = Vec::new(); + let (mut ff_channel, cpuset, core) = match FlushAndFlush::new_any_single_core() { + Ok(res) => res, + Err(err) => { + return Err(ProberError::TopologyError(err)); + } + }; + let old_affinity = match set_affinity(&ff_channel.main_core()) { + Ok(old) => old, + Err(nixerr) => return Err(ProberError::Nix(nixerr)), + }; + let mut fr_channel = match FlushAndReload::new(core, core) { + Ok(res) => res, + Err(err) => { + return Err(ProberError::TopologyError(err)); + } + }; + + for i in 0..num_pages { + let mut p = match MMappedMemory::::try_new(PAGE_LEN, false) { + Ok(p) => p, + Err(e) => { + return Err(ProberError::NoMem(e)); + } + }; + for j in 0..PAGE_LEN { + p[j] = (i * PAGE_CACHELINE_LEN + j) as u8; + } + let page_addresses = + ((0..PAGE_LEN).step_by(CACHE_LINE_LEN)).map(|offset| &p[offset] as *const u8); + let ff_page_handles = unsafe { ff_channel.calibrate(page_addresses.clone()) }.unwrap(); + let fr_page_handles = unsafe { fr_channel.calibrate(page_addresses) }.unwrap(); + + vec.push(p); + handles.push((ff_page_handles, fr_page_handles)); + } + + let mut page_indexes = (0..(handles.len())).cycle().peekable(); + + handles.shuffle(&mut rand::thread_rng()); + + let mut ff_handles = Vec::new(); + let mut fr_handles = Vec::new(); + for (ff_handle, fr_handle) in handles { + ff_handles.push(ff_handle); + fr_handles.push(fr_handle); + } + + Ok(Prober { + pages: vec, + ff_handles, + fr_handles, + page_indexes, + ff_channel, + fr_channel, + }) + } + + /* + fn probe(&mut self, probe_type: Probe, offset: usize) -> CacheStatus { + let page_index = self.page_indexes.peek().unwrap(); + match probe_type { + Probe::Load => { + let h = &mut self.handles[*page_index][offset].fr; + unsafe { self.fr_channel.test_single(h, false) }.unwrap() + } + Probe::Flush => { + let h = &mut self.handles[*page_index][offset].ff; + unsafe { self.ff_channel.test_single(h, false) }.unwrap() + } + } + } + */ + + fn probe_pattern_once( + &mut self, + pattern: &ProbePattern, + result: Option<&mut ProbePatternResult>, + ) { + enum ProbeOutput { + Single(CacheStatus), + Full(Vec<(*const u8, CacheStatus)>), + } + + self.page_indexes.next(); + let page_index = *self.page_indexes.peek().unwrap(); + + let mut ff_handles = self.ff_handles[page_index].iter_mut().collect(); + + unsafe { self.ff_channel.prepare(&mut ff_handles) }; + + let mut pattern_res = vec![CacheStatus::Miss; pattern.pattern.len()]; + for (i, offset) in pattern.pattern.iter().enumerate() { + let h = &mut self.fr_handles[page_index][*offset]; + pattern_res[i] = unsafe { self.fr_channel.test_single(h, false) }.unwrap() + } + + let mut probe_out = match pattern.probe { + Load(offset) => { + let h = &mut self.fr_handles[page_index][offset]; + ProbeOutput::Single(unsafe { self.fr_channel.test_single(h, false) }.unwrap()) + } + Flush(offset) => { + let h = &mut self.ff_handles[page_index][offset]; + ProbeOutput::Single(unsafe { self.ff_channel.test_single(h, false) }.unwrap()) + } + Probe::FullFlush => { + ProbeOutput::Full(unsafe { self.ff_channel.test(&mut ff_handles, true).unwrap() }) + } + }; + + if let Some(result_ref) = result { + result_ref.num_iteration += 1; + + match result_ref.probe_result { + ProbeResult::Load(ref mut r) | ProbeResult::Flush(ref mut r) => { + if let ProbeOutput::Single(status) = probe_out { + if status == Hit { + *r += 1; + } + } else { + panic!() + } + } + ProbeResult::FullFlush(ref mut v) => { + if let ProbeOutput::Full(vstatus) = probe_out { + for (i, status) in vstatus.iter().enumerate() { + if status.1 == Hit { + v[i] += 1; + } + } + } else { + panic!() + } + } + } + + for (i, res) in pattern_res.into_iter().enumerate() { + if res == Hit { + result_ref.pattern_result[i] += 1 + } + } + } + } + + pub fn probe_pattern( + &mut self, + pattern: &ProbePattern, + num_iteration: u32, + warmup: u32, + ) -> ProbePatternResult { + let mut result = ProbePatternResult { + num_iteration: 0, + pattern_result: vec![0; pattern.pattern.len()], + probe_result: match pattern.probe { + Load(_) => ProbeResult::Load(0), + Flush(_) => ProbeResult::Flush(0), + Probe::FullFlush => ProbeResult::FullFlush(vec![0; PAGE_CACHELINE_LEN]), + }, + }; + for _ in 0..warmup { + self.probe_pattern_once(pattern, None); + } + + for _ in 0..num_iteration { + self.probe_pattern_once(pattern, Some(&mut result)); + } + + result + } +} diff --git a/prefetcher_reverse/src/main.rs b/prefetcher_reverse/src/main.rs index ca1c41c..4c32308 100644 --- a/prefetcher_reverse/src/main.rs +++ b/prefetcher_reverse/src/main.rs @@ -11,27 +11,14 @@ use cache_utils::mmap; use cache_utils::mmap::MMappedMemory; use flush_flush::{FFHandle, FFPrimitives, FlushAndFlush}; use nix::Error; +use prefetcher_reverse::{Prober, CACHE_LINE_LEN, PAGE_CACHELINE_LEN}; use rand::seq::SliceRandom; use std::iter::Cycle; use std::ops::Range; -pub const CACHE_LINE_LEN: usize = 64; - -pub const PAGE_CACHELINE_LEN: usize = PAGE_LEN / CACHE_LINE_LEN; - pub const NUM_ITERATION: usize = 1 << 10; pub const NUM_PAGES: usize = 256; -fn max_stride(offset: usize, len: usize) -> (isize, isize) { - if len == 0 { - (1, 1) - } else { - let min = -((offset / (len * CACHE_LINE_LEN)) as isize); - let max = ((PAGE_LEN - offset) / (len * CACHE_LINE_LEN)) as isize; - (min, max) - } -} - // TODO negative stride fn generate_pattern(offset: usize, len: usize, stride: isize) -> Option> { let end = (offset as isize + stride * len as isize) * CACHE_LINE_LEN as isize; @@ -57,7 +44,7 @@ fn execute_pattern( unsafe { maccess(pointer) }; } - let mut measures = unsafe { channel.test(page_handles) }; + let mut measures = unsafe { channel.test(page_handles, true) }; let mut res = vec![false; PAGE_CACHELINE_LEN]; @@ -78,140 +65,15 @@ fn execute_pattern_probe1( unsafe { maccess(pointer) }; } - let mut measure = unsafe { channel.test_single(&mut page_handles[probe_offset]) }; + let mut measure = unsafe { channel.test_single(&mut page_handles[probe_offset], true) }; measure.unwrap() == Hit } -enum ProberError { - NoMem(Error), - TopologyError(TopologyAwareError), - Nix(nix::Error), -} - -struct Prober { - pages: Vec>, - handles: Vec>, - page_indexes: Cycle>, - channel: FlushAndFlush, -} - -struct ProbeResult { - probe_all_initial: [u32; PAGE_CACHELINE_LEN], - probe_1: [u32; PAGE_CACHELINE_LEN], - probe_all_final: [u32; PAGE_CACHELINE_LEN], -} - -impl Prober { - fn new(num_pages: usize) -> Result { - let mut vec = Vec::new(); - let mut handles = Vec::new(); - let (mut channel, cpuset, core) = match FlushAndFlush::new_any_single_core(FFPrimitives {}) - { - Ok(res) => res, - Err(err) => { - return Err(ProberError::TopologyError(err)); - } - }; - let old_affinity = match set_affinity(&channel.main_core()) { - Ok(old) => old, - Err(nixerr) => return Err(ProberError::Nix(nixerr)), - }; // FIXME error handling - for i in 0..NUM_PAGES { - let mut p = match MMappedMemory::::try_new(PAGE_LEN, false) { - Ok(p) => p, - Err(e) => { - return Err(ProberError::NoMem(e)); - } - }; - for j in 0..PAGE_LEN { - p[j] = (i * PAGE_CACHELINE_LEN + j) as u8; - } - let page_addresses = - ((0..PAGE_LEN).step_by(CACHE_LINE_LEN)).map(|offset| &p[offset] as *const u8); - let page_handles = unsafe { channel.calibrate(page_addresses) }.unwrap(); - vec.push(p); - handles.push(page_handles); - } - - let mut page_indexes = (0..(handles.len())).cycle(); - - handles.shuffle(&mut rand::thread_rng()); - let mut handles_mutref = Vec::new(); - for page in handles.iter_mut() { - handles_mutref.push( - page.iter_mut() - .collect::::Handle>>(), - ); - } - - Ok(Prober { - pages: vec, - handles, - page_indexes, - channel, - }) - } - - fn probe_pattern(&mut self, pattern: Vec) -> ProbeResult { - let mut handles_mutref = Vec::new(); - for page in self.handles.iter_mut() { - handles_mutref.push( - page.iter_mut() - .collect::::Handle>>(), - ); - } - let mut probe_all_result_first = [0; PAGE_CACHELINE_LEN]; - for _ in 0..NUM_ITERATION { - let page_index = self.page_indexes.next().unwrap(); - unsafe { self.channel.prepare(&mut handles_mutref[page_index]) }; - let res = execute_pattern(&mut self.channel, &mut handles_mutref[page_index], &pattern); - for j in 0..PAGE_CACHELINE_LEN { - if res[j] { - probe_all_result_first[j] += 1; - } - } - } - let mut probe1_result = [0; PAGE_CACHELINE_LEN]; - for i in 0..PAGE_CACHELINE_LEN { - for _ in 0..NUM_ITERATION { - let page_index = self.page_indexes.next().unwrap(); - unsafe { self.channel.prepare(&mut handles_mutref[page_index]) }; - let res = execute_pattern_probe1( - &mut self.channel, - &mut handles_mutref[page_index], - &pattern, - i, - ); - if res { - probe1_result[i] += 1; - } - } - } - let mut probe_all_result = [0; PAGE_CACHELINE_LEN]; - for _ in 0..NUM_ITERATION { - let page_index = self.page_indexes.next().unwrap(); - unsafe { self.channel.prepare(&mut handles_mutref[page_index]) }; - let res = execute_pattern(&mut self.channel, &mut handles_mutref[page_index], &pattern); - for j in 0..PAGE_CACHELINE_LEN { - if res[j] { - probe_all_result[j] += 1; - } - } - } - - ProbeResult { - probe_all_initial: probe_all_result_first, - probe_1: probe1_result, - probe_all_final: probe_all_result, - } - } -} - fn main() { let mut vec = Vec::new(); let mut handles = Vec::new(); - let (mut channel, cpuset, core) = FlushAndFlush::new_any_single_core(FFPrimitives {}).unwrap(); + let (mut channel, cpuset, core) = FlushAndFlush::new_any_single_core().unwrap(); let old_affinity = set_affinity(&channel.main_core()); for i in 0..NUM_PAGES { let mut p = MMappedMemory::::new(PAGE_LEN, false);