blob: acf88229c7b71633fbca0a59d066a12a7bd8dc6e [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
Imre Kis86fd04a2024-11-29 16:09:59 +0100106 #[test]
107 fn test_block_iterator() {
108 let mut blocks = BlockIterator::new(
109 PhysicalAddress(0x3fff_c000),
110 VirtualAddress(0x3fff_c000),
111 0x4020_5000,
112 TranslationGranule::<36>::Granule4k,
113 )
114 .unwrap();
115 test_block!(0x3fff_c000, 0x3fff_c000, 0x1000, blocks.next().unwrap());
116 test_block!(0x3fff_d000, 0x3fff_d000, 0x1000, blocks.next().unwrap());
117 test_block!(0x3fff_e000, 0x3fff_e000, 0x1000, blocks.next().unwrap());
118 test_block!(0x3fff_f000, 0x3fff_f000, 0x1000, blocks.next().unwrap());
119 test_block!(
120 0x4000_0000,
121 0x4000_0000,
122 0x4000_0000,
123 blocks.next().unwrap()
124 );
125 test_block!(
126 0x8000_0000,
127 0x8000_0000,
128 0x0020_0000,
129 blocks.next().unwrap()
130 );
131 test_block!(0x8020_0000, 0x8020_0000, 0x1000, blocks.next().unwrap());
132 }
133
134 #[test]
135 fn test_block_iterator_unaligned() {
136 let blocks = BlockIterator::new(
137 PhysicalAddress(0x3fff_c000),
138 VirtualAddress(0x3f20_0000),
139 0x200000,
140 TranslationGranule::<36>::Granule4k,
141 )
142 .unwrap();
143 for (i, block) in blocks.enumerate().take(512) {
144 test_block!(
145 0x3fff_c000 + (i << 12),
146 0x3f20_0000 + (i << 12),
147 0x1000,
148 block
149 );
150 }
151 }
152}