1use crate::{AccessPolicy, HostView, PersistTrigger, policy::PersistPolicy, types::StagingBuffer};
2use bitmaps::{Bits, BitsImpl};
3
4pub struct HostViewStaged<'a, const TS: usize, const BS: usize, const BC: usize, AP, PP, PT, PK, SB>
8where
9 AP: AccessPolicy,
10 PP: PersistPolicy<PK>,
11 PT: PersistTrigger<PK>,
12 BitsImpl<BC>: Bits,
13 SB: StagingBuffer,
14{
15 base: HostView<'a, TS, BS, BC, AP, PP, PT, PK>,
16 sb: &'a mut SB,
17}
18
19impl<'a, const TS: usize, const BS: usize, const BC: usize, AP, PP, PT, PK, SB>
20 HostViewStaged<'a, TS, BS, BC, AP, PP, PT, PK, SB>
21where
22 AP: AccessPolicy,
23 PP: PersistPolicy<PK>,
24 PT: PersistTrigger<PK>,
25 BitsImpl<BC>: Bits,
26 SB: StagingBuffer,
27{
28 pub(crate) fn new(base: HostView<'a, TS, BS, BC, AP, PP, PT, PK>, sb: &'a mut SB) -> Self {
29 Self { base, sb }
30 }
31
32 pub fn read_range(&self, addr: u16, out: &mut [u8]) -> Result<(), crate::ShadowError> {
34 self.base.read_range(addr, out)
35 }
36
37 pub fn write_range(&mut self, addr: u16, data: &[u8]) -> Result<(), crate::ShadowError> {
39 self.base.write_range(addr, data)
40 }
41
42 pub fn read_range_overlay(&self, addr: u16, out: &mut [u8]) -> Result<(), crate::ShadowError> {
44 if !self.base.access_policy.can_read(addr, out.len()) {
45 return Err(crate::ShadowError::Denied);
46 }
47
48 self.base.read_range(addr, out)?;
49 self.sb.apply_overlay(addr, out)?;
50 Ok(())
51 }
52
53 pub fn write_range_staged(&mut self, addr: u16, data: &[u8]) -> Result<(), crate::ShadowError> {
55 if !self.base.access_policy.can_write(addr, data.len()) {
56 return Err(crate::ShadowError::Denied);
57 }
58
59 self.sb.write_staged(addr, data)
60 }
61
62 pub fn commit(&mut self) -> Result<(), crate::ShadowError> {
68 if !self.sb.any_staged() {
69 return Ok(());
70 }
71
72 let mut should_persist = false;
73 self.sb.for_each_staged(|addr, data| {
74 self.base.write_range_no_persist(addr, data)?;
75 should_persist |=
76 self.base
77 .persist_policy
78 .push_persist_keys_for_range(addr, data.len(), |key| {
79 self.base.persist_trigger.push_key(key)
80 });
81 Ok(())
82 })?;
83
84 self.sb.clear_staged()?;
85
86 if should_persist {
87 self.base.persist_trigger.request_persist();
88 }
89
90 Ok(())
91 }
92
93 #[deprecated(since = "0.1.2", note = "renamed to `commit()`")]
95 pub fn action(&mut self) -> Result<(), crate::ShadowError> {
96 self.commit()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use crate::ShadowError;
103 use crate::persist::NoPersist;
104 use crate::policy::{AllowAllPolicy, NoPersistPolicy};
105 use crate::staged::PatchStagingBuffer;
106 use crate::test_support::{DenyAllPolicy, TestTable};
107 use crate::view::HostView;
108
109 use super::*;
110
111 type TestStage = PatchStagingBuffer<64, 8>;
112
113 #[test]
114 fn commit_applies_staged_writes_to_table() {
115 let mut table = TestTable::new();
116 let policy = AllowAllPolicy::default();
117 let persist_policy = NoPersistPolicy::default();
118 let mut trigger = NoPersist;
119 let mut stage = TestStage::new();
120
121 {
122 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
123 let mut view = HostViewStaged::new(base, &mut stage);
124
125 view.write_range_staged(0, &[0xAA, 0xBB, 0xCC, 0xDD])
126 .unwrap();
127 view.commit().unwrap();
128 }
129
130 let mut buf = [0u8; 4];
132 table.read_range(0, &mut buf).unwrap();
133 assert_eq!(buf, [0xAA, 0xBB, 0xCC, 0xDD]);
134 }
135
136 #[test]
137 fn commit_marks_affected_blocks_dirty() {
138 let mut table = TestTable::new();
139 let policy = AllowAllPolicy::default();
140 let persist_policy = NoPersistPolicy::default();
141 let mut trigger = NoPersist;
142 let mut stage = TestStage::new();
143
144 {
146 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
147 let mut view = HostViewStaged::new(base, &mut stage);
148 view.write_range_staged(0, &[0x01; 4]).unwrap();
149 }
150
151 assert!(!table.any_dirty());
153
154 {
156 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
157 let mut view = HostViewStaged::new(base, &mut stage);
158 view.commit().unwrap();
159 }
160
161 assert!(table.is_dirty(0, 4).unwrap());
162 }
163
164 #[test]
165 fn commit_clears_staging_buffer() {
166 let mut table = TestTable::new();
167 let policy = AllowAllPolicy::default();
168 let persist_policy = NoPersistPolicy::default();
169 let mut trigger = NoPersist;
170 let mut stage = TestStage::new();
171
172 {
173 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
174 let mut view = HostViewStaged::new(base, &mut stage);
175
176 view.write_range_staged(0, &[0x01; 4]).unwrap();
177 view.commit().unwrap();
178 }
179
180 assert!(!stage.any_staged());
181 }
182
183 #[test]
184 fn commit_empty_buffer_is_noop() {
185 let mut table = TestTable::new();
186 let policy = AllowAllPolicy::default();
187 let persist_policy = NoPersistPolicy::default();
188 let mut trigger = NoPersist;
189 let mut stage = TestStage::new();
190
191 {
192 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
193 let mut view = HostViewStaged::new(base, &mut stage);
194
195 view.commit().unwrap();
197 }
198
199 assert!(!table.any_dirty());
200 }
201
202 #[test]
203 fn read_range_overlay_shows_staged_data() {
204 let mut table = TestTable::new();
205 table.write_range(0, &[0x11, 0x22, 0x33, 0x44]).unwrap();
206
207 let policy = AllowAllPolicy::default();
208 let persist_policy = NoPersistPolicy::default();
209 let mut trigger = NoPersist;
210 let mut stage = TestStage::new();
211
212 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
213 let mut view = HostViewStaged::new(base, &mut stage);
214
215 view.write_range_staged(2, &[0xAA, 0xBB]).unwrap();
217
218 let mut buf = [0u8; 4];
219 view.read_range_overlay(0, &mut buf).unwrap();
220
221 assert_eq!(buf, [0x11, 0x22, 0xAA, 0xBB]);
223 }
224
225 #[test]
226 fn read_range_ignores_staged_data() {
227 let mut table = TestTable::new();
228 table.write_range(0, &[0x11, 0x22, 0x33, 0x44]).unwrap();
229
230 let policy = AllowAllPolicy::default();
231 let persist_policy = NoPersistPolicy::default();
232 let mut trigger = NoPersist;
233 let mut stage = TestStage::new();
234
235 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
236 let mut view = HostViewStaged::new(base, &mut stage);
237
238 view.write_range_staged(0, &[0xAA; 4]).unwrap();
240
241 let mut buf = [0u8; 4];
243 view.read_range(0, &mut buf).unwrap();
244 assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
245 }
246
247 #[test]
248 fn staged_write_checks_access_policy() {
249 let mut table = TestTable::new();
250 let policy = DenyAllPolicy;
251 let persist_policy = NoPersistPolicy::default();
252 let mut trigger = NoPersist;
253 let mut stage = TestStage::new();
254
255 let base = HostView::new(&mut table, &policy, &persist_policy, &mut trigger);
256 let mut view = HostViewStaged::new(base, &mut stage);
257
258 assert_eq!(
259 view.write_range_staged(0, &[0x01; 4]),
260 Err(ShadowError::Denied)
261 );
262
263 assert!(!stage.any_staged());
265 }
266}