blob: 1cba7c4ac8b44c6b98182fbbb2d77d85fe78043c [file] [log] [blame]
Imre Kis703482d2023-11-30 15:51:26 +01001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Module for handling physical and virtual memory regions
5//!
6//! A region is a continuous memory area in the given memory space.
7
8use alloc::vec::Vec;
9use log::debug;
10
11use super::{
Imre Kisd5b96fd2024-09-11 17:04:32 +020012 address::{PhysicalAddress, VirtualAddress},
Imre Kis703482d2023-11-30 15:51:26 +010013 page_pool::{PagePool, Pages},
14 region_pool::Region,
15};
16
17/// Physical region
18///
19/// A physical memory region can be in three different state
20/// * Unused
21/// * Points to a page pool allocated address
22/// * Points to a physical address without allocation
23pub enum PhysicalRegion {
24 Unused,
25 Allocated(PagePool, Pages),
Imre Kisd5b96fd2024-09-11 17:04:32 +020026 PhysicalAddress(PhysicalAddress),
Imre Kis703482d2023-11-30 15:51:26 +010027}
28
29impl PhysicalRegion {
30 /// Get physical memory address
Imre Kisd5b96fd2024-09-11 17:04:32 +020031 fn get_pa(&self) -> PhysicalAddress {
Imre Kis703482d2023-11-30 15:51:26 +010032 match self {
33 PhysicalRegion::Unused => panic!("Unused area has no PA"),
34 PhysicalRegion::Allocated(_page_pool, pages) => pages.get_pa(),
35 PhysicalRegion::PhysicalAddress(pa) => *pa,
36 }
37 }
38}
39
40/// Virtual region
41///
42/// A virtual memory region has a virtual address, a length and a physical region.
43pub struct VirtualRegion {
Imre Kisd5b96fd2024-09-11 17:04:32 +020044 va: VirtualAddress,
Imre Kis703482d2023-11-30 15:51:26 +010045 length: usize,
46 physical_region: PhysicalRegion,
47}
48
49impl VirtualRegion {
50 /// Create new virtual memory region without a physical region
Imre Kisd5b96fd2024-09-11 17:04:32 +020051 pub fn new(va: VirtualAddress, length: usize) -> Self {
Imre Kis703482d2023-11-30 15:51:26 +010052 Self::new_from_fields(va, length, PhysicalRegion::Unused)
53 }
54
55 /// Create virtual region with points to a given physical address
Imre Kisd5b96fd2024-09-11 17:04:32 +020056 pub fn new_with_pa(pa: PhysicalAddress, va: VirtualAddress, length: usize) -> Self {
Imre Kis703482d2023-11-30 15:51:26 +010057 Self::new_from_fields(va, length, PhysicalRegion::PhysicalAddress(pa))
58 }
59
60 /// Create virtual region by defining all the fields of the object
Imre Kisd5b96fd2024-09-11 17:04:32 +020061 fn new_from_fields(va: VirtualAddress, length: usize, physical_region: PhysicalRegion) -> Self {
Imre Kis703482d2023-11-30 15:51:26 +010062 Self {
63 va,
64 length,
65 physical_region,
66 }
67 }
68
69 /// Get the base address of the linked physical region
Imre Kisd5b96fd2024-09-11 17:04:32 +020070 pub fn get_pa(&self) -> PhysicalAddress {
Imre Kis703482d2023-11-30 15:51:26 +010071 self.physical_region.get_pa()
72 }
73
74 /// Get physical address for a virtual address
Imre Kisd5b96fd2024-09-11 17:04:32 +020075 pub fn get_pa_for_va(&self, va: VirtualAddress) -> PhysicalAddress {
76 let offset = va.diff(self.va).unwrap();
Imre Kis703482d2023-11-30 15:51:26 +010077
78 assert!(offset < self.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +020079 self.get_pa().add_offset(offset).unwrap()
Imre Kis703482d2023-11-30 15:51:26 +010080 }
81}
82
83impl Region for VirtualRegion {
84 type Resource = PhysicalRegion;
Imre Kisd5b96fd2024-09-11 17:04:32 +020085 type Base = VirtualAddress;
Imre Kis589aa052024-09-10 20:19:47 +020086 type Length = usize;
Imre Kisf0370e82024-11-18 16:24:55 +010087 type Alignment = usize;
Imre Kis703482d2023-11-30 15:51:26 +010088
Imre Kisd5b96fd2024-09-11 17:04:32 +020089 fn base(&self) -> Self::Base {
Imre Kis703482d2023-11-30 15:51:26 +010090 self.va
91 }
92
Imre Kisd5b96fd2024-09-11 17:04:32 +020093 fn length(&self) -> Self::Length {
Imre Kis703482d2023-11-30 15:51:26 +010094 self.length
95 }
96
97 fn used(&self) -> bool {
98 !matches!(self.physical_region, PhysicalRegion::Unused)
99 }
100
Imre Kisd5b96fd2024-09-11 17:04:32 +0200101 fn contains(&self, base: Self::Base, length: Self::Length) -> bool {
Imre Kis703482d2023-11-30 15:51:26 +0100102 if let (Some(end), Some(self_end)) =
Imre Kisd5b96fd2024-09-11 17:04:32 +0200103 (base.add_offset(length), self.va.add_offset(self.length))
Imre Kis703482d2023-11-30 15:51:26 +0100104 {
105 self.va <= base && end <= self_end
106 } else {
107 false
108 }
109 }
110
Imre Kisf0370e82024-11-18 16:24:55 +0100111 fn try_alloc_aligned(
112 &self,
113 length: Self::Length,
114 alignment: Self::Alignment,
115 ) -> Option<Self::Base> {
116 let aligned_base = self.va.align_up(alignment);
117 let base_offset = aligned_base.diff(self.va)?;
118
119 let required_length = base_offset.checked_add(length)?;
120 if required_length <= self.length {
121 Some(aligned_base)
122 } else {
123 None
124 }
125 }
126
Imre Kis703482d2023-11-30 15:51:26 +0100127 fn try_append(&mut self, other: &Self) -> bool {
128 if let (Some(self_end), Some(new_length)) = (
Imre Kisd5b96fd2024-09-11 17:04:32 +0200129 self.va.add_offset(self.length),
Imre Kis703482d2023-11-30 15:51:26 +0100130 self.length.checked_add(other.length),
131 ) {
132 if self_end == other.va {
133 // VA is consecutive
134 match (&self.physical_region, &other.physical_region) {
135 (PhysicalRegion::Unused, PhysicalRegion::Unused) => {
136 // Unused range can be merged without further conditions
137 self.length = new_length;
138 return true;
139 }
140 (
141 PhysicalRegion::PhysicalAddress(self_pa),
142 PhysicalRegion::PhysicalAddress(other_pa),
143 ) => {
144 // Used ranges can be only merged if the PA doesn't overflow and it is
145 // consecutive
Imre Kisd5b96fd2024-09-11 17:04:32 +0200146 if let Some(self_end_pa) = self_pa.add_offset(self.length) {
Imre Kis703482d2023-11-30 15:51:26 +0100147 if self_end_pa == *other_pa {
148 self.length = new_length;
149 return true;
150 }
151 }
152 }
153
154 // PhyisicalRegion::Allocated instances cannot be merged at the moment. Not sure
155 // if it's a valid use case. If needed the pages has to be merged which might
156 // require tricks to invalidate the pages of 'other'.
157 _ => {}
158 }
159 }
160 }
161
162 false
163 }
164
165 fn create_split(
166 &self,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200167 base: Self::Base,
168 length: Self::Length,
Imre Kis703482d2023-11-30 15:51:26 +0100169 resource: Option<Self::Resource>,
170 ) -> (Self, Vec<Self>) {
171 assert!(self.contains(base, length));
172 assert!(self.used() != resource.is_some());
173
174 if let Some(physical_region) = resource {
175 // Self is unused, setting part of it to used
176 let pa = physical_region.get_pa();
177
178 let mut res = Vec::new();
179 if self.va != base {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200180 res.push(Self::new(self.va, base.diff(self.va).unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100181 }
182
183 res.push(Self::new_from_fields(base, length, physical_region));
184
Imre Kisd5b96fd2024-09-11 17:04:32 +0200185 let end = base.add_offset(length).unwrap();
186 let self_end = self.va.add_offset(self.length).unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100187 if end != self_end {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200188 res.push(Self::new(end, self_end.diff(end).unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100189 }
190
191 (
192 Self::new_from_fields(base, length, PhysicalRegion::PhysicalAddress(pa)),
193 res,
194 )
195 } else {
196 // Self is used, mark part of it unused
197 let mut res = Vec::new();
198 if self.va != base {
199 let physical_region = match &self.physical_region {
200 PhysicalRegion::Allocated(_page_pool, _pages) => {
201 todo!("Implement Pages::split");
202 }
203 PhysicalRegion::PhysicalAddress(pa) => PhysicalRegion::PhysicalAddress(*pa),
204 _ => {
205 panic!("Splitting unused region by other unused")
206 }
207 };
208
209 res.push(Self::new_from_fields(
210 self.va,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200211 base.diff(self.va).unwrap(),
Imre Kis703482d2023-11-30 15:51:26 +0100212 physical_region,
213 ));
214 }
215
216 res.push(Self::new(base, length));
217
Imre Kisd5b96fd2024-09-11 17:04:32 +0200218 let end = base.add_offset(length).unwrap();
219 let self_end = self.va.add_offset(self.length).unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100220 if end != self_end {
221 let physical_region = match &self.physical_region {
222 PhysicalRegion::Allocated(_page_pool, _pages) => {
223 todo!("Implement Pages::split");
224 }
225 PhysicalRegion::PhysicalAddress(pa) => {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200226 let offset = end.diff(self.va).unwrap();
227 PhysicalRegion::PhysicalAddress(pa.add_offset(offset).unwrap())
Imre Kis703482d2023-11-30 15:51:26 +0100228 }
229 _ => {
230 panic!("Splitting unused region by other unused")
231 }
232 };
233
234 res.push(Self::new_from_fields(
235 end,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200236 self_end.diff(end).unwrap(),
Imre Kis703482d2023-11-30 15:51:26 +0100237 physical_region,
238 ));
239 }
240
241 (Self::new(base, length), res)
242 }
243 }
244}
245
246impl Drop for VirtualRegion {
247 /// If the virtual region has a linked physical region which was allocated then release the
248 /// allocated pages.
249 fn drop(&mut self) {
250 let mut physical_region = PhysicalRegion::Unused;
251
252 core::mem::swap(&mut self.physical_region, &mut physical_region);
253
254 if let PhysicalRegion::Allocated(page_pool, pages) = physical_region {
255 debug!(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200256 "Dropping physical region with pages: PA={:#08x} VA={:#08x}",
257 pages.get_pa().0,
258 self.base().0,
Imre Kis703482d2023-11-30 15:51:26 +0100259 );
260
261 page_pool.release_pages(pages).unwrap();
262 }
263 }
264}
265
266#[cfg(test)]
Imre Kis42935a22024-10-17 11:30:16 +0200267mod tests {
Imre Kis42935a22024-10-17 11:30:16 +0200268 use super::*;
Imre Kis21d7f722025-01-17 17:55:35 +0100269 use crate::{page_pool::PagePoolArea, KernelAddressTranslator};
270
271 struct DummyKernelAddressTranslator {}
272
273 impl KernelAddressTranslator for DummyKernelAddressTranslator {
274 fn kernel_to_pa(va: VirtualAddress) -> PhysicalAddress {
275 va.identity_pa()
276 }
277
278 fn pa_to_kernel(pa: PhysicalAddress) -> VirtualAddress {
279 pa.identity_va()
280 }
281 }
Imre Kis703482d2023-11-30 15:51:26 +0100282
Imre Kis42935a22024-10-17 11:30:16 +0200283 #[test]
284 #[should_panic]
285 fn test_physical_region_unused() {
286 let region = PhysicalRegion::Unused;
287 region.get_pa();
288 }
Imre Kis703482d2023-11-30 15:51:26 +0100289
Imre Kis42935a22024-10-17 11:30:16 +0200290 #[test]
291 fn test_physical_region() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200292 const PA: PhysicalAddress = PhysicalAddress(0x0123_4567_89ab_cdef);
Imre Kis42935a22024-10-17 11:30:16 +0200293 const LENGTH: usize = 0x8000_0000_0000;
Imre Kis703482d2023-11-30 15:51:26 +0100294
Imre Kis42935a22024-10-17 11:30:16 +0200295 static PAGE_POOL_AREA: PagePoolArea<16> = PagePoolArea::new();
Imre Kisd5b96fd2024-09-11 17:04:32 +0200296 let region = PhysicalRegion::Allocated(
Imre Kis21d7f722025-01-17 17:55:35 +0100297 PagePool::new::<DummyKernelAddressTranslator, 16>(&PAGE_POOL_AREA),
Imre Kisd5b96fd2024-09-11 17:04:32 +0200298 Pages::new(PA.0, LENGTH, true),
299 );
Imre Kis42935a22024-10-17 11:30:16 +0200300 assert_eq!(PA, region.get_pa());
Imre Kis703482d2023-11-30 15:51:26 +0100301
Imre Kis42935a22024-10-17 11:30:16 +0200302 let region = PhysicalRegion::PhysicalAddress(PA);
303 assert_eq!(PA, region.get_pa());
304 }
Imre Kis703482d2023-11-30 15:51:26 +0100305
Imre Kis42935a22024-10-17 11:30:16 +0200306 #[test]
307 fn test_virtual_region() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200308 const VA: VirtualAddress = VirtualAddress(0x0123_4567_89ab_cdef);
309 const PA: PhysicalAddress = PhysicalAddress(0xfedc_ba98_7654_3210);
Imre Kis42935a22024-10-17 11:30:16 +0200310 const LENGTH: usize = 0x8000_0000_0000;
Imre Kis703482d2023-11-30 15:51:26 +0100311
Imre Kis42935a22024-10-17 11:30:16 +0200312 let region = VirtualRegion::new(VA, LENGTH);
313 assert_eq!(VA, region.va);
314 assert_eq!(VA, region.base());
315 assert_eq!(LENGTH, region.length);
316 assert_eq!(LENGTH, region.length());
317 assert!(matches!(region.physical_region, PhysicalRegion::Unused));
318 assert!(!region.used());
Imre Kis703482d2023-11-30 15:51:26 +0100319
Imre Kis42935a22024-10-17 11:30:16 +0200320 let region = VirtualRegion::new_with_pa(PA, VA, LENGTH);
321 assert_eq!(VA, region.va);
322 assert_eq!(VA, region.base());
323 assert_eq!(LENGTH, region.length);
324 assert_eq!(LENGTH, region.length());
325 assert!(matches!(
326 region.physical_region,
327 PhysicalRegion::PhysicalAddress(_)
328 ));
329 assert_eq!(PA, region.get_pa());
330 assert!(region.used());
331 }
Imre Kis703482d2023-11-30 15:51:26 +0100332
Imre Kis42935a22024-10-17 11:30:16 +0200333 #[test]
334 fn test_virtual_region_get_pa_for_va() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200335 let region = VirtualRegion::new_with_pa(
336 PhysicalAddress(0x8000_0000_0000_0000),
337 VirtualAddress(0x4000_0000_0000_0000),
338 0x1000,
Imre Kis42935a22024-10-17 11:30:16 +0200339 );
340 assert_eq!(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200341 PhysicalAddress(0x8000_0000_0000_0000),
342 region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0000))
Imre Kis42935a22024-10-17 11:30:16 +0200343 );
344 assert_eq!(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200345 PhysicalAddress(0x8000_0000_0000_0001),
346 region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0001))
347 );
348 assert_eq!(
349 PhysicalAddress(0x8000_0000_0000_0fff),
350 region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0fff))
Imre Kis42935a22024-10-17 11:30:16 +0200351 );
352 }
Imre Kis703482d2023-11-30 15:51:26 +0100353
Imre Kis42935a22024-10-17 11:30:16 +0200354 #[test]
355 #[should_panic]
356 fn test_virtual_region_get_pa_for_va_low_va() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200357 let region = VirtualRegion::new_with_pa(
358 PhysicalAddress(0x8000_0000_0000_0000),
359 VirtualAddress(0x4000_0000_0000_0000),
360 0x1000,
361 );
362 region.get_pa_for_va(VirtualAddress(0x3fff_ffff_ffff_ffff));
Imre Kis42935a22024-10-17 11:30:16 +0200363 }
Imre Kis703482d2023-11-30 15:51:26 +0100364
Imre Kis42935a22024-10-17 11:30:16 +0200365 #[test]
366 #[should_panic]
367 fn test_virtual_region_get_pa_for_va_high_va() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200368 let region = VirtualRegion::new_with_pa(
369 PhysicalAddress(0x8000_0000_0000_0000),
370 VirtualAddress(0x4000_0000_0000_0000),
371 0x1000,
372 );
373 region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_1000));
Imre Kis42935a22024-10-17 11:30:16 +0200374 }
Imre Kis703482d2023-11-30 15:51:26 +0100375
Imre Kis42935a22024-10-17 11:30:16 +0200376 #[test]
Imre Kisf0370e82024-11-18 16:24:55 +0100377 fn test_virtual_region_try_alloc() {
378 let region = VirtualRegion::new_with_pa(
379 PhysicalAddress(0x8000_1000),
380 VirtualAddress(0x4000_1000),
381 0x10000,
382 );
383
384 assert_eq!(
385 Some(VirtualAddress(0x4000_1000)),
386 region.try_alloc(0x1000, None)
387 );
388 assert_eq!(
389 Some(VirtualAddress(0x4000_2000)),
390 region.try_alloc(0x1000, Some(0x2000))
391 );
392 assert_eq!(None, region.try_alloc(0x1000, Some(0x10_0000)));
393 }
394
395 #[test]
Imre Kis42935a22024-10-17 11:30:16 +0200396 fn test_virtual_region_contains() {
397 const VA: usize = 0x8000_0000_0000_0000;
398 const LENGTH: usize = 0x8000_0000_0000;
Imre Kis703482d2023-11-30 15:51:26 +0100399
Imre Kisd5b96fd2024-09-11 17:04:32 +0200400 let region_overflow_end =
401 VirtualRegion::new(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000);
402 assert!(!region_overflow_end.contains(VirtualAddress(0x8000_0000_0000_0000), 1));
Imre Kis703482d2023-11-30 15:51:26 +0100403
Imre Kisd5b96fd2024-09-11 17:04:32 +0200404 let region =
405 VirtualRegion::new(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0000);
406 assert!(!region.contains(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000));
Imre Kis703482d2023-11-30 15:51:26 +0100407
Imre Kisd5b96fd2024-09-11 17:04:32 +0200408 assert!(!region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0001));
409 assert!(!region.contains(VirtualAddress(0x3fff_ffff_ffff_ffff), 0x8000_0000_0000_0000));
410 assert!(region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0000));
411 assert!(region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x7fff_ffff_ffff_ffff));
412 assert!(region.contains(VirtualAddress(0x4000_0000_0000_0001), 0x7fff_ffff_ffff_ffff));
Imre Kis42935a22024-10-17 11:30:16 +0200413 }
Imre Kis703482d2023-11-30 15:51:26 +0100414
Imre Kis42935a22024-10-17 11:30:16 +0200415 #[test]
416 fn test_virtual_region_try_append() {
417 // Both unused
Imre Kisd5b96fd2024-09-11 17:04:32 +0200418 let mut region_unused0 = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000);
419 let mut region_unused1 = VirtualRegion::new(VirtualAddress(0x4000_1000), 0x1000);
Imre Kis703482d2023-11-30 15:51:26 +0100420
Imre Kis42935a22024-10-17 11:30:16 +0200421 assert!(!region_unused1.try_append(&region_unused0));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200422 assert_eq!(VirtualAddress(0x4000_0000), region_unused0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200423 assert_eq!(0x1000, region_unused0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200424 assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200425 assert_eq!(0x1000, region_unused1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100426
Imre Kis42935a22024-10-17 11:30:16 +0200427 assert!(region_unused0.try_append(&region_unused1));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200428 assert_eq!(VirtualAddress(0x4000_0000), region_unused0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200429 assert_eq!(0x2000, region_unused0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200430 assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200431 assert_eq!(0x1000, region_unused1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100432
Imre Kis42935a22024-10-17 11:30:16 +0200433 // Unused and PA region
Imre Kisd5b96fd2024-09-11 17:04:32 +0200434 let mut region_unused = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000);
435 let region_physical = VirtualRegion::new_with_pa(
436 PhysicalAddress(0x8000_0000),
437 VirtualAddress(0x4000_1000),
438 0x1000,
439 );
Imre Kis42935a22024-10-17 11:30:16 +0200440 assert!(!region_unused.try_append(&region_physical));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200441 assert_eq!(VirtualAddress(0x4000_0000), region_unused.va);
Imre Kis42935a22024-10-17 11:30:16 +0200442 assert_eq!(0x1000, region_unused.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200443 assert_eq!(VirtualAddress(0x4000_1000), region_physical.va);
Imre Kis42935a22024-10-17 11:30:16 +0200444 assert_eq!(0x1000, region_physical.length);
Imre Kis703482d2023-11-30 15:51:26 +0100445
Imre Kis42935a22024-10-17 11:30:16 +0200446 // Both PA regions but non-consecutive PA ranges
Imre Kisd5b96fd2024-09-11 17:04:32 +0200447 let mut region_physical0 = VirtualRegion::new_with_pa(
448 PhysicalAddress(0x8000_0000),
449 VirtualAddress(0x4000_0000),
450 0x1000,
451 );
452 let region_physical1 = VirtualRegion::new_with_pa(
453 PhysicalAddress(0x9000_0000),
454 VirtualAddress(0x4000_1000),
455 0x1000,
456 );
Imre Kis42935a22024-10-17 11:30:16 +0200457 assert!(!region_physical0.try_append(&region_physical1));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200458 assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200459 assert_eq!(0x1000, region_physical0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200460 assert_eq!(VirtualAddress(0x4000_1000), region_physical1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200461 assert_eq!(0x1000, region_physical1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100462
Imre Kis42935a22024-10-17 11:30:16 +0200463 // Both PA regions with consecutive PA ranges
Imre Kisd5b96fd2024-09-11 17:04:32 +0200464 let mut region_physical0 = VirtualRegion::new_with_pa(
465 PhysicalAddress(0x8000_0000),
466 VirtualAddress(0x4000_0000),
467 0x1000,
468 );
469 let region_physical1 = VirtualRegion::new_with_pa(
470 PhysicalAddress(0x8000_1000),
471 VirtualAddress(0x4000_1000),
472 0x1000,
473 );
Imre Kis42935a22024-10-17 11:30:16 +0200474 assert!(region_physical0.try_append(&region_physical1));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200475 assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200476 assert_eq!(0x2000, region_physical0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200477 assert_eq!(VirtualAddress(0x4000_1000), region_physical1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200478 assert_eq!(0x1000, region_physical1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100479
Imre Kis42935a22024-10-17 11:30:16 +0200480 // VA overflow
Imre Kisd5b96fd2024-09-11 17:04:32 +0200481 let mut region_unused0: VirtualRegion =
482 VirtualRegion::new(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000);
483 let mut region_unused1 = VirtualRegion::new(VirtualAddress(0x4000_1000), 0x1000);
Imre Kis703482d2023-11-30 15:51:26 +0100484
Imre Kis42935a22024-10-17 11:30:16 +0200485 assert!(!region_unused0.try_append(&region_unused1));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200486 assert_eq!(VirtualAddress(0x8000_0000_0000_0000), region_unused0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200487 assert_eq!(0x8000_0000_0000_0000, region_unused0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200488 assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200489 assert_eq!(0x1000, region_unused1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100490
Imre Kis42935a22024-10-17 11:30:16 +0200491 assert!(!region_unused1.try_append(&region_unused0));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200492 assert_eq!(VirtualAddress(0x8000_0000_0000_0000), region_unused0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200493 assert_eq!(0x8000_0000_0000_0000, region_unused0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200494 assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200495 assert_eq!(0x1000, region_unused1.length);
Imre Kis703482d2023-11-30 15:51:26 +0100496
Imre Kis42935a22024-10-17 11:30:16 +0200497 // PA overflow
Imre Kisd5b96fd2024-09-11 17:04:32 +0200498 let mut region_physical0 = VirtualRegion::new_with_pa(
499 PhysicalAddress(0x8000_0000_0000_0000),
500 VirtualAddress(0x4000_0000),
501 0x8000_0000_0000_0000,
502 );
503 let region_physical1 = VirtualRegion::new_with_pa(
504 PhysicalAddress(0x9000_0000),
505 VirtualAddress(0x8000_0000_4000_0000),
506 0x1000,
507 );
Imre Kis42935a22024-10-17 11:30:16 +0200508 assert!(!region_physical0.try_append(&region_physical1));
Imre Kisd5b96fd2024-09-11 17:04:32 +0200509 assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va);
Imre Kis42935a22024-10-17 11:30:16 +0200510 assert_eq!(0x8000_0000_0000_0000, region_physical0.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200511 assert_eq!(VirtualAddress(0x8000_0000_4000_0000), region_physical1.va);
Imre Kis42935a22024-10-17 11:30:16 +0200512 assert_eq!(0x1000, region_physical1.length);
513 }
Imre Kis703482d2023-11-30 15:51:26 +0100514
Imre Kis42935a22024-10-17 11:30:16 +0200515 #[test]
516 fn test_virtual_region_create_split_by_used() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200517 let region_unused = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x4000);
Imre Kis703482d2023-11-30 15:51:26 +0100518
Imre Kis42935a22024-10-17 11:30:16 +0200519 // New region at the start
520 let (new_region, splitted_regions) = region_unused.create_split(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200521 VirtualAddress(0x4000_0000),
Imre Kis42935a22024-10-17 11:30:16 +0200522 0x1000,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200523 Some(PhysicalRegion::PhysicalAddress(PhysicalAddress(
524 0x8000_0000,
525 ))),
Imre Kis42935a22024-10-17 11:30:16 +0200526 );
Imre Kis703482d2023-11-30 15:51:26 +0100527
Imre Kisd5b96fd2024-09-11 17:04:32 +0200528 assert_eq!(VirtualAddress(0x4000_0000), new_region.va);
Imre Kis42935a22024-10-17 11:30:16 +0200529 assert_eq!(0x1000, new_region.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200530 assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200531 assert!(matches!(
532 new_region.physical_region,
533 PhysicalRegion::PhysicalAddress(_)
534 ));
Imre Kis703482d2023-11-30 15:51:26 +0100535
Imre Kisd5b96fd2024-09-11 17:04:32 +0200536 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200537 assert_eq!(0x1000, splitted_regions[0].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200538 assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200539 assert!(matches!(
540 splitted_regions[0].physical_region,
541 PhysicalRegion::PhysicalAddress(_)
542 ));
Imre Kis703482d2023-11-30 15:51:26 +0100543
Imre Kisd5b96fd2024-09-11 17:04:32 +0200544 assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200545 assert_eq!(0x3000, splitted_regions[1].length);
546 assert!(matches!(
547 splitted_regions[1].physical_region,
548 PhysicalRegion::Unused
549 ));
Imre Kis703482d2023-11-30 15:51:26 +0100550
Imre Kis42935a22024-10-17 11:30:16 +0200551 // New region in the middle
552 let (new_region, splitted_regions) = region_unused.create_split(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200553 VirtualAddress(0x4000_1000),
Imre Kis42935a22024-10-17 11:30:16 +0200554 0x1000,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200555 Some(PhysicalRegion::PhysicalAddress(PhysicalAddress(
556 0x8000_0000,
557 ))),
Imre Kis42935a22024-10-17 11:30:16 +0200558 );
Imre Kis703482d2023-11-30 15:51:26 +0100559
Imre Kisd5b96fd2024-09-11 17:04:32 +0200560 assert_eq!(VirtualAddress(0x4000_1000), new_region.va);
Imre Kis42935a22024-10-17 11:30:16 +0200561 assert_eq!(0x1000, new_region.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200562 assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200563 assert!(matches!(
564 new_region.physical_region,
565 PhysicalRegion::PhysicalAddress(_)
566 ));
Imre Kis703482d2023-11-30 15:51:26 +0100567
Imre Kisd5b96fd2024-09-11 17:04:32 +0200568 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200569 assert_eq!(0x1000, splitted_regions[0].length);
570 assert!(matches!(
571 splitted_regions[0].physical_region,
572 PhysicalRegion::Unused
573 ));
Imre Kis703482d2023-11-30 15:51:26 +0100574
Imre Kisd5b96fd2024-09-11 17:04:32 +0200575 assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200576 assert_eq!(0x1000, splitted_regions[1].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200577 assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[1].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200578 assert!(matches!(
579 splitted_regions[1].physical_region,
580 PhysicalRegion::PhysicalAddress(_)
581 ));
Imre Kis703482d2023-11-30 15:51:26 +0100582
Imre Kisd5b96fd2024-09-11 17:04:32 +0200583 assert_eq!(VirtualAddress(0x4000_2000), splitted_regions[2].va);
Imre Kis42935a22024-10-17 11:30:16 +0200584 assert_eq!(0x2000, splitted_regions[2].length);
585 assert!(matches!(
586 splitted_regions[2].physical_region,
587 PhysicalRegion::Unused
588 ));
Imre Kis703482d2023-11-30 15:51:26 +0100589
Imre Kis42935a22024-10-17 11:30:16 +0200590 // New region at the end
591 let (new_region, splitted_regions) = region_unused.create_split(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200592 VirtualAddress(0x4000_3000),
Imre Kis42935a22024-10-17 11:30:16 +0200593 0x1000,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200594 Some(PhysicalRegion::PhysicalAddress(PhysicalAddress(
595 0x8000_0000,
596 ))),
Imre Kis42935a22024-10-17 11:30:16 +0200597 );
Imre Kis703482d2023-11-30 15:51:26 +0100598
Imre Kisd5b96fd2024-09-11 17:04:32 +0200599 assert_eq!(VirtualAddress(0x4000_3000), new_region.va);
Imre Kis42935a22024-10-17 11:30:16 +0200600 assert_eq!(0x1000, new_region.length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200601 assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200602 assert!(matches!(
603 new_region.physical_region,
604 PhysicalRegion::PhysicalAddress(_)
605 ));
Imre Kis703482d2023-11-30 15:51:26 +0100606
Imre Kisd5b96fd2024-09-11 17:04:32 +0200607 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200608 assert_eq!(0x3000, splitted_regions[0].length);
609 assert!(matches!(
610 splitted_regions[0].physical_region,
611 PhysicalRegion::Unused
612 ));
Imre Kis703482d2023-11-30 15:51:26 +0100613
Imre Kisd5b96fd2024-09-11 17:04:32 +0200614 assert_eq!(VirtualAddress(0x4000_3000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200615 assert_eq!(0x1000, splitted_regions[1].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200616 assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[1].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200617 assert!(matches!(
618 splitted_regions[1].physical_region,
619 PhysicalRegion::PhysicalAddress(_)
620 ));
621 }
Imre Kis703482d2023-11-30 15:51:26 +0100622
Imre Kis42935a22024-10-17 11:30:16 +0200623 #[test]
624 fn test_virtual_region_create_split_by_unused() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200625 let region_unused = VirtualRegion::new_with_pa(
626 PhysicalAddress(0x8000_0000),
627 VirtualAddress(0x4000_0000),
628 0x4000,
629 );
Imre Kis703482d2023-11-30 15:51:26 +0100630
Imre Kis42935a22024-10-17 11:30:16 +0200631 // New region at the start
Imre Kisd5b96fd2024-09-11 17:04:32 +0200632 let (new_region, splitted_regions) =
633 region_unused.create_split(VirtualAddress(0x4000_0000), 0x1000, None);
Imre Kis703482d2023-11-30 15:51:26 +0100634
Imre Kisd5b96fd2024-09-11 17:04:32 +0200635 assert_eq!(VirtualAddress(0x4000_0000), new_region.va); // TODO: why do we need explicit type here?
Imre Kis42935a22024-10-17 11:30:16 +0200636 assert_eq!(0x1000, new_region.length);
637 assert!(matches!(new_region.physical_region, PhysicalRegion::Unused));
Imre Kis703482d2023-11-30 15:51:26 +0100638
Imre Kisd5b96fd2024-09-11 17:04:32 +0200639 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200640 assert_eq!(0x1000, splitted_regions[0].length);
641 assert!(matches!(
642 splitted_regions[0].physical_region,
643 PhysicalRegion::Unused
644 ));
Imre Kis703482d2023-11-30 15:51:26 +0100645
Imre Kisd5b96fd2024-09-11 17:04:32 +0200646 assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200647 assert_eq!(0x3000, splitted_regions[1].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200648 assert_eq!(PhysicalAddress(0x8000_1000), splitted_regions[1].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200649 assert!(matches!(
650 splitted_regions[1].physical_region,
651 PhysicalRegion::PhysicalAddress(_)
652 ));
Imre Kis703482d2023-11-30 15:51:26 +0100653
Imre Kis42935a22024-10-17 11:30:16 +0200654 // New region in the middle
Imre Kisd5b96fd2024-09-11 17:04:32 +0200655 let (new_region, splitted_regions) =
656 region_unused.create_split(VirtualAddress(0x4000_1000), 0x1000, None);
Imre Kis703482d2023-11-30 15:51:26 +0100657
Imre Kisd5b96fd2024-09-11 17:04:32 +0200658 assert_eq!(VirtualAddress(0x4000_1000), new_region.va);
Imre Kis42935a22024-10-17 11:30:16 +0200659 assert_eq!(0x1000, new_region.length);
660 assert!(matches!(new_region.physical_region, PhysicalRegion::Unused));
Imre Kis703482d2023-11-30 15:51:26 +0100661
Imre Kisd5b96fd2024-09-11 17:04:32 +0200662 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200663 assert_eq!(0x1000, splitted_regions[0].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200664 assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200665 assert!(matches!(
666 splitted_regions[0].physical_region,
667 PhysicalRegion::PhysicalAddress(_)
668 ));
Imre Kis703482d2023-11-30 15:51:26 +0100669
Imre Kisd5b96fd2024-09-11 17:04:32 +0200670 assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200671 assert_eq!(0x1000, splitted_regions[1].length);
672 assert!(matches!(
673 splitted_regions[1].physical_region,
674 PhysicalRegion::Unused
675 ));
Imre Kis703482d2023-11-30 15:51:26 +0100676
Imre Kisd5b96fd2024-09-11 17:04:32 +0200677 assert_eq!(VirtualAddress(0x4000_2000), splitted_regions[2].va);
Imre Kis42935a22024-10-17 11:30:16 +0200678 assert_eq!(0x2000, splitted_regions[2].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200679 assert_eq!(PhysicalAddress(0x8000_2000), splitted_regions[2].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200680 assert!(matches!(
681 splitted_regions[2].physical_region,
682 PhysicalRegion::PhysicalAddress(_)
683 ));
Imre Kis703482d2023-11-30 15:51:26 +0100684
Imre Kis42935a22024-10-17 11:30:16 +0200685 // New region at the end
Imre Kisd5b96fd2024-09-11 17:04:32 +0200686 let (new_region, splitted_regions) =
687 region_unused.create_split(VirtualAddress(0x4000_3000), 0x1000, None);
Imre Kis703482d2023-11-30 15:51:26 +0100688
Imre Kisd5b96fd2024-09-11 17:04:32 +0200689 assert_eq!(VirtualAddress(0x4000_3000), new_region.va);
Imre Kis42935a22024-10-17 11:30:16 +0200690 assert_eq!(0x1000, new_region.length);
691 assert!(matches!(new_region.physical_region, PhysicalRegion::Unused));
Imre Kis703482d2023-11-30 15:51:26 +0100692
Imre Kisd5b96fd2024-09-11 17:04:32 +0200693 assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va);
Imre Kis42935a22024-10-17 11:30:16 +0200694 assert_eq!(0x3000, splitted_regions[0].length);
Imre Kisd5b96fd2024-09-11 17:04:32 +0200695 assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa());
Imre Kis42935a22024-10-17 11:30:16 +0200696 assert!(matches!(
697 splitted_regions[0].physical_region,
698 PhysicalRegion::PhysicalAddress(_)
699 ));
Imre Kis703482d2023-11-30 15:51:26 +0100700
Imre Kisd5b96fd2024-09-11 17:04:32 +0200701 assert_eq!(VirtualAddress(0x4000_3000), splitted_regions[1].va);
Imre Kis42935a22024-10-17 11:30:16 +0200702 assert_eq!(0x1000, splitted_regions[1].length);
Imre Kis703482d2023-11-30 15:51:26 +0100703
Imre Kis42935a22024-10-17 11:30:16 +0200704 assert!(matches!(
705 splitted_regions[1].physical_region,
706 PhysicalRegion::Unused
707 ));
708 }
Imre Kis703482d2023-11-30 15:51:26 +0100709
Imre Kis42935a22024-10-17 11:30:16 +0200710 #[test]
711 #[should_panic]
712 fn test_virtual_region_does_not_contain() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200713 let region = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000);
Imre Kis42935a22024-10-17 11:30:16 +0200714 region.create_split(
Imre Kisd5b96fd2024-09-11 17:04:32 +0200715 VirtualAddress(0x8000_0000),
Imre Kis42935a22024-10-17 11:30:16 +0200716 0x1000,
Imre Kisd5b96fd2024-09-11 17:04:32 +0200717 Some(PhysicalRegion::PhysicalAddress(PhysicalAddress(
718 0xc000_0000,
719 ))),
Imre Kis42935a22024-10-17 11:30:16 +0200720 );
721 }
Imre Kis703482d2023-11-30 15:51:26 +0100722
Imre Kis42935a22024-10-17 11:30:16 +0200723 #[test]
724 #[should_panic]
725 fn test_virtual_region_create_split_same_used() {
Imre Kisd5b96fd2024-09-11 17:04:32 +0200726 let region = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000);
727 region.create_split(
728 VirtualAddress(0x4000_0000),
729 0x1000,
730 Some(PhysicalRegion::Unused),
731 );
Imre Kis42935a22024-10-17 11:30:16 +0200732 }
Imre Kis703482d2023-11-30 15:51:26 +0100733
Imre Kis42935a22024-10-17 11:30:16 +0200734 #[test]
735 fn test_virtual_region_drop() {
Imre Kis42935a22024-10-17 11:30:16 +0200736 static PAGE_POOL_AREA: PagePoolArea<8192> = PagePoolArea::new();
Imre Kis21d7f722025-01-17 17:55:35 +0100737 let page_pool = PagePool::new::<DummyKernelAddressTranslator, 8192>(&PAGE_POOL_AREA);
Imre Kis631127d2024-11-21 13:09:01 +0100738 let page = page_pool.allocate_pages(4096, None).unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100739
Imre Kis42935a22024-10-17 11:30:16 +0200740 let physical_region = PhysicalRegion::Allocated(page_pool, page);
Imre Kis703482d2023-11-30 15:51:26 +0100741
Imre Kis42935a22024-10-17 11:30:16 +0200742 // Testing physical region drop through virtualregion
Imre Kisd5b96fd2024-09-11 17:04:32 +0200743 let virtual_region =
744 VirtualRegion::new_from_fields(VirtualAddress(0x4000_0000), 1000, physical_region);
Imre Kis42935a22024-10-17 11:30:16 +0200745 drop(virtual_region);
746 }
Imre Kis703482d2023-11-30 15:51:26 +0100747}