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