1use alloc::boxed::Box;
5use alloc::vec::Vec;
6use guestmem_core::ranges::PagedRange;
7use thiserror::Error;
8use zerocopy::FromBytes;
9use zerocopy::Immutable;
10use zerocopy::IntoBytes;
11use zerocopy::KnownLayout;
12
13const PAGE_SIZE: usize = 4096;
14
15#[repr(C)]
16#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
17pub struct GpaRange {
18 pub len: u32,
19 pub offset: u32,
20}
21
22pub fn validate_gpa_ranges(count: usize, buf: &[u64]) -> Result<usize, Error> {
25 let mut rem: &[u64] = buf;
26 for _ in 0..count {
27 let (_, rest) = parse(rem)?;
28 rem = rest;
29 }
30 Ok(buf.len() - rem.len())
31}
32
33#[derive(Debug, Default, Clone)]
34pub struct MultiPagedRangeBuf {
35 buf: Box<[u64]>,
39 valid: usize,
43 count: usize,
45}
46
47impl MultiPagedRangeBuf {
48 pub fn from_range_buffer(count: usize, mut buf: Vec<u64>) -> Result<Self, Error> {
49 let valid = validate_gpa_ranges(count, buf.as_ref())?;
50 buf.truncate(valid);
51 Ok(MultiPagedRangeBuf {
52 buf: buf.into_boxed_slice(),
53 valid,
54 count,
55 })
56 }
57
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn iter(&self) -> MultiPagedRangeIter<'_> {
63 MultiPagedRangeIter {
64 buf: self.buf.as_ref(),
65 count: self.count,
66 }
67 }
68
69 pub fn range_count(&self) -> usize {
70 self.count
71 }
72
73 pub fn first(&self) -> Option<PagedRange<'_>> {
74 self.iter().next()
75 }
76
77 pub fn contiguous_aligned(&self) -> Option<PagedRange<'_>> {
80 if self.count != 1 {
81 return None;
82 }
83 let first = self.first()?;
84 if first.offset() != 0 || first.len() % PAGE_SIZE != 0 {
85 return None;
86 }
87 Some(first)
88 }
89
90 pub fn range_buffer(&self) -> &[u64] {
91 &self.buf[..self.valid]
92 }
93
94 pub fn clear(&mut self) {
96 self.valid = 0;
97 self.count = 0;
98 }
99
100 fn ensure_space(&mut self, additional: usize) -> &mut [u64] {
101 let required = self.valid + additional;
102 if required > self.buf.len() {
103 self.resize_buffer(required);
104 }
105 &mut self.buf[self.valid..required]
106 }
107
108 #[cold]
109 fn resize_buffer(&mut self, new_size: usize) {
110 let mut buf: Vec<u64> = core::mem::take(&mut self.buf).into();
113 buf.resize(new_size, 0);
114 buf.resize(buf.capacity(), 0);
116 self.buf = buf.into_boxed_slice();
117 }
118
119 pub fn push_range(&mut self, range: PagedRange<'_>) {
121 let len = 1 + range.gpns().len();
122 let buf = self.ensure_space(len);
123 let hdr = GpaRange {
124 len: range.len() as u32,
125 offset: range.offset() as u32,
126 };
127 buf[0] = zerocopy::transmute!(hdr);
128 buf[1..].copy_from_slice(range.gpns());
129 self.count += 1;
130 self.valid += len;
131 }
132
133 pub fn try_extend_with<E>(
142 &mut self,
143 len: usize,
144 count: usize,
145 f: impl FnOnce(&mut [u64]) -> Result<(), E>,
146 ) -> Result<Result<(), Error>, E> {
147 let buf = self.ensure_space(len);
148 f(buf)?;
149 let valid_len = match validate_gpa_ranges(count, buf) {
150 Ok(v) => v,
151 Err(e) => return Ok(Err(e)),
152 };
153 self.valid += valid_len;
157 self.count += count;
158 Ok(Ok(()))
159 }
160}
161
162impl<'a> IntoIterator for &'a MultiPagedRangeBuf {
163 type Item = PagedRange<'a>;
164 type IntoIter = MultiPagedRangeIter<'a>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 self.iter()
168 }
169}
170
171impl<'a> FromIterator<PagedRange<'a>> for MultiPagedRangeBuf {
172 fn from_iter<I: IntoIterator<Item = PagedRange<'a>>>(iter: I) -> MultiPagedRangeBuf {
173 let mut this = MultiPagedRangeBuf::new();
174 for range in iter {
175 this.push_range(range);
176 }
177 this
178 }
179}
180
181#[derive(Clone, Debug)]
182pub struct MultiPagedRangeIter<'a> {
183 buf: &'a [u64],
184 count: usize,
185}
186
187impl<'a> Iterator for MultiPagedRangeIter<'a> {
188 type Item = PagedRange<'a>;
189
190 fn next(&mut self) -> Option<Self::Item> {
191 if self.count == 0 {
192 return None;
193 }
194 let hdr = GpaRange::read_from_prefix(self.buf[0].as_bytes())
195 .unwrap()
196 .0; let page_count = ((hdr.offset + hdr.len) as usize).div_ceil(PAGE_SIZE); let (this, rest) = self.buf.split_at(page_count + 1);
199 let range = PagedRange::new(hdr.offset as usize, hdr.len as usize, &this[1..]).unwrap();
200 self.count -= 1;
201 self.buf = rest;
202 Some(range)
203 }
204}
205
206#[derive(Debug, Error)]
207pub enum Error {
208 #[error("empty range")]
209 EmptyRange,
210 #[error("empty byte count")]
211 EmptyByteCount,
212 #[error("range too small")]
213 RangeTooSmall,
214 #[error("byte offset too large")]
215 OffsetTooLarge,
216 #[error("integer overflow")]
217 Overflow,
218}
219
220fn parse(buf: &[u64]) -> Result<(PagedRange<'_>, &[u64]), Error> {
221 let (hdr, gpas) = buf.split_first().ok_or(Error::EmptyRange)?;
222 let byte_count = *hdr as u32;
223 if byte_count == 0 {
224 return Err(Error::EmptyByteCount);
225 }
226 let byte_offset = (*hdr >> 32) as u32;
227 if byte_offset > 0xfff {
228 return Err(Error::OffsetTooLarge);
229 }
230 let pages = (byte_count
231 .checked_add(4095)
232 .ok_or(Error::Overflow)?
233 .checked_add(byte_offset)
234 .ok_or(Error::Overflow)?) as usize
235 / PAGE_SIZE;
236 if gpas.len() < pages {
237 return Err(Error::RangeTooSmall);
238 }
239 let (gpas, rest) = gpas.split_at(pages);
240 assert!(!gpas.is_empty());
241 Ok((
242 PagedRange::new(byte_offset as usize, byte_count as usize, gpas)
243 .expect("already validated"),
244 rest,
245 ))
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn large_offset() {
254 let hdr = GpaRange {
256 len: 1,
257 offset: 0x1000,
258 };
259 let buf = vec![
260 u64::from_le_bytes(hdr.as_bytes().try_into().unwrap()),
261 0xdead_beef,
262 ];
263
264 let err = MultiPagedRangeBuf::from_range_buffer(1, buf).unwrap_err();
266 assert!(matches!(err, Error::OffsetTooLarge));
267 }
268}