blob: ecaa6b5542fdc2ec9eb49c5a2175140cb75769a4 [file] [log] [blame]
Imre Kis87cee5b2025-01-15 18:52:35 +01001// SPDX-FileCopyrightText: Copyright 2023-2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
Imre Kis703482d2023-11-30 15:51:26 +01004#![allow(dead_code)]
5#![allow(non_camel_case_types)]
6#![cfg_attr(not(test), no_std)]
7
8extern crate alloc;
9
10use core::arch::asm;
11use core::iter::zip;
Imre Kisc1dab892024-03-26 12:03:58 +010012use core::ops::Range;
Imre Kis703482d2023-11-30 15:51:26 +010013use core::{fmt, panic};
14
15use alloc::boxed::Box;
16use alloc::format;
17use alloc::string::{String, ToString};
18use alloc::vec::Vec;
19use log::debug;
20
21use bitflags::bitflags;
22use packed_struct::prelude::*;
23
24use self::descriptor::DescriptorType;
25
26use self::descriptor::{Attributes, DataAccessPermissions, Descriptor, Shareability};
27use self::kernel_space::KernelSpace;
28use self::page_pool::{Page, PagePool, Pages};
29use self::region::{PhysicalRegion, VirtualRegion};
30use self::region_pool::{Region, RegionPool, RegionPoolError};
31
32mod descriptor;
33pub mod kernel_space;
34pub mod page_pool;
35mod region;
36mod region_pool;
37
38/// The first level of memory descriptors table which
39#[repr(C, align(512))]
40pub struct BaseTable {
41 pub descriptors: [Descriptor; 64],
42}
43
44impl BaseTable {
45 pub fn new() -> Self {
46 BaseTable {
Imre Kisf5f6fa72024-04-18 14:04:21 +020047 descriptors: core::array::from_fn(|_| Descriptor::default()),
Imre Kis703482d2023-11-30 15:51:26 +010048 }
49 }
50}
51
52/// Translation table error type
53#[derive(Debug)]
54pub enum XlatError {
55 InvalidParameterError(String),
56 AllocationError(String),
57 AlignmentError(String),
58 Overflow,
59 InvalidOperation(String),
60 Overlap,
61 NotFound,
62 RegionPoolError(RegionPoolError),
63}
64
65/// Memory attributes
66///
67/// MAIR_EL1 should be configured in the same way in startup.s
68#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq, Eq, Default)]
69pub enum MemoryAttributesIndex {
70 #[default]
71 Device_nGnRnE = 0x00,
72 Normal_IWBWA_OWBWA = 0x01,
73}
74
75bitflags! {
76 #[derive(Debug, Clone, Copy)]
77 pub struct MemoryAccessRights : u32 {
78 const R = 0b00000001;
79 const W = 0b00000010;
80 const X = 0b00000100;
81 const NS = 0b00001000;
82
83 const RW = Self::R.bits() | Self::W.bits();
84 const RX = Self::R.bits() | Self::X.bits();
85 const RWX = Self::R.bits() | Self::W.bits() | Self::X.bits();
86
87 const USER = 0b00010000;
88 const DEVICE = 0b00100000;
Imre Kisc1dab892024-03-26 12:03:58 +010089 const GLOBAL = 0b01000000;
Imre Kis703482d2023-11-30 15:51:26 +010090 }
91}
92
93impl From<MemoryAccessRights> for Attributes {
94 fn from(access_rights: MemoryAccessRights) -> Self {
95 let data_access_permissions = match (
96 access_rights.contains(MemoryAccessRights::USER),
97 access_rights.contains(MemoryAccessRights::W),
98 ) {
99 (false, false) => DataAccessPermissions::ReadOnly_None,
100 (false, true) => DataAccessPermissions::ReadWrite_None,
101 (true, false) => DataAccessPermissions::ReadOnly_ReadOnly,
102 (true, true) => DataAccessPermissions::ReadWrite_ReadWrite,
103 };
104
105 let mem_attr_index = if access_rights.contains(MemoryAccessRights::DEVICE) {
106 MemoryAttributesIndex::Device_nGnRnE
107 } else {
108 MemoryAttributesIndex::Normal_IWBWA_OWBWA
109 };
110
111 Attributes {
112 uxn: !access_rights.contains(MemoryAccessRights::X)
113 || !access_rights.contains(MemoryAccessRights::USER),
114 pxn: !access_rights.contains(MemoryAccessRights::X)
115 || access_rights.contains(MemoryAccessRights::USER),
116 contiguous: false,
Imre Kisc1dab892024-03-26 12:03:58 +0100117 not_global: !access_rights.contains(MemoryAccessRights::GLOBAL),
Imre Kis703482d2023-11-30 15:51:26 +0100118 access_flag: true,
119 shareability: Shareability::NonShareable,
120 data_access_permissions,
121 non_secure: access_rights.contains(MemoryAccessRights::NS),
122 mem_attr_index,
123 }
124 }
125}
126
127#[derive(PartialEq)]
128struct Block {
129 pa: usize,
130 va: usize,
131 granule: usize,
132}
133
134impl Block {
135 fn new(pa: usize, va: usize, granule: usize) -> Self {
136 assert!(Xlat::GRANULE_SIZES.contains(&granule));
137 Self { pa, va, granule }
138 }
139}
140
141impl fmt::Debug for Block {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 f.debug_struct("Block")
144 .field("pa", &format_args!("{:#010x}", self.pa))
145 .field("va", &format_args!("{:#010x}", self.va))
146 .field("granule", &format_args!("{:#010x}", self.granule))
147 .finish()
148 }
149}
150
Imre Kisc1dab892024-03-26 12:03:58 +0100151/// Enum for selecting TTBR0_EL1 or TTBR1_EL1
Imre Kis9a7440e2024-04-18 15:44:45 +0200152#[allow(clippy::upper_case_acronyms)]
Imre Kisc1dab892024-03-26 12:03:58 +0100153pub enum TTBR {
154 TTBR0_EL1,
155 TTBR1_EL1,
156}
157
Imre Kis703482d2023-11-30 15:51:26 +0100158pub struct Xlat {
159 base_table: Box<BaseTable>,
160 page_pool: PagePool,
161 regions: RegionPool<VirtualRegion>,
162}
163
164/// Memory translation table handling
165/// # High level interface
166/// * allocate and map zero initialized region (with or without VA)
167/// * allocate and map memory region and load contents (with or without VA)
168/// * map memory region by PA (with or without VA)
169/// * unmap memory region by PA
170/// * query PA by VA
171/// * set access rights of mapped memory areas
172/// * active mapping
173///
174/// # Debug features
175/// * print translation table details
176///
177/// # Region level interface
178/// * map regions
179/// * unmap region
180/// * find a mapped region which contains
181/// * find empty area for region
182/// * set access rights for a region
183/// * create blocks by region
184///
185/// # Block level interface
186/// * map block
187/// * unmap block
188/// * set access rights of block
189impl Xlat {
Imre Kis703482d2023-11-30 15:51:26 +0100190 pub const GRANULE_SIZES: [usize; 4] = [0, 0x4000_0000, 0x0020_0000, 0x0000_1000];
191
Imre Kisc1dab892024-03-26 12:03:58 +0100192 pub fn new(page_pool: PagePool, va_range: Range<usize>) -> Self {
Imre Kis703482d2023-11-30 15:51:26 +0100193 let mut regions = RegionPool::new();
194 regions
Imre Kisc1dab892024-03-26 12:03:58 +0100195 .add(VirtualRegion::new(va_range.start, va_range.len()))
Imre Kis703482d2023-11-30 15:51:26 +0100196 .unwrap();
197 Self {
198 base_table: Box::new(BaseTable::new()),
199 page_pool,
200 regions,
201 }
202 }
203
204 /// Allocate memory pages from the page pool, maps it to the given VA and fills it with the
205 /// initial data
206 /// # Arguments
207 /// * va: Virtual address of the memory area
208 /// * data: Data to be loaded to the memory area
209 /// * access_rights: Memory access rights of the area
210 /// # Return value
211 /// * Virtual address of the mapped memory
212 pub fn allocate_initalized_range(
213 &mut self,
214 va: Option<usize>,
215 data: &[u8],
216 access_rights: MemoryAccessRights,
217 ) -> Result<usize, XlatError> {
218 let mut pages = self.page_pool.allocate_pages(data.len()).map_err(|e| {
219 XlatError::AllocationError(format!(
220 "Cannot allocate pages for {} bytes ({:?})",
221 data.len(),
222 e
223 ))
224 })?;
225
226 pages.copy_data_to_page(data);
227
228 let pages_length = pages.length();
229 let physical_region = PhysicalRegion::Allocated(self.page_pool.clone(), pages);
230 let region = if let Some(required_va) = va {
231 self.regions
232 .acquire(required_va, pages_length, physical_region)
233 } else {
234 self.regions.allocate(pages_length, physical_region)
235 }
236 .map_err(XlatError::RegionPoolError)?;
237
238 self.map_region(region, access_rights.into())
239 }
240
241 /// Allocate memory pages from the page pool, maps it to the given VA and fills it with zeros
242 /// # Arguments
243 /// * va: Virtual address of the memory area
244 /// * length: Length of the memory area in bytes
245 /// * access_rights: Memory access rights of the area
246 /// # Return value
247 /// * Virtual address of the mapped memory
248 pub fn allocate_zero_init_range(
249 &mut self,
250 va: Option<usize>,
251 length: usize,
252 access_rights: MemoryAccessRights,
253 ) -> Result<usize, XlatError> {
254 let mut pages = self.page_pool.allocate_pages(length).map_err(|e| {
255 XlatError::AllocationError(format!("Cannot allocate pages for {length} bytes ({e:?})"))
256 })?;
257
258 pages.zero_init();
259
260 let pages_length = pages.length();
261 let physical_region = PhysicalRegion::Allocated(self.page_pool.clone(), pages);
262 let region = if let Some(required_va) = va {
263 self.regions
264 .acquire(required_va, pages_length, physical_region)
265 } else {
266 self.regions.allocate(pages_length, physical_region)
267 }
268 .map_err(XlatError::RegionPoolError)?;
269
270 self.map_region(region, access_rights.into())
271 }
272
273 /// Map memory area by physical address
274 /// # Arguments
275 /// * va: Virtual address of the memory area
276 /// * pa: Physical address of the memory area
277 /// * length: Length of the memory area in bytes
278 /// * access_rights: Memory access rights of the area
279 /// # Return value
280 /// * Virtual address of the mapped memory
281 pub fn map_physical_address_range(
282 &mut self,
283 va: Option<usize>,
284 pa: usize,
285 length: usize,
286 access_rights: MemoryAccessRights,
287 ) -> Result<usize, XlatError> {
288 let resource = PhysicalRegion::PhysicalAddress(pa);
289 let region = if let Some(required_va) = va {
290 self.regions.acquire(required_va, length, resource)
291 } else {
292 self.regions.allocate(length, resource)
293 }
294 .map_err(XlatError::RegionPoolError)?;
295
296 self.map_region(region, access_rights.into())
297 }
298
299 /// Unmap memory area by virtual address
300 /// # Arguments
301 /// * va: Virtual address
302 /// * length: Length of the memory area in bytes
303 pub fn unmap_virtual_address_range(
304 &mut self,
305 va: usize,
306 length: usize,
307 ) -> Result<(), XlatError> {
308 let pa = self.get_pa_by_va(va, length)?;
309
310 let region_to_release = VirtualRegion::new_with_pa(pa, va, length);
311
312 self.unmap_region(&region_to_release)?;
313
314 self.regions
315 .release(region_to_release)
316 .map_err(XlatError::RegionPoolError)
317 }
318
319 /// Query physical address by virtual address range. Only returns a value if the memory area
320 /// mapped as continuous area.
321 /// # Arguments
322 /// * va: Virtual address of the memory area
323 /// * length: Length of the memory area in bytes
324 /// # Return value
325 /// * Physical address of the mapped memory
326 pub fn get_pa_by_va(&self, va: usize, length: usize) -> Result<usize, XlatError> {
327 let containing_region = self
328 .find_containing_region(va, length)
329 .ok_or(XlatError::NotFound)?;
330
331 if !containing_region.used() {
332 return Err(XlatError::NotFound);
333 }
334
335 Ok(containing_region.get_pa_for_va(va))
336 }
337
338 /// Sets the memory access right of memory area
339 /// # Arguments
340 /// * va: Virtual address of the memory area
341 /// * length: Length of the memory area in bytes
342 /// * access_rights: New memory access rights of the area
343 pub fn set_access_rights(
344 &mut self,
345 va: usize,
346 length: usize,
347 access_rights: MemoryAccessRights,
348 ) -> Result<(), XlatError> {
349 let containing_region = self
350 .find_containing_region(va, length)
351 .ok_or(XlatError::NotFound)?;
352
353 if !containing_region.used() {
354 return Err(XlatError::NotFound);
355 }
356
357 let region = VirtualRegion::new_with_pa(containing_region.get_pa_for_va(va), va, length);
358 self.map_region(region, access_rights.into())?;
359
360 Ok(())
361 }
362
363 /// Activate memory mapping represented by the object
364 /// # Arguments
365 /// * asid: ASID of the table base address
Imre Kisc1dab892024-03-26 12:03:58 +0100366 /// * ttbr: Selects TTBR0_EL1/TTBR1_EL1
367 pub fn activate(&self, asid: u8, ttbr: TTBR) {
Imre Kis703482d2023-11-30 15:51:26 +0100368 let base_table_pa = KernelSpace::kernel_to_pa(self.base_table.descriptors.as_ptr() as u64);
Imre Kisc1dab892024-03-26 12:03:58 +0100369 let ttbr_value = ((asid as u64) << 48) | base_table_pa;
370 #[cfg(target_arch = "aarch64")]
371 match ttbr {
372 TTBR::TTBR0_EL1 => unsafe {
373 asm!(
374 "msr ttbr0_el1, {0}
375 isb",
376 in(reg) ttbr_value)
377 },
378 TTBR::TTBR1_EL1 => unsafe {
379 asm!(
380 "msr ttbr1_el1, {0}
381 isb
382
383 tlbi vmalle1
384 dsb sy
385 isb",
386 in(reg) ttbr_value)
387 },
388 }
Imre Kis703482d2023-11-30 15:51:26 +0100389 }
390
391 /// Prints the translation tables to debug console recursively
392 pub fn print(&self) {
393 debug!(
394 "Xlat table -> {:#010x}",
395 self.base_table.descriptors.as_ptr() as u64
396 );
397 Self::print_table(1, 0, &self.base_table.descriptors);
398 }
399
400 /// Prints a single translation table to the debug console
401 /// # Arguments
402 /// * level: Level of the translation table
403 /// * va: Base virtual address of the table
404 /// * table: Table entries
405 pub fn print_table(level: usize, va: usize, table: &[Descriptor]) {
406 let level_prefix = match level {
407 0 | 1 => "|-",
408 2 => "| |-",
409 _ => "| | |-",
410 };
411
412 for (descriptor, va) in zip(table, (va..).step_by(Self::GRANULE_SIZES[level])) {
413 match descriptor.get_descriptor_type(level) {
414 DescriptorType::Block => debug!(
415 "{} {:#010x} Block -> {:#010x}",
416 level_prefix,
417 va,
418 descriptor.get_block_output_address(level)
419 ),
420 DescriptorType::Table => {
421 let next_level_table = unsafe { descriptor.get_next_level_table(level) };
422 debug!(
423 "{} {:#010x} Table -> {:#010x}",
424 level_prefix,
425 va,
426 next_level_table.as_ptr() as usize
427 );
428 Self::print_table(level + 1, va, next_level_table);
429 }
430 _ => {}
431 }
432 }
433 }
434
435 /// Adds memory region from the translation table. The function splits the region to blocks and
436 /// uses the block level functions to do the mapping.
437 /// # Arguments
438 /// * region: Memory region object
439 /// # Return value
440 /// * Virtual address of the mapped memory
441 fn map_region(
442 &mut self,
443 region: VirtualRegion,
444 attributes: Attributes,
445 ) -> Result<usize, XlatError> {
446 let blocks = Self::split_region_to_blocks(region.get_pa(), region.base(), region.length())?;
447 for block in blocks {
448 self.map_block(block, attributes.clone());
449 }
450
451 Ok(region.base())
452 }
453
454 /// Remove memory region from the translation table. The function splits the region to blocks
455 /// and uses the block level functions to do the unmapping.
456 /// # Arguments
457 /// * region: Memory region object
458 fn unmap_region(&mut self, region: &VirtualRegion) -> Result<(), XlatError> {
459 let blocks = Self::split_region_to_blocks(region.get_pa(), region.base(), region.length())?;
460 for block in blocks {
461 self.unmap_block(block);
462 }
463
464 Ok(())
465 }
466
467 /// Find mapped region that contains the whole region
468 /// # Arguments
469 /// * region: Virtual address to look for
470 /// # Return value
471 /// * Reference to virtual region if found
472 fn find_containing_region(&self, va: usize, length: usize) -> Option<&VirtualRegion> {
473 self.regions.find_containing_region(va, length).ok()
474 }
475
476 /// Splits memory region to blocks that matches the granule size of the translation table.
477 /// # Arguments
478 /// * pa: Physical address
479 /// * va: Virtual address
480 /// * length: Region size in bytes
481 /// # Return value
482 /// * Vector of granule sized blocks
483 fn split_region_to_blocks(
484 mut pa: usize,
485 mut va: usize,
486 mut length: usize,
487 ) -> Result<Vec<Block>, XlatError> {
488 let min_granule_mask = Self::GRANULE_SIZES.last().unwrap() - 1;
489
490 if length == 0 {
491 return Err(XlatError::InvalidParameterError(
492 "Length cannot be 0".to_string(),
493 ));
494 }
495
496 if pa & min_granule_mask != 0
497 || va & min_granule_mask != 0
498 || length & min_granule_mask != 0
499 {
500 return Err(XlatError::InvalidParameterError(format!(
501 "Addresses and length must be aligned {pa:#010x} {va:#010x} {length:#x}"
502 )));
503 }
504
505 let mut pages = Vec::new();
506
507 while length > 0 {
508 for granule in &Self::GRANULE_SIZES {
509 if (pa | va) & (*granule - 1) == 0 && length >= *granule {
510 pages.push(Block::new(pa, va, *granule));
511 pa += *granule;
512 va = va.checked_add(*granule).ok_or(XlatError::Overflow)?;
513
514 length -= *granule;
515 break;
516 }
517 }
518 }
519
520 Ok(pages)
521 }
522
523 /// Add block to memory mapping
524 /// # Arguments
525 /// * block: Memory block that can be represented by a single translation table entry
526 /// * attributes: Memory block's permissions, flags
527 fn map_block(&mut self, block: Block, attributes: Attributes) {
528 Self::set_block_descriptor_recursively(
529 attributes,
530 block.pa,
531 block.va,
532 block.granule,
533 1,
534 self.base_table.descriptors.as_mut_slice(),
535 &self.page_pool,
536 );
537 }
538
539 /// Adds the block descriptor to the translation table along all the intermediate tables the
540 /// reach the required granule.
541 /// # Arguments
542 /// * attributes: Memory block's permssions, flags
543 /// * pa: Physical address
544 /// * va: Virtual address
545 /// * granule: Translation granule in bytes
546 /// * level: Translation table level
547 /// * table: Translation table on the given level
548 /// * page_pool: Page pool where the function can allocate pages for the translation tables
549 fn set_block_descriptor_recursively(
550 attributes: Attributes,
551 pa: usize,
552 va: usize,
553 granule: usize,
554 level: usize,
555 table: &mut [Descriptor],
556 page_pool: &PagePool,
557 ) {
558 // Get descriptor of the current level
559 let descriptor = &mut table[va / Self::GRANULE_SIZES[level]];
560
561 // We reached the required granule level
562 if Self::GRANULE_SIZES[level] == granule {
563 descriptor.set_block_descriptor(level, pa, attributes);
564 return;
565 }
566
567 // Need to iterate forward
568 match descriptor.get_descriptor_type(level) {
569 DescriptorType::Invalid => {
570 let mut page = page_pool.allocate_pages(Page::SIZE).unwrap();
571 unsafe {
572 let next_table = page.get_as_slice();
573 descriptor.set_table_descriptor(level, next_table, None);
574 }
575 Self::set_block_descriptor_recursively(
576 attributes,
577 pa,
578 va & (Self::GRANULE_SIZES[level] - 1),
579 granule,
580 level + 1,
581 unsafe { descriptor.get_next_level_table_mut(level) },
582 page_pool,
583 )
584 }
585 DescriptorType::Block => {
586 // Saving current descriptor details
587 let current_va = va & !(Self::GRANULE_SIZES[level] - 1);
588 let current_pa = descriptor.get_block_output_address(level);
589 let current_attributes = descriptor.get_block_attributes(level);
590
591 // Replace block descriptor by table descriptor
592 let mut page = page_pool.allocate_pages(Page::SIZE).unwrap();
593 unsafe {
594 let next_table = page.get_as_slice();
595 descriptor.set_table_descriptor(level, next_table, None);
596 }
597
598 // Explode block descriptor to table entries
599 for exploded_va in (current_va..(current_va + Self::GRANULE_SIZES[level]))
600 .step_by(Self::GRANULE_SIZES[level + 1])
601 {
602 let offset = exploded_va - current_va;
603 Self::set_block_descriptor_recursively(
604 current_attributes.clone(),
605 current_pa + offset,
606 exploded_va & (Self::GRANULE_SIZES[level] - 1),
607 Self::GRANULE_SIZES[level + 1],
608 level + 1,
609 unsafe { descriptor.get_next_level_table_mut(level) },
610 page_pool,
611 )
612 }
613
614 // Invoke self to continue recursion on the newly created level
615 Self::set_block_descriptor_recursively(
616 attributes, pa, va, granule, level, table, page_pool,
617 );
618 }
619 DescriptorType::Table => Self::set_block_descriptor_recursively(
620 attributes,
621 pa,
622 va & (Self::GRANULE_SIZES[level] - 1),
623 granule,
624 level + 1,
625 unsafe { descriptor.get_next_level_table_mut(level) },
626 page_pool,
627 ),
628 }
629 }
630
631 /// Remove block from memory mapping
632 /// # Arguments
633 /// * block: memory block that can be represented by a single translation entry
634 fn unmap_block(&mut self, block: Block) {
635 Self::remove_block_descriptor_recursively(
636 block.va,
637 block.granule,
638 1,
639 self.base_table.descriptors.as_mut_slice(),
640 &self.page_pool,
641 );
642 }
643
644 /// Removes block descriptor from the translation table along all the intermediate tables which
645 /// become empty during the removal process.
646 /// # Arguments
647 /// * va: Virtual address
648 /// * granule: Translation granule in bytes
649 /// * level: Translation table level
650 /// * table: Translation table on the given level
651 /// * page_pool: Page pool where the function can release the pages of empty tables
652 fn remove_block_descriptor_recursively(
653 va: usize,
654 granule: usize,
655 level: usize,
656 table: &mut [Descriptor],
657 page_pool: &PagePool,
658 ) {
659 // Get descriptor of the current level
660 let descriptor = &mut table[va / Self::GRANULE_SIZES[level]];
661
662 // We reached the required granule level
663 if Self::GRANULE_SIZES[level] == granule {
664 descriptor.set_block_descriptor_to_invalid(level);
665 return;
666 }
667
668 // Need to iterate forward
669 match descriptor.get_descriptor_type(level) {
670 DescriptorType::Invalid => {
671 panic!("Cannot remove block from non-existing table");
672 }
673 DescriptorType::Block => {
674 panic!("Cannot remove block with different granule");
675 }
676 DescriptorType::Table => {
677 let next_level_table = unsafe { descriptor.get_next_level_table_mut(level) };
678 Self::remove_block_descriptor_recursively(
679 va & (Self::GRANULE_SIZES[level] - 1),
680 granule,
681 level + 1,
682 next_level_table,
683 page_pool,
684 );
685
686 if next_level_table.iter().all(|d| !d.is_valid()) {
687 // Empty table
688 let mut page = unsafe {
689 Pages::from_slice(descriptor.set_table_descriptor_to_invalid(level))
690 };
691 page.zero_init();
692 page_pool.release_pages(page).unwrap();
693 }
694 }
695 }
696 }
697
698 fn get_descriptor(&mut self, va: usize, granule: usize) -> &mut Descriptor {
699 Self::walk_descriptors(va, granule, 0, &mut self.base_table.descriptors)
700 }
701
702 fn walk_descriptors(
703 va: usize,
704 granule: usize,
705 level: usize,
706 table: &mut [Descriptor],
707 ) -> &mut Descriptor {
708 // Get descriptor of the current level
709 let descriptor = &mut table[va / Self::GRANULE_SIZES[level]];
710
711 if Self::GRANULE_SIZES[level] == granule {
712 return descriptor;
713 }
714
715 // Need to iterate forward
716 match descriptor.get_descriptor_type(level) {
717 DescriptorType::Invalid => {
718 panic!("Invalid descriptor");
719 }
720 DescriptorType::Block => {
721 panic!("Cannot split existing block descriptor to table");
722 }
723 DescriptorType::Table => Self::walk_descriptors(
724 va & (Self::GRANULE_SIZES[level] - 1),
725 granule,
726 level + 1,
727 unsafe { descriptor.get_next_level_table_mut(level) },
728 ),
729 }
730 }
731}
732
733#[test]
734fn test_split_to_pages() {
735 let pages = Xlat::split_region_to_blocks(0x3fff_c000, 0x3fff_c000, 0x4020_5000).unwrap();
736 assert_eq!(Block::new(0x3fff_c000, 0x3fff_c000, 0x1000), pages[0]);
737 assert_eq!(Block::new(0x3fff_d000, 0x3fff_d000, 0x1000), pages[1]);
738 assert_eq!(Block::new(0x3fff_e000, 0x3fff_e000, 0x1000), pages[2]);
739 assert_eq!(Block::new(0x3fff_f000, 0x3fff_f000, 0x1000), pages[3]);
740 assert_eq!(Block::new(0x4000_0000, 0x4000_0000, 0x4000_0000), pages[4]);
741 assert_eq!(Block::new(0x8000_0000, 0x8000_0000, 0x0020_0000), pages[5]);
742 assert_eq!(Block::new(0x8020_0000, 0x8020_0000, 0x1000), pages[6]);
743}
744
745#[test]
746fn test_split_to_pages_unaligned() {
747 let pages = Xlat::split_region_to_blocks(0x3fff_c000, 0x3f20_0000, 0x200000).unwrap();
748 for (i, block) in pages.iter().enumerate().take(512) {
749 assert_eq!(
750 Block::new(0x3fff_c000 + (i << 12), 0x3f20_0000 + (i << 12), 0x1000),
751 *block
752 );
753 }
754}