blob: e80617b108f4f58a08363a5bc84d21604a0ac6bb [file] [log] [blame]
Imre Kis86fd04a2024-11-29 16:09:59 +01001// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use core::fmt;
5
6use alloc::format;
7use alloc::string::ToString;
8
9use super::{
10 address::{PhysicalAddress, VirtualAddress},
11 TranslationGranule, XlatError,
12};
13
14#[derive(PartialEq)]
15pub struct Block {
16 pub pa: PhysicalAddress,
17 pub va: VirtualAddress,
18 pub size: usize,
19}
20
21impl Block {
22 pub fn new(pa: PhysicalAddress, va: VirtualAddress, size: usize) -> Self {
23 Self { pa, va, size }
24 }
25}
26
27impl fmt::Debug for Block {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.debug_struct("Block")
30 .field("pa", &format_args!("{:#010x}", self.pa.0))
31 .field("va", &format_args!("{:#010x}", self.va.0))
32 .field("size", &format_args!("{:#010x}", self.size))
33 .finish()
34 }
35}
36
37pub struct BlockIterator<const VA_BITS: usize> {
38 pa: PhysicalAddress,
39 va: VirtualAddress,
40 length: usize,
41 granule: TranslationGranule<VA_BITS>,
42}
43
44impl<const VA_BITS: usize> BlockIterator<VA_BITS> {
45 pub fn new(
46 pa: PhysicalAddress,
47 va: VirtualAddress,
48 length: usize,
49 granule: TranslationGranule<VA_BITS>,
50 ) -> Result<Self, XlatError> {
51 let min_granule_mask = granule.block_size_at_level(3) - 1;
52
53 if length == 0 {
54 return Err(XlatError::InvalidParameterError(
55 "Length cannot be 0".to_string(),
56 ));
57 }
58
59 if (pa.0 | va.0 | length) & min_granule_mask != 0 {
60 return Err(XlatError::InvalidParameterError(format!(
61 "Addresses and length must be aligned {:#08x} {:#08x} {:#x} {:#x}",
62 pa.0, va.0, length, min_granule_mask
63 )));
64 }
65
66 Ok(Self {
67 pa,
68 va,
69 length,
70 granule,
71 })
72 }
73}
74
75impl<const VA_BITS: usize> Iterator for BlockIterator<VA_BITS> {
76 type Item = Block;
77
78 fn next(&mut self) -> Option<Self::Item> {
79 if self.length > 0 {
80 let initial_lookup_level = self.granule.initial_lookup_level();
81
82 for block_size in
83 (initial_lookup_level..=3).map(|level| self.granule.block_size_at_level(level))
84 {
85 if (self.pa.0 | self.va.0) & (block_size - 1) == 0 && self.length >= block_size {
86 let block = Block::new(self.pa, self.va, block_size);
87
88 self.pa = self.pa.add_offset(block_size).unwrap();
89 self.va = self.va.add_offset(block_size).unwrap();
90 self.length -= block_size;
91
92 return Some(block);
93 }
94 }
95 }
96
97 None
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 macro_rules! test_block {
106 ( $pa:expr, $va:expr, $size:literal, $blocks:expr ) => {
107 assert_eq!(
108 Block::new(PhysicalAddress($pa), VirtualAddress($va), $size),
109 $blocks
110 );
111 };
112 }
113
114 fn make_block(pa: usize, va: usize, size: usize) -> Block {
115 Block::new(PhysicalAddress(pa), VirtualAddress(va), size)
116 }
117
118 #[test]
119 fn test_block_iterator() {
120 let mut blocks = BlockIterator::new(
121 PhysicalAddress(0x3fff_c000),
122 VirtualAddress(0x3fff_c000),
123 0x4020_5000,
124 TranslationGranule::<36>::Granule4k,
125 )
126 .unwrap();
127 test_block!(0x3fff_c000, 0x3fff_c000, 0x1000, blocks.next().unwrap());
128 test_block!(0x3fff_d000, 0x3fff_d000, 0x1000, blocks.next().unwrap());
129 test_block!(0x3fff_e000, 0x3fff_e000, 0x1000, blocks.next().unwrap());
130 test_block!(0x3fff_f000, 0x3fff_f000, 0x1000, blocks.next().unwrap());
131 test_block!(
132 0x4000_0000,
133 0x4000_0000,
134 0x4000_0000,
135 blocks.next().unwrap()
136 );
137 test_block!(
138 0x8000_0000,
139 0x8000_0000,
140 0x0020_0000,
141 blocks.next().unwrap()
142 );
143 test_block!(0x8020_0000, 0x8020_0000, 0x1000, blocks.next().unwrap());
144 }
145
146 #[test]
147 fn test_block_iterator_unaligned() {
148 let blocks = BlockIterator::new(
149 PhysicalAddress(0x3fff_c000),
150 VirtualAddress(0x3f20_0000),
151 0x200000,
152 TranslationGranule::<36>::Granule4k,
153 )
154 .unwrap();
155 for (i, block) in blocks.enumerate().take(512) {
156 test_block!(
157 0x3fff_c000 + (i << 12),
158 0x3f20_0000 + (i << 12),
159 0x1000,
160 block
161 );
162 }
163 }
164}