openentropy_core/sources/timing/
dram_row_buffer.rs1use rand::Rng;
4
5use crate::source::{EntropySource, Platform, SourceCategory, SourceInfo};
6use crate::sources::helpers::{extract_timing_entropy, mach_time};
7
8pub struct DRAMRowBufferSource;
14
15static DRAM_ROW_BUFFER_INFO: SourceInfo = SourceInfo {
16 name: "dram_row_buffer",
17 description: "DRAM row buffer hit/miss timing from random memory accesses",
18 physics: "Measures DRAM row buffer hit/miss timing by accessing different memory rows. \
19 DRAM is organized into rows of capacitor cells. Accessing an open row (hit) \
20 is fast; accessing a different row requires precharge + activate (miss), \
21 which is slower. The exact timing depends on: physical address mapping, \
22 row buffer state from ALL system activity, memory controller scheduling, \
23 and DRAM refresh interference.",
24 category: SourceCategory::Timing,
25 platform: Platform::Any,
26 requirements: &[],
27 entropy_rate_estimate: 3.0,
28 composite: false,
29 is_fast: false,
30};
31
32impl EntropySource for DRAMRowBufferSource {
33 fn info(&self) -> &SourceInfo {
34 &DRAM_ROW_BUFFER_INFO
35 }
36
37 fn is_available(&self) -> bool {
38 true
39 }
40
41 fn collect(&self, n_samples: usize) -> Vec<u8> {
42 const BUF_SIZE: usize = 32 * 1024 * 1024; let num_accesses = n_samples * 4 + 64;
47
48 let mut buffer: Vec<u8> = vec![0u8; BUF_SIZE];
50 for i in (0..BUF_SIZE).step_by(4096) {
51 buffer[i] = i as u8;
52 }
53
54 let mut rng = rand::rng();
55 let mut timings = Vec::with_capacity(num_accesses);
56
57 for _ in 0..num_accesses {
58 let idx1 = rng.random_range(0..BUF_SIZE);
61 let idx2 = rng.random_range(0..BUF_SIZE);
62
63 let t0 = mach_time();
64 let _v1 = unsafe { std::ptr::read_volatile(&buffer[idx1]) };
67 let _v2 = unsafe { std::ptr::read_volatile(&buffer[idx2]) };
68 let t1 = mach_time();
69
70 timings.push(t1.wrapping_sub(t0));
71 }
72
73 std::hint::black_box(&buffer);
75
76 extract_timing_entropy(&timings, n_samples)
77 }
78}
79
80#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 #[ignore] fn dram_row_buffer_collects_bytes() {
91 let src = DRAMRowBufferSource;
92 assert!(src.is_available());
93 let data = src.collect(128);
94 assert!(!data.is_empty());
95 assert!(data.len() <= 128);
96 if data.len() > 1 {
98 let first = data[0];
99 assert!(data.iter().any(|&b| b != first), "all bytes were identical");
100 }
101 }
102
103 #[test]
104 fn source_info_category() {
105 assert_eq!(DRAMRowBufferSource.info().category, SourceCategory::Timing);
106 }
107
108 #[test]
109 fn source_info_name() {
110 assert_eq!(DRAMRowBufferSource.name(), "dram_row_buffer");
111 }
112}