| // SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com> |
| // SPDX-License-Identifier: MIT OR Apache-2.0 |
| |
| //! Module for handling physical and virtual memory regions |
| //! |
| //! A region is a continuous memory area in the given memory space. |
| |
| use alloc::vec::Vec; |
| use log::debug; |
| |
| use super::{ |
| address::{PhysicalAddress, VirtualAddress}, |
| page_pool::{PagePool, Pages}, |
| region_pool::Region, |
| }; |
| |
| /// Physical region |
| /// |
| /// A physical memory region can be in three different state |
| /// * Unused |
| /// * Points to a page pool allocated address |
| /// * Points to a physical address without allocation |
| pub enum PhysicalRegion { |
| Unused, |
| Allocated(PagePool, Pages), |
| PhysicalAddress(PhysicalAddress), |
| } |
| |
| impl PhysicalRegion { |
| /// Get physical memory address |
| fn get_pa(&self) -> PhysicalAddress { |
| match self { |
| PhysicalRegion::Unused => panic!("Unused area has no PA"), |
| PhysicalRegion::Allocated(_page_pool, pages) => pages.get_pa(), |
| PhysicalRegion::PhysicalAddress(pa) => *pa, |
| } |
| } |
| } |
| |
| /// Virtual region |
| /// |
| /// A virtual memory region has a virtual address, a length and a physical region. |
| pub struct VirtualRegion { |
| va: VirtualAddress, |
| length: usize, |
| physical_region: PhysicalRegion, |
| } |
| |
| impl VirtualRegion { |
| /// Create new virtual memory region without a physical region |
| pub fn new(va: VirtualAddress, length: usize) -> Self { |
| Self::new_from_fields(va, length, PhysicalRegion::Unused) |
| } |
| |
| /// Create virtual region with points to a given physical address |
| pub fn new_with_pa(pa: PhysicalAddress, va: VirtualAddress, length: usize) -> Self { |
| Self::new_from_fields(va, length, PhysicalRegion::PhysicalAddress(pa)) |
| } |
| |
| /// Create virtual region by defining all the fields of the object |
| fn new_from_fields(va: VirtualAddress, length: usize, physical_region: PhysicalRegion) -> Self { |
| Self { |
| va, |
| length, |
| physical_region, |
| } |
| } |
| |
| /// Get the base address of the linked physical region |
| pub fn get_pa(&self) -> PhysicalAddress { |
| self.physical_region.get_pa() |
| } |
| |
| /// Get physical address for a virtual address |
| pub fn get_pa_for_va(&self, va: VirtualAddress) -> PhysicalAddress { |
| let offset = va.diff(self.va).unwrap(); |
| |
| assert!(offset < self.length); |
| self.get_pa().add_offset(offset).unwrap() |
| } |
| } |
| |
| impl Region for VirtualRegion { |
| type Resource = PhysicalRegion; |
| type Base = VirtualAddress; |
| type Length = usize; |
| type Alignment = usize; |
| |
| fn base(&self) -> Self::Base { |
| self.va |
| } |
| |
| fn length(&self) -> Self::Length { |
| self.length |
| } |
| |
| fn used(&self) -> bool { |
| !matches!(self.physical_region, PhysicalRegion::Unused) |
| } |
| |
| fn contains(&self, base: Self::Base, length: Self::Length) -> bool { |
| if let (Some(end), Some(self_end)) = |
| (base.add_offset(length), self.va.add_offset(self.length)) |
| { |
| self.va <= base && end <= self_end |
| } else { |
| false |
| } |
| } |
| |
| fn try_alloc_aligned( |
| &self, |
| length: Self::Length, |
| alignment: Self::Alignment, |
| ) -> Option<Self::Base> { |
| let aligned_base = self.va.align_up(alignment); |
| let base_offset = aligned_base.diff(self.va)?; |
| |
| let required_length = base_offset.checked_add(length)?; |
| if required_length <= self.length { |
| Some(aligned_base) |
| } else { |
| None |
| } |
| } |
| |
| fn try_append(&mut self, other: &Self) -> bool { |
| if let (Some(self_end), Some(new_length)) = ( |
| self.va.add_offset(self.length), |
| self.length.checked_add(other.length), |
| ) { |
| if self_end == other.va { |
| // VA is consecutive |
| match (&self.physical_region, &other.physical_region) { |
| (PhysicalRegion::Unused, PhysicalRegion::Unused) => { |
| // Unused range can be merged without further conditions |
| self.length = new_length; |
| return true; |
| } |
| ( |
| PhysicalRegion::PhysicalAddress(self_pa), |
| PhysicalRegion::PhysicalAddress(other_pa), |
| ) => { |
| // Used ranges can be only merged if the PA doesn't overflow and it is |
| // consecutive |
| if let Some(self_end_pa) = self_pa.add_offset(self.length) { |
| if self_end_pa == *other_pa { |
| self.length = new_length; |
| return true; |
| } |
| } |
| } |
| |
| // PhyisicalRegion::Allocated instances cannot be merged at the moment. Not sure |
| // if it's a valid use case. If needed the pages has to be merged which might |
| // require tricks to invalidate the pages of 'other'. |
| _ => {} |
| } |
| } |
| } |
| |
| false |
| } |
| |
| fn create_split( |
| &self, |
| base: Self::Base, |
| length: Self::Length, |
| resource: Option<Self::Resource>, |
| ) -> (Self, Vec<Self>) { |
| assert!(self.contains(base, length)); |
| assert!(self.used() != resource.is_some()); |
| |
| if let Some(physical_region) = resource { |
| // Self is unused, setting part of it to used |
| let pa = physical_region.get_pa(); |
| |
| let mut res = Vec::new(); |
| if self.va != base { |
| res.push(Self::new(self.va, base.diff(self.va).unwrap())); |
| } |
| |
| res.push(Self::new_from_fields(base, length, physical_region)); |
| |
| let end = base.add_offset(length).unwrap(); |
| let self_end = self.va.add_offset(self.length).unwrap(); |
| if end != self_end { |
| res.push(Self::new(end, self_end.diff(end).unwrap())); |
| } |
| |
| ( |
| Self::new_from_fields(base, length, PhysicalRegion::PhysicalAddress(pa)), |
| res, |
| ) |
| } else { |
| // Self is used, mark part of it unused |
| let mut res = Vec::new(); |
| if self.va != base { |
| let physical_region = match &self.physical_region { |
| PhysicalRegion::Allocated(_page_pool, _pages) => { |
| todo!("Implement Pages::split"); |
| } |
| PhysicalRegion::PhysicalAddress(pa) => PhysicalRegion::PhysicalAddress(*pa), |
| _ => { |
| panic!("Splitting unused region by other unused") |
| } |
| }; |
| |
| res.push(Self::new_from_fields( |
| self.va, |
| base.diff(self.va).unwrap(), |
| physical_region, |
| )); |
| } |
| |
| res.push(Self::new(base, length)); |
| |
| let end = base.add_offset(length).unwrap(); |
| let self_end = self.va.add_offset(self.length).unwrap(); |
| if end != self_end { |
| let physical_region = match &self.physical_region { |
| PhysicalRegion::Allocated(_page_pool, _pages) => { |
| todo!("Implement Pages::split"); |
| } |
| PhysicalRegion::PhysicalAddress(pa) => { |
| let offset = end.diff(self.va).unwrap(); |
| PhysicalRegion::PhysicalAddress(pa.add_offset(offset).unwrap()) |
| } |
| _ => { |
| panic!("Splitting unused region by other unused") |
| } |
| }; |
| |
| res.push(Self::new_from_fields( |
| end, |
| self_end.diff(end).unwrap(), |
| physical_region, |
| )); |
| } |
| |
| (Self::new(base, length), res) |
| } |
| } |
| } |
| |
| impl Drop for VirtualRegion { |
| /// If the virtual region has a linked physical region which was allocated then release the |
| /// allocated pages. |
| fn drop(&mut self) { |
| let mut physical_region = PhysicalRegion::Unused; |
| |
| core::mem::swap(&mut self.physical_region, &mut physical_region); |
| |
| if let PhysicalRegion::Allocated(page_pool, pages) = physical_region { |
| debug!( |
| "Dropping physical region with pages: PA={:#08x} VA={:#08x}", |
| pages.get_pa().0, |
| self.base().0, |
| ); |
| |
| page_pool.release_pages(pages).unwrap(); |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::{page_pool::PagePoolArea, KernelAddressTranslator}; |
| |
| struct DummyKernelAddressTranslator {} |
| |
| impl KernelAddressTranslator for DummyKernelAddressTranslator { |
| fn kernel_to_pa(va: VirtualAddress) -> PhysicalAddress { |
| va.identity_pa() |
| } |
| |
| fn pa_to_kernel(pa: PhysicalAddress) -> VirtualAddress { |
| pa.identity_va() |
| } |
| } |
| |
| #[test] |
| #[should_panic] |
| fn test_physical_region_unused() { |
| let region = PhysicalRegion::Unused; |
| region.get_pa(); |
| } |
| |
| #[test] |
| fn test_physical_region() { |
| const PA: PhysicalAddress = PhysicalAddress(0x0123_4567_89ab_cdef); |
| const LENGTH: usize = 0x8000_0000_0000; |
| |
| static PAGE_POOL_AREA: PagePoolArea<16> = PagePoolArea::new(); |
| let region = PhysicalRegion::Allocated( |
| PagePool::new::<DummyKernelAddressTranslator, 16>(&PAGE_POOL_AREA), |
| Pages::new(PA.0, LENGTH, true), |
| ); |
| assert_eq!(PA, region.get_pa()); |
| |
| let region = PhysicalRegion::PhysicalAddress(PA); |
| assert_eq!(PA, region.get_pa()); |
| } |
| |
| #[test] |
| fn test_virtual_region() { |
| const VA: VirtualAddress = VirtualAddress(0x0123_4567_89ab_cdef); |
| const PA: PhysicalAddress = PhysicalAddress(0xfedc_ba98_7654_3210); |
| const LENGTH: usize = 0x8000_0000_0000; |
| |
| let region = VirtualRegion::new(VA, LENGTH); |
| assert_eq!(VA, region.va); |
| assert_eq!(VA, region.base()); |
| assert_eq!(LENGTH, region.length); |
| assert_eq!(LENGTH, region.length()); |
| assert!(matches!(region.physical_region, PhysicalRegion::Unused)); |
| assert!(!region.used()); |
| |
| let region = VirtualRegion::new_with_pa(PA, VA, LENGTH); |
| assert_eq!(VA, region.va); |
| assert_eq!(VA, region.base()); |
| assert_eq!(LENGTH, region.length); |
| assert_eq!(LENGTH, region.length()); |
| assert!(matches!( |
| region.physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| assert_eq!(PA, region.get_pa()); |
| assert!(region.used()); |
| } |
| |
| #[test] |
| fn test_virtual_region_get_pa_for_va() { |
| let region = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000_0000_0000), |
| VirtualAddress(0x4000_0000_0000_0000), |
| 0x1000, |
| ); |
| assert_eq!( |
| PhysicalAddress(0x8000_0000_0000_0000), |
| region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0000)) |
| ); |
| assert_eq!( |
| PhysicalAddress(0x8000_0000_0000_0001), |
| region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0001)) |
| ); |
| assert_eq!( |
| PhysicalAddress(0x8000_0000_0000_0fff), |
| region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_0fff)) |
| ); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn test_virtual_region_get_pa_for_va_low_va() { |
| let region = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000_0000_0000), |
| VirtualAddress(0x4000_0000_0000_0000), |
| 0x1000, |
| ); |
| region.get_pa_for_va(VirtualAddress(0x3fff_ffff_ffff_ffff)); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn test_virtual_region_get_pa_for_va_high_va() { |
| let region = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000_0000_0000), |
| VirtualAddress(0x4000_0000_0000_0000), |
| 0x1000, |
| ); |
| region.get_pa_for_va(VirtualAddress(0x4000_0000_0000_1000)); |
| } |
| |
| #[test] |
| fn test_virtual_region_try_alloc() { |
| let region = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_1000), |
| VirtualAddress(0x4000_1000), |
| 0x10000, |
| ); |
| |
| assert_eq!( |
| Some(VirtualAddress(0x4000_1000)), |
| region.try_alloc(0x1000, None) |
| ); |
| assert_eq!( |
| Some(VirtualAddress(0x4000_2000)), |
| region.try_alloc(0x1000, Some(0x2000)) |
| ); |
| assert_eq!(None, region.try_alloc(0x1000, Some(0x10_0000))); |
| } |
| |
| #[test] |
| fn test_virtual_region_contains() { |
| const VA: usize = 0x8000_0000_0000_0000; |
| const LENGTH: usize = 0x8000_0000_0000; |
| |
| let region_overflow_end = |
| VirtualRegion::new(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000); |
| assert!(!region_overflow_end.contains(VirtualAddress(0x8000_0000_0000_0000), 1)); |
| |
| let region = |
| VirtualRegion::new(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0000); |
| assert!(!region.contains(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000)); |
| |
| assert!(!region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0001)); |
| assert!(!region.contains(VirtualAddress(0x3fff_ffff_ffff_ffff), 0x8000_0000_0000_0000)); |
| assert!(region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x8000_0000_0000_0000)); |
| assert!(region.contains(VirtualAddress(0x4000_0000_0000_0000), 0x7fff_ffff_ffff_ffff)); |
| assert!(region.contains(VirtualAddress(0x4000_0000_0000_0001), 0x7fff_ffff_ffff_ffff)); |
| } |
| |
| #[test] |
| fn test_virtual_region_try_append() { |
| // Both unused |
| let mut region_unused0 = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000); |
| let mut region_unused1 = VirtualRegion::new(VirtualAddress(0x4000_1000), 0x1000); |
| |
| assert!(!region_unused1.try_append(®ion_unused0)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_unused0.va); |
| assert_eq!(0x1000, region_unused0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va); |
| assert_eq!(0x1000, region_unused1.length); |
| |
| assert!(region_unused0.try_append(®ion_unused1)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_unused0.va); |
| assert_eq!(0x2000, region_unused0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va); |
| assert_eq!(0x1000, region_unused1.length); |
| |
| // Unused and PA region |
| let mut region_unused = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000); |
| let region_physical = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000), |
| VirtualAddress(0x4000_1000), |
| 0x1000, |
| ); |
| assert!(!region_unused.try_append(®ion_physical)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_unused.va); |
| assert_eq!(0x1000, region_unused.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_physical.va); |
| assert_eq!(0x1000, region_physical.length); |
| |
| // Both PA regions but non-consecutive PA ranges |
| let mut region_physical0 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000), |
| VirtualAddress(0x4000_0000), |
| 0x1000, |
| ); |
| let region_physical1 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x9000_0000), |
| VirtualAddress(0x4000_1000), |
| 0x1000, |
| ); |
| assert!(!region_physical0.try_append(®ion_physical1)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va); |
| assert_eq!(0x1000, region_physical0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_physical1.va); |
| assert_eq!(0x1000, region_physical1.length); |
| |
| // Both PA regions with consecutive PA ranges |
| let mut region_physical0 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000), |
| VirtualAddress(0x4000_0000), |
| 0x1000, |
| ); |
| let region_physical1 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_1000), |
| VirtualAddress(0x4000_1000), |
| 0x1000, |
| ); |
| assert!(region_physical0.try_append(®ion_physical1)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va); |
| assert_eq!(0x2000, region_physical0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_physical1.va); |
| assert_eq!(0x1000, region_physical1.length); |
| |
| // VA overflow |
| let mut region_unused0: VirtualRegion = |
| VirtualRegion::new(VirtualAddress(0x8000_0000_0000_0000), 0x8000_0000_0000_0000); |
| let mut region_unused1 = VirtualRegion::new(VirtualAddress(0x4000_1000), 0x1000); |
| |
| assert!(!region_unused0.try_append(®ion_unused1)); |
| assert_eq!(VirtualAddress(0x8000_0000_0000_0000), region_unused0.va); |
| assert_eq!(0x8000_0000_0000_0000, region_unused0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va); |
| assert_eq!(0x1000, region_unused1.length); |
| |
| assert!(!region_unused1.try_append(®ion_unused0)); |
| assert_eq!(VirtualAddress(0x8000_0000_0000_0000), region_unused0.va); |
| assert_eq!(0x8000_0000_0000_0000, region_unused0.length); |
| assert_eq!(VirtualAddress(0x4000_1000), region_unused1.va); |
| assert_eq!(0x1000, region_unused1.length); |
| |
| // PA overflow |
| let mut region_physical0 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000_0000_0000), |
| VirtualAddress(0x4000_0000), |
| 0x8000_0000_0000_0000, |
| ); |
| let region_physical1 = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x9000_0000), |
| VirtualAddress(0x8000_0000_4000_0000), |
| 0x1000, |
| ); |
| assert!(!region_physical0.try_append(®ion_physical1)); |
| assert_eq!(VirtualAddress(0x4000_0000), region_physical0.va); |
| assert_eq!(0x8000_0000_0000_0000, region_physical0.length); |
| assert_eq!(VirtualAddress(0x8000_0000_4000_0000), region_physical1.va); |
| assert_eq!(0x1000, region_physical1.length); |
| } |
| |
| #[test] |
| fn test_virtual_region_create_split_by_used() { |
| let region_unused = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x4000); |
| |
| // New region at the start |
| let (new_region, splitted_regions) = region_unused.create_split( |
| VirtualAddress(0x4000_0000), |
| 0x1000, |
| Some(PhysicalRegion::PhysicalAddress(PhysicalAddress( |
| 0x8000_0000, |
| ))), |
| ); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), new_region.va); |
| assert_eq!(0x1000, new_region.length); |
| assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa()); |
| assert!(matches!( |
| new_region.physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x1000, splitted_regions[0].length); |
| assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa()); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va); |
| assert_eq!(0x3000, splitted_regions[1].length); |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| // New region in the middle |
| let (new_region, splitted_regions) = region_unused.create_split( |
| VirtualAddress(0x4000_1000), |
| 0x1000, |
| Some(PhysicalRegion::PhysicalAddress(PhysicalAddress( |
| 0x8000_0000, |
| ))), |
| ); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), new_region.va); |
| assert_eq!(0x1000, new_region.length); |
| assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa()); |
| assert!(matches!( |
| new_region.physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x1000, splitted_regions[0].length); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va); |
| assert_eq!(0x1000, splitted_regions[1].length); |
| assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[1].get_pa()); |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_2000), splitted_regions[2].va); |
| assert_eq!(0x2000, splitted_regions[2].length); |
| assert!(matches!( |
| splitted_regions[2].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| // New region at the end |
| let (new_region, splitted_regions) = region_unused.create_split( |
| VirtualAddress(0x4000_3000), |
| 0x1000, |
| Some(PhysicalRegion::PhysicalAddress(PhysicalAddress( |
| 0x8000_0000, |
| ))), |
| ); |
| |
| assert_eq!(VirtualAddress(0x4000_3000), new_region.va); |
| assert_eq!(0x1000, new_region.length); |
| assert_eq!(PhysicalAddress(0x8000_0000), new_region.get_pa()); |
| assert!(matches!( |
| new_region.physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x3000, splitted_regions[0].length); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_3000), splitted_regions[1].va); |
| assert_eq!(0x1000, splitted_regions[1].length); |
| assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[1].get_pa()); |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| } |
| |
| #[test] |
| fn test_virtual_region_create_split_by_unused() { |
| let region_unused = VirtualRegion::new_with_pa( |
| PhysicalAddress(0x8000_0000), |
| VirtualAddress(0x4000_0000), |
| 0x4000, |
| ); |
| |
| // New region at the start |
| let (new_region, splitted_regions) = |
| region_unused.create_split(VirtualAddress(0x4000_0000), 0x1000, None); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), new_region.va); // TODO: why do we need explicit type here? |
| assert_eq!(0x1000, new_region.length); |
| assert!(matches!(new_region.physical_region, PhysicalRegion::Unused)); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x1000, splitted_regions[0].length); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va); |
| assert_eq!(0x3000, splitted_regions[1].length); |
| assert_eq!(PhysicalAddress(0x8000_1000), splitted_regions[1].get_pa()); |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| // New region in the middle |
| let (new_region, splitted_regions) = |
| region_unused.create_split(VirtualAddress(0x4000_1000), 0x1000, None); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), new_region.va); |
| assert_eq!(0x1000, new_region.length); |
| assert!(matches!(new_region.physical_region, PhysicalRegion::Unused)); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x1000, splitted_regions[0].length); |
| assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa()); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_1000), splitted_regions[1].va); |
| assert_eq!(0x1000, splitted_regions[1].length); |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::Unused |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_2000), splitted_regions[2].va); |
| assert_eq!(0x2000, splitted_regions[2].length); |
| assert_eq!(PhysicalAddress(0x8000_2000), splitted_regions[2].get_pa()); |
| assert!(matches!( |
| splitted_regions[2].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| // New region at the end |
| let (new_region, splitted_regions) = |
| region_unused.create_split(VirtualAddress(0x4000_3000), 0x1000, None); |
| |
| assert_eq!(VirtualAddress(0x4000_3000), new_region.va); |
| assert_eq!(0x1000, new_region.length); |
| assert!(matches!(new_region.physical_region, PhysicalRegion::Unused)); |
| |
| assert_eq!(VirtualAddress(0x4000_0000), splitted_regions[0].va); |
| assert_eq!(0x3000, splitted_regions[0].length); |
| assert_eq!(PhysicalAddress(0x8000_0000), splitted_regions[0].get_pa()); |
| assert!(matches!( |
| splitted_regions[0].physical_region, |
| PhysicalRegion::PhysicalAddress(_) |
| )); |
| |
| assert_eq!(VirtualAddress(0x4000_3000), splitted_regions[1].va); |
| assert_eq!(0x1000, splitted_regions[1].length); |
| |
| assert!(matches!( |
| splitted_regions[1].physical_region, |
| PhysicalRegion::Unused |
| )); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn test_virtual_region_does_not_contain() { |
| let region = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000); |
| region.create_split( |
| VirtualAddress(0x8000_0000), |
| 0x1000, |
| Some(PhysicalRegion::PhysicalAddress(PhysicalAddress( |
| 0xc000_0000, |
| ))), |
| ); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn test_virtual_region_create_split_same_used() { |
| let region = VirtualRegion::new(VirtualAddress(0x4000_0000), 0x1000); |
| region.create_split( |
| VirtualAddress(0x4000_0000), |
| 0x1000, |
| Some(PhysicalRegion::Unused), |
| ); |
| } |
| |
| #[test] |
| fn test_virtual_region_drop() { |
| static PAGE_POOL_AREA: PagePoolArea<8192> = PagePoolArea::new(); |
| let page_pool = PagePool::new::<DummyKernelAddressTranslator, 8192>(&PAGE_POOL_AREA); |
| let page = page_pool.allocate_pages(4096, None).unwrap(); |
| |
| let physical_region = PhysicalRegion::Allocated(page_pool, page); |
| |
| // Testing physical region drop through virtualregion |
| let virtual_region = |
| VirtualRegion::new_from_fields(VirtualAddress(0x4000_0000), 1000, physical_region); |
| drop(virtual_region); |
| } |
| } |