1use zx::sys::zx_paddr_t;
8use zx::system_get_page_size;
9
10#[derive(Debug, Clone)]
18pub struct PhysIter<'a> {
19 chunk_list: &'a [zx_paddr_t],
20 buf_length: usize,
21 max_length: usize,
22 chunk_size: usize,
23 merge: bool,
24 chunk_index: usize,
25 offset_in_chunk: usize,
26 current: Option<(zx_paddr_t, usize)>,
27 iterated_bytes: usize,
28}
29
30impl<'a> PhysIter<'a> {
31 pub fn new(chunk_list: &'a [zx_paddr_t], vmo_offset: usize, buf_length: usize) -> Self {
39 Self::new_with_options(
40 chunk_list,
41 system_get_page_size() as usize,
42 vmo_offset,
43 buf_length,
44 usize::MAX,
45 false,
46 )
47 }
48
49 pub fn new_with_options(
57 chunk_list: &'a [zx_paddr_t],
58 chunk_size: usize,
59 vmo_offset: usize,
60 buf_length: usize,
61 max_length: usize,
62 merge: bool,
63 ) -> Self {
64 assert!(!chunk_list.is_empty(), "chunk_list must not be empty");
65 assert!(buf_length > 0, "buf_length must be greater than 0");
66 assert!(chunk_size.is_power_of_two(), "chunk_size must be a power of 2");
67 assert!(
68 chunk_list.len() == 1 || vmo_offset < chunk_size,
69 "vmo_offset must be less than chunk_size when chunk_list has multiple entries"
70 );
71
72 let max_length = if max_length == 0 { usize::MAX } else { max_length };
73
74 let mut iter = Self {
75 chunk_list,
76 buf_length,
77 max_length,
78 chunk_size,
79 merge,
80 chunk_index: 0,
81 offset_in_chunk: vmo_offset,
82 current: None,
83 iterated_bytes: 0,
84 };
85 let first_addr = chunk_list[0] + vmo_offset;
86 let first_size = iter.boundary_truncate();
87 iter.current = Some((first_addr, first_size));
88 iter
89 }
90
91 fn boundary_truncate(&self) -> usize {
92 let end_of_buffer = self.buf_length - self.iterated_bytes;
93 if self.chunk_list.len() == 1 {
94 return self.max_length.min(end_of_buffer);
95 }
96
97 let mut end_of_chunk = self.chunk_size - self.offset_in_chunk;
98
99 if self.merge {
100 let mut idx = self.chunk_index;
101 while end_of_chunk < self.max_length
102 && end_of_chunk < end_of_buffer
103 && idx + 1 < self.chunk_list.len()
104 && self.chunk_list[idx] + self.chunk_size == self.chunk_list[idx + 1]
105 {
106 end_of_chunk += self.chunk_size;
107 idx += 1;
108 }
109 }
110
111 self.max_length.min(end_of_chunk.min(end_of_buffer))
112 }
113}
114
115impl Iterator for PhysIter<'_> {
116 type Item = (zx_paddr_t, usize);
117
118 fn next(&mut self) -> Option<Self::Item> {
119 let (cur_addr, cur_size) = self.current?;
120 let item = (cur_addr, cur_size);
121
122 self.iterated_bytes += cur_size;
123 if self.iterated_bytes == self.buf_length {
124 self.current = None;
125 return Some(item);
126 }
127
128 let next_addr = if self.chunk_list.len() == 1 {
129 cur_addr + cur_size
130 } else {
131 let next_offset = self.offset_in_chunk + cur_size;
132 self.chunk_index += next_offset / self.chunk_size;
133 self.offset_in_chunk = next_offset % self.chunk_size;
134 self.chunk_list[self.chunk_index] + self.offset_in_chunk
135 };
136
137 let next_size = self.boundary_truncate();
138 self.current = Some((next_addr, next_size));
139 Some(item)
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use test_case::test_case;
147
148 const PAGE_SIZE: usize = 4096;
149 const HALF_PAGE: usize = 2048;
150
151 #[test_case(
152 &[PAGE_SIZE, 2 * PAGE_SIZE],
153 PAGE_SIZE,
154 0,
155 2 * PAGE_SIZE,
156 usize::MAX,
157 false,
158 &[(PAGE_SIZE, PAGE_SIZE), (2 * PAGE_SIZE, PAGE_SIZE)];
159 "simple_page_boundary"
160 )]
161 #[test_case(
162 &[PAGE_SIZE, 3 * PAGE_SIZE, 5 * PAGE_SIZE],
163 PAGE_SIZE,
164 0,
165 3 * PAGE_SIZE,
166 usize::MAX,
167 false,
168 &[(PAGE_SIZE, PAGE_SIZE), (3 * PAGE_SIZE, PAGE_SIZE), (5 * PAGE_SIZE, PAGE_SIZE)];
169 "non_contiguous_pages"
170 )]
171 #[test_case(
172 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE],
173 PAGE_SIZE,
174 PAGE_SIZE - 5,
175 2 * PAGE_SIZE + 5,
176 usize::MAX,
177 false,
178 &[(2 * PAGE_SIZE - 5, 5), (2 * PAGE_SIZE, PAGE_SIZE), (3 * PAGE_SIZE, PAGE_SIZE)];
179 "partial_first_page_contiguous"
180 )]
181 #[test_case(
182 &[PAGE_SIZE, 3 * PAGE_SIZE, 5 * PAGE_SIZE],
183 PAGE_SIZE,
184 PAGE_SIZE - 5,
185 2 * PAGE_SIZE + 5,
186 usize::MAX,
187 false,
188 &[(2 * PAGE_SIZE - 5, 5), (3 * PAGE_SIZE, PAGE_SIZE), (5 * PAGE_SIZE, PAGE_SIZE)];
189 "partial_first_page_non_contiguous"
190 )]
191 #[test_case(
192 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE],
193 PAGE_SIZE,
194 0,
195 2 * PAGE_SIZE + 5,
196 usize::MAX,
197 false,
198 &[(PAGE_SIZE, PAGE_SIZE), (2 * PAGE_SIZE, PAGE_SIZE), (3 * PAGE_SIZE, 5)];
199 "partial_last_page_contiguous"
200 )]
201 #[test_case(
202 &[PAGE_SIZE, 3 * PAGE_SIZE, 5 * PAGE_SIZE],
203 PAGE_SIZE,
204 0,
205 2 * PAGE_SIZE + 5,
206 usize::MAX,
207 false,
208 &[(PAGE_SIZE, PAGE_SIZE), (3 * PAGE_SIZE, PAGE_SIZE), (5 * PAGE_SIZE, 5)];
209 "partial_last_page_non_contiguous"
210 )]
211 #[test_case(
212 &[PAGE_SIZE, 2 * PAGE_SIZE],
213 PAGE_SIZE,
214 0,
215 2 * PAGE_SIZE,
216 HALF_PAGE,
217 false,
218 &[
219 (2 * HALF_PAGE, HALF_PAGE),
220 (3 * HALF_PAGE, HALF_PAGE),
221 (4 * HALF_PAGE, HALF_PAGE),
222 (5 * HALF_PAGE, HALF_PAGE),
223 ];
224 "sub_chunk_max_length"
225 )]
226 #[test_case(
227 &[PAGE_SIZE],
228 PAGE_SIZE,
229 0,
230 1 << 20,
231 usize::MAX,
232 false,
233 &[(PAGE_SIZE, 1 << 20)];
234 "zx_bti_contiguous_large_buffer"
235 )]
236 #[test_case(
237 &[HALF_PAGE, 2 * HALF_PAGE, 3 * HALF_PAGE],
238 HALF_PAGE,
239 0,
240 3 * HALF_PAGE,
241 usize::MAX,
242 false,
243 &[(HALF_PAGE, HALF_PAGE), (2 * HALF_PAGE, HALF_PAGE), (3 * HALF_PAGE, HALF_PAGE)];
244 "zx_bti_compress_contiguous"
245 )]
246 #[test_case(
247 &[HALF_PAGE, 3 * HALF_PAGE, 5 * HALF_PAGE],
248 HALF_PAGE,
249 0,
250 3 * HALF_PAGE,
251 usize::MAX,
252 false,
253 &[(HALF_PAGE, HALF_PAGE), (3 * HALF_PAGE, HALF_PAGE), (5 * HALF_PAGE, HALF_PAGE)];
254 "zx_bti_compress_non_contiguous"
255 )]
256 #[test_case(
257 &[PAGE_SIZE],
258 PAGE_SIZE,
259 0,
260 2 * PAGE_SIZE,
261 HALF_PAGE,
262 false,
263 &[
264 (2 * HALF_PAGE, HALF_PAGE),
265 (3 * HALF_PAGE, HALF_PAGE),
266 (4 * HALF_PAGE, HALF_PAGE),
267 (5 * HALF_PAGE, HALF_PAGE),
268 ];
269 "zx_bti_contiguous_sub_chunk_max_length"
270 )]
271 #[test_case(
272 &[PAGE_SIZE, 3 * PAGE_SIZE],
273 PAGE_SIZE,
274 0,
275 2 * PAGE_SIZE,
276 HALF_PAGE,
277 false,
278 &[
279 (2 * HALF_PAGE, HALF_PAGE),
280 (3 * HALF_PAGE, HALF_PAGE),
281 (6 * HALF_PAGE, HALF_PAGE),
282 (7 * HALF_PAGE, HALF_PAGE),
283 ];
284 "zx_bti_non_contiguous_sub_chunk_max_length"
285 )]
286 #[test_case(
287 &[PAGE_SIZE, 2 * PAGE_SIZE],
288 PAGE_SIZE,
289 0,
290 2 * PAGE_SIZE,
291 usize::MAX,
292 true,
293 &[(PAGE_SIZE, 2 * PAGE_SIZE)];
294 "simple_page_boundary_merge"
295 )]
296 #[test_case(
297 &[PAGE_SIZE, 3 * PAGE_SIZE, 5 * PAGE_SIZE],
298 PAGE_SIZE,
299 0,
300 3 * PAGE_SIZE,
301 usize::MAX,
302 true,
303 &[(PAGE_SIZE, PAGE_SIZE), (3 * PAGE_SIZE, PAGE_SIZE), (5 * PAGE_SIZE, PAGE_SIZE)];
304 "non_contiguous_pages_merge"
305 )]
306 #[test_case(
307 &[PAGE_SIZE, 2 * PAGE_SIZE, 4 * PAGE_SIZE, 5 * PAGE_SIZE, 6 * PAGE_SIZE],
308 PAGE_SIZE,
309 0,
310 5 * PAGE_SIZE,
311 usize::MAX,
312 true,
313 &[(PAGE_SIZE, 2 * PAGE_SIZE), (4 * PAGE_SIZE, 3 * PAGE_SIZE)];
314 "mixed_contiguous_and_non_contiguous_merge"
315 )]
316 #[test_case(
317 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE],
318 PAGE_SIZE,
319 PAGE_SIZE - 5,
320 2 * PAGE_SIZE + 5,
321 usize::MAX,
322 true,
323 &[(2 * PAGE_SIZE - 5, 2 * PAGE_SIZE + 5)];
324 "partial_first_page_contiguous_merge"
325 )]
326 #[test_case(
327 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE],
328 PAGE_SIZE,
329 0,
330 2 * PAGE_SIZE + 5,
331 usize::MAX,
332 true,
333 &[(PAGE_SIZE, 2 * PAGE_SIZE + 5)];
334 "partial_last_page_contiguous_merge"
335 )]
336 #[test_case(
337 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE, 4 * PAGE_SIZE],
338 PAGE_SIZE,
339 0,
340 4 * PAGE_SIZE,
341 2 * PAGE_SIZE,
342 true,
343 &[(PAGE_SIZE, 2 * PAGE_SIZE), (3 * PAGE_SIZE, 2 * PAGE_SIZE)];
344 "merge_respects_chunk_aligned_max_length"
345 )]
346 #[test_case(
347 &[PAGE_SIZE, 2 * PAGE_SIZE, 3 * PAGE_SIZE, 5 * PAGE_SIZE],
348 PAGE_SIZE,
349 0,
350 4 * PAGE_SIZE,
351 PAGE_SIZE + HALF_PAGE,
352 true,
353 &[
354 (PAGE_SIZE, PAGE_SIZE + HALF_PAGE),
355 (2 * PAGE_SIZE + HALF_PAGE, PAGE_SIZE + HALF_PAGE),
356 (5 * PAGE_SIZE, PAGE_SIZE),
357 ];
358 "merge_respects_unaligned_max_length"
359 )]
360 #[test_case(
361 &[HALF_PAGE, 2 * HALF_PAGE, 3 * HALF_PAGE],
362 HALF_PAGE,
363 0,
364 3 * HALF_PAGE,
365 usize::MAX,
366 true,
367 &[(HALF_PAGE, 3 * HALF_PAGE)];
368 "zx_bti_compress_contiguous_merge"
369 )]
370 #[test_case(
371 &[0x22_0000, 0x35_0000],
372 0x10_0000,
373 0xf_8000,
374 0x1_0000,
375 0x1_0000,
376 true,
377 &[(0x31_8000, 0x8000), (0x35_0000, 0x8000)];
378 "page_aligned_large_chunks_non_contiguous"
379 )]
380 #[test_case(
381 &[0x22_0000, 0x32_0000],
382 0x10_0000,
383 0xf_8000,
384 0x1_0000,
385 0x1_0000,
386 true,
387 &[(0x31_8000, 0x1_0000)];
388 "page_aligned_large_chunks_contiguous_merge"
389 )]
390 #[test_case(
391 &[0x22_0000, 0x35_0000],
392 0x10_0000,
393 0xd_8000,
394 0x3_0000,
395 0x1_0000,
396 true,
397 &[(0x2f_8000, 0x1_0000), (0x30_8000, 0x1_0000), (0x31_8000, 0x8000), (0x35_0000, 0x8000)];
398 "page_aligned_large_chunks_multi_step_across_boundary"
399 )]
400 fn test_phys_iter(
401 chunk_list: &[zx_paddr_t],
402 chunk_size: usize,
403 vmo_offset: usize,
404 buf_length: usize,
405 max_length: usize,
406 merge: bool,
407 expected: &[(zx_paddr_t, usize)],
408 ) {
409 let iter = PhysIter::new_with_options(
410 chunk_list, chunk_size, vmo_offset, buf_length, max_length, merge,
411 );
412 let actual: Vec<_> = iter.collect();
413 assert_eq!(actual, expected);
414 }
415}