diff --git a/cache_utils/src/cache_info.rs b/cache_utils/src/cache_info.rs index 4930d9d..4261efa 100644 --- a/cache_utils/src/cache_info.rs +++ b/cache_utils/src/cache_info.rs @@ -6,7 +6,6 @@ extern crate alloc; use alloc::vec::Vec; use core::arch::x86_64 as arch_x86; - const CACHE_INFO_CPUID_LEAF: u32 = 0x4; pub fn get_cache_info() -> Vec { @@ -33,19 +32,19 @@ pub enum CacheType { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CacheInfo { - cache_type: CacheType, - level: u8, - self_init: bool, - fully_assoc: bool, - core_for_cache: u16, - core_in_package: u16, - cache_line_size: u16, - physical_line_partition: u16, - associativity: u16, - sets: u32, - wbinvd_no_guarantee: bool, - inclusive: bool, - complex_cache_indexing: bool, + pub cache_type: CacheType, + pub level: u8, + pub self_init: bool, + pub fully_assoc: bool, + pub core_for_cache: u16, + pub core_in_package: u16, + pub cache_line_size: u16, + pub physical_line_partition: u16, + pub associativity: u16, + pub sets: u32, + pub wbinvd_no_guarantee: bool, + pub inclusive: bool, + pub complex_cache_indexing: bool, } impl CacheInfo { diff --git a/cache_utils/src/calibration.rs b/cache_utils/src/calibration.rs index c5d99b9..fd9a363 100644 --- a/cache_utils/src/calibration.rs +++ b/cache_utils/src/calibration.rs @@ -1,5 +1,5 @@ use crate::{flush, maccess, rdtsc_fence}; -use polling_serial::serial_println; +use polling_serial::serial_println as println; extern crate alloc; use alloc::vec; @@ -45,8 +45,10 @@ pub unsafe fn only_flush(p: *const u8) -> u64 { const BUCKET_SIZE: usize = 5; const BUCKET_NUMBER: usize = 250; +// TODO same as below, also add the whole page calibration + pub fn calibrate_access(array: &[u8; 4096]) -> u64 { - serial_println!("Calibrating..."); + println!("Calibrating..."); // Allocate a target array // TBD why size, why the position in the array, why the type (usize) @@ -66,7 +68,7 @@ pub fn calibrate_access(array: &[u8; 4096]) -> u64 { // the address in memory we are going to target let pointer = &array[0] as *const u8; - serial_println!("buffer start {:p}", pointer); + println!("buffer start {:p}", pointer); if pointer as usize & 0x3f != 0 { panic!("not aligned nicely"); @@ -94,7 +96,7 @@ pub fn calibrate_access(array: &[u8; 4096]) -> u64 { let mut hit_max_i = 0; let mut miss_min_i = 0; for i in 0..hit_histogram.len() { - serial_println!( + println!( "{:3}: {:10} {:10}", i * BUCKET_SIZE, hit_histogram[i], @@ -108,8 +110,8 @@ pub fn calibrate_access(array: &[u8; 4096]) -> u64 { miss_min_i = i } } - serial_println!("Miss min {}", miss_min_i * BUCKET_SIZE); - serial_println!("Max hit {}", hit_max_i * BUCKET_SIZE); + println!("Miss min {}", miss_min_i * BUCKET_SIZE); + println!("Max hit {}", hit_max_i * BUCKET_SIZE); let mut min = u32::max_value(); let mut min_i = 0; @@ -120,16 +122,24 @@ pub fn calibrate_access(array: &[u8; 4096]) -> u64 { } } - serial_println!("Threshold {}", min_i * BUCKET_SIZE); - serial_println!("Calibration done."); + println!("Threshold {}", min_i * BUCKET_SIZE); + println!("Calibration done."); (min_i * BUCKET_SIZE) as u64 } const CFLUSH_BUCKET_SIZE: usize = 1; const CFLUSH_BUCKET_NUMBER: usize = 250; -pub fn calibrate_flush(array: &[u8; 4096]) -> u64 { - serial_println!("Calibrating cflush..."); +/* TODO Code cleanup : + - change type back to a slice OK + - change return type to return thresholds per cache line ? + - change iteration to be per cache line OK + - take the cache line size as a parameter OK + - parametrize 4k vs 2M ? Or just use the slice length ? OK +*/ + +pub fn calibrate_flush(array: &[u8], cache_line_size: usize) -> u64 { + println!("Calibrating cflush..."); // Allocate a target array // TBD why size, why the position in the array, why the type (usize) @@ -149,13 +159,13 @@ pub fn calibrate_flush(array: &[u8; 4096]) -> u64 { panic!("not aligned nicely"); } // do a large sample of accesses to a cached line - for i in 0..(4 << 10) { + for i in (0..(array.len() as isize)).step_by(cache_line_size) { let mut hit_histogram = vec![0; CFLUSH_BUCKET_NUMBER]; let mut miss_histogram = hit_histogram.clone(); - serial_println!("Calibration for {:p}", unsafe { pointer.offset(i) }); + println!("Calibration for {:p}", unsafe { pointer.offset(i) }); unsafe { load_and_flush(pointer.offset(i)) }; // align down on 64 bytes - for _ in 1..(1 << 10) { + for _ in 1..(1 << 11) { let d = unsafe { load_and_flush(pointer.offset(i)) } as usize; hit_histogram[min(CFLUSH_BUCKET_NUMBER - 1, d / CFLUSH_BUCKET_SIZE) as usize] += 1; } @@ -169,11 +179,14 @@ pub fn calibrate_flush(array: &[u8; 4096]) -> u64 { miss_histogram[min(CFLUSH_BUCKET_NUMBER - 1, d / CFLUSH_BUCKET_SIZE) as usize] += 1; } + // extract min, max, & median of the distribution. + // set the threshold to mid point between miss max & hit min. + let mut hit_max: (usize, u32) = (0, 0); let mut miss_max: (usize, u32) = (0, 0); for i in 0..hit_histogram.len() { - serial_println!( + println!( "{:3}: {:10} {:10}", i * CFLUSH_BUCKET_SIZE, hit_histogram[i], @@ -186,8 +199,8 @@ pub fn calibrate_flush(array: &[u8; 4096]) -> u64 { miss_max = (i, miss_histogram[i]); } } - serial_println!("Miss max {}", miss_max.0 * CFLUSH_BUCKET_SIZE); - serial_println!("Max hit {}", hit_max.0 * CFLUSH_BUCKET_SIZE); + println!("Miss max {}", miss_max.0 * CFLUSH_BUCKET_SIZE); + println!("Max hit {}", hit_max.0 * CFLUSH_BUCKET_SIZE); let mut threshold: (usize, u32) = (0, u32::max_value()); for i in miss_max.0..hit_max.0 { if hit_histogram[i] + miss_histogram[i] < threshold.1 { @@ -195,8 +208,8 @@ pub fn calibrate_flush(array: &[u8; 4096]) -> u64 { } } - serial_println!("Threshold {}", threshold.0 * CFLUSH_BUCKET_SIZE); - serial_println!("Calibration done."); + println!("Threshold {}", threshold.0 * CFLUSH_BUCKET_SIZE); + println!("Calibration done."); } //(threshold.0 * CFLUSH_BUCKET_SIZE) as u64 0 diff --git a/cache_utils/src/main.rs b/cache_utils/src/main.rs new file mode 100644 index 0000000..ad3255b --- /dev/null +++ b/cache_utils/src/main.rs @@ -0,0 +1,2 @@ +// TODO create a nice program that can run on a system and will do the calibration. +// Make multithreaded, with core pinning or single threaded pinned to cores from the shell ? diff --git a/src/main.rs b/src/main.rs index b9da481..2baa3e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -201,6 +201,18 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { serial_println!("{:#?}", caches); println!("Caches: {:?}", caches); + let mut cache_line_size: Option = None; + for cache in caches { + if let Some(cache_line_size) = cache_line_size { + if cache_line_size != cache.cache_line_size { + unimplemented!("Does not support multiple cache line for now"); + } + } else { + cache_line_size = Some(cache.cache_line_size) + } + } + + let cache_line_size = cache_line_size.unwrap_or(64) as usize; println!( "prefetcher status: {}", @@ -214,13 +226,16 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { 4096 ] }); - let threshold_flush_p = cache_utils::calibration::calibrate_flush(unsafe { - arrayref::array_ref![ - core::slice::from_raw_parts(victim4k_start as *mut u8, 4096), - 0, - 4096 - ] - }); + let threshold_flush_p = cache_utils::calibration::calibrate_flush( + unsafe { + arrayref::array_ref![ + core::slice::from_raw_parts(victim4k_start as *mut u8, 4096), + 0, + 4096 + ] + }, + cache_line_size, + ); cache_utils::prefetcher::enable_prefetchers(false); serial_println!("Prefetcher disabled"); let threshold_access = cache_utils::calibration::calibrate_access(unsafe { @@ -230,13 +245,16 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { 4096 ] }); - let threshold_flush = cache_utils::calibration::calibrate_flush(unsafe { - arrayref::array_ref![ - core::slice::from_raw_parts(victim4k_start as *mut u8, 4096), - 0, - 4096 - ] - }); + let threshold_flush = cache_utils::calibration::calibrate_flush( + unsafe { + arrayref::array_ref![ + core::slice::from_raw_parts(victim4k_start as *mut u8, 4096), + 0, + 4096 + ] + }, + cache_line_size, + ); serial_println!("Please compare histograms for sanity"); if distance(threshold_access_p, threshold_access) > 10