blob_pager_and_verifier/
delivery.rs1use anyhow::{Error, anyhow, bail};
6use fuchsia_sync::Mutex;
7use mapping::{DELIVERY_VMO_SIZE, DeliveryCommand, RawDeliveryCommand};
8use std::collections::HashMap;
9use std::sync::Arc;
10use storage_ptr_slice::PtrByteSlice;
11use zx;
12
13#[derive(Copy, Clone)]
15pub struct UnverifiedPages<'a> {
16 slice: PtrByteSlice<'a>,
17 delivery_offset: u64,
19}
20
21impl<'a> UnverifiedPages<'a> {
22 pub(crate) fn new(slice: PtrByteSlice<'a>, delivery_offset: u64) -> Result<Self, zx::Status> {
23 let page_size = zx::system_get_page_size() as usize;
24 if slice.len() % page_size != 0 || delivery_offset % (page_size as u64) != 0 {
25 return Err(zx::Status::INVALID_ARGS);
26 }
27 Ok(Self { slice, delivery_offset })
28 }
29
30 pub fn len_in_bytes(&self) -> usize {
32 self.slice.len()
33 }
34
35 pub fn len_in_pages(&self) -> usize {
37 self.slice.len() / (zx::system_get_page_size() as usize)
38 }
39
40 pub fn delivery_offset(&self) -> u64 {
42 self.delivery_offset
43 }
44
45 pub fn as_ptr_byte_slice(&self) -> PtrByteSlice<'a> {
47 self.slice
48 }
49}
50
51pub trait DeliveryQueueProvider: Send + Sync + 'static {
53 fn deliver_pages(
55 &self,
56 key: u64,
57 target_offset: u64,
58 unverified_pages: UnverifiedPages<'_>,
59 ) -> Result<(), Error>;
60
61 fn register_blob(
63 &self,
64 key: u64,
65 leaf_data: PtrByteSlice<'_>,
66 read_alignment: usize,
67 ) -> Result<(), Error> {
68 let _ = (key, leaf_data, read_alignment);
69 Ok(())
70 }
71}
72
73pub struct TestVmoProvider {
75 pager: Arc<zx::Pager>,
76 delivery_vmo: zx::Vmo,
77 vmos: Mutex<HashMap<u64, zx::Vmo>>,
78}
79
80impl TestVmoProvider {
81 pub fn new(pager: Arc<zx::Pager>, delivery_vmo: zx::Vmo) -> Self {
82 Self { pager, delivery_vmo, vmos: Mutex::new(HashMap::new()) }
83 }
84
85 pub fn delivery_vmo(&self) -> &zx::Vmo {
86 &self.delivery_vmo
87 }
88
89 pub fn register_vmo(&self, key: u64, vmo: zx::Vmo) {
90 self.vmos.lock().insert(key, vmo);
91 }
92
93 pub fn unregister_vmo(&self, key: u64) {
94 self.vmos.lock().remove(&key);
95 }
96}
97
98impl DeliveryQueueProvider for TestVmoProvider {
99 fn deliver_pages(
100 &self,
101 key: u64,
102 target_offset: u64,
103 unverified_pages: UnverifiedPages<'_>,
104 ) -> Result<(), Error> {
105 let vmos = self.vmos.lock();
106 if let Some(target_vmo) = vmos.get(&key) {
107 let length = unverified_pages.len_in_bytes() as u64;
108 if length == 0 {
109 return Ok(());
110 }
111 self.pager.supply_pages(
112 target_vmo,
113 target_offset..target_offset + length,
114 &self.delivery_vmo,
115 unverified_pages.delivery_offset(),
116 )?;
117 Ok(())
118 } else {
119 bail!("Unknown or expired key {key} in deliver_pages");
120 }
121 }
122}
123
124const NUM_DELIVERY_THREADS: usize = 2;
125
126pub struct DeliveryQueueProcessor {
127 delivery_vmo: zx::Vmo,
128 delivery_threads: Vec<std::thread::JoinHandle<()>>,
129}
130
131impl DeliveryQueueProcessor {
132 pub fn spawn(
133 receiver: vmo_fifo::Receiver<RawDeliveryCommand>,
134 provider: Arc<dyn DeliveryQueueProvider>,
135 delivery_vmo: zx::Vmo,
136 ) -> Result<Self, Error> {
137 let delivery_vmo_clone = delivery_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
138 let receiver = Arc::new(receiver);
139 let mut delivery_threads = Vec::with_capacity(NUM_DELIVERY_THREADS);
140
141 for _ in 0..NUM_DELIVERY_THREADS {
142 let receiver = receiver.clone();
143 let provider = provider.clone();
144 let thread = std::thread::spawn(move || {
145 while let Ok((msg, maybe_cmd)) = receiver.claim_with(|msg| {
146 match DeliveryCommand::try_from(**msg) {
147 Ok(cmd @ DeliveryCommand::RegisterBlob { .. }) => {
151 if let Err(e) = Self::handle_delivery(cmd, msg, &*provider) {
152 log::warn!("Failed to handle delivery command: {:?}", e);
153 }
154 None
155 }
156 Ok(cmd) => Some(cmd),
157 Err(e) => {
158 log::warn!("Invalid delivery command opcode: {:?}", e);
159 None
160 }
161 }
162 }) {
163 if let Some(cmd) = maybe_cmd {
164 if let Err(e) = Self::handle_delivery(cmd, &msg, &*provider) {
165 log::warn!("Failed to handle delivery command: {:?}", e);
166 }
167 }
168 if let Err(e) = msg.pop() {
169 log::warn!("Failed to pop delivery message: {:?}", e);
170 break;
171 }
172 }
173 });
174 delivery_threads.push(thread);
175 }
176
177 Ok(Self { delivery_vmo: delivery_vmo_clone, delivery_threads })
178 }
179
180 pub fn delivery_vmo(&self) -> &zx::Vmo {
181 &self.delivery_vmo
182 }
183
184 fn handle_delivery(
185 cmd: DeliveryCommand,
186 raw_msg: &vmo_fifo::Message<'_, RawDeliveryCommand>,
187 provider: &dyn DeliveryQueueProvider,
188 ) -> Result<(), Error> {
189 match cmd {
190 DeliveryCommand::Data { key, target_offset, length, offset } => {
191 if offset.checked_add(length).unwrap_or(u32::MAX) > DELIVERY_VMO_SIZE as u32 {
192 bail!("Data payload out of bounds");
193 }
194 let vmo_offset = raw_msg.payload_region_offset() as u64 + offset as u64;
195 let page_size = zx::system_get_page_size() as u32;
201 if offset % page_size != 0 {
202 bail!("Delivery payload offset must be page aligned: {offset}");
203 }
204 if length % page_size != 0 {
205 bail!("Delivery payload length must be page aligned: {length}");
206 }
207 if target_offset % (page_size as u64) != 0 {
208 bail!("Delivery payload target_offset must be page aligned: {target_offset}");
209 }
210 let unverified_pages =
211 UnverifiedPages::new(raw_msg.payload_slice(offset, length), vmo_offset)
212 .map_err(|s| anyhow!("Invalid unverified pages: {s}"))?;
213 provider.deliver_pages(key, target_offset, unverified_pages)?;
214 }
215 DeliveryCommand::RegisterBlob { key, offset, length, read_alignment } => {
216 if offset.checked_add(length).unwrap_or(u32::MAX) > DELIVERY_VMO_SIZE as u32 {
217 bail!("RegisterBlob payload out of bounds");
218 }
219 let leaf_data = raw_msg.payload_slice(offset, length);
220 provider.register_blob(key, leaf_data, read_alignment as usize)?;
221 }
222 }
223 Ok(())
224 }
225}
226
227impl Drop for DeliveryQueueProcessor {
228 fn drop(&mut self) {
229 let _ = self.delivery_vmo.signal(zx::Signals::empty(), vmo_fifo::SIG_SHUTDOWN);
230 for thread in self.delivery_threads.drain(..) {
231 let _ = thread.join();
232 }
233 }
234}