blob: caa8b97870593894bca0a4bb116a791571592755 [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
#[derive(Clone, Copy, Debug)]
pub enum TranslationGranule<const VA_BITS: usize> {
Granule4k = 4 * 1024,
Granule16k = 16 * 1024,
Granule64k = 64 * 1024,
}
impl<const VA_BITS: usize> TranslationGranule<VA_BITS> {
pub const fn initial_lookup_level(&self) -> isize {
match self {
TranslationGranule::Granule4k => match VA_BITS {
#[cfg(feature = "feat_ttst")]
16..=21 => 3,
#[cfg(feature = "feat_ttst")]
22..=24 => 2,
25..=30 => 2,
31..=39 => 1,
40..=48 => 0,
#[cfg(feature = "feat_lpa2")]
49..=52 => -1,
_ => panic!("Invalid VA_BITS"),
},
TranslationGranule::Granule16k => match VA_BITS {
#[cfg(feature = "feat_ttst")]
16..=24 => 3,
25 => 3,
26..=36 => 2,
37..=47 => 1,
48 => 0,
#[cfg(feature = "feat_lpa2")]
49..=52 => 0,
_ => panic!("Invalid VA_BITS"),
},
TranslationGranule::Granule64k => match VA_BITS {
#[cfg(feature = "feat_ttst")]
17..=24 => 3,
25..=29 => 3,
30..=42 => 2,
43..=48 => 1,
#[cfg(feature = "feat_lva")]
49..=52 => 1,
_ => panic!("Invalid VA_BITS"),
},
}
}
pub const fn entry_count_at_level(&self, level: isize) -> usize {
let total_bits_at_level = self.total_bits_at_level(level);
let bits_per_level = match self {
TranslationGranule::Granule4k => 9,
TranslationGranule::Granule16k => 11,
TranslationGranule::Granule64k => 13,
};
let bits = if total_bits_at_level + bits_per_level < VA_BITS {
total_bits_at_level + bits_per_level
} else {
VA_BITS
} - total_bits_at_level;
1 << bits
}
pub const fn table_size<D>(&self, level: isize) -> usize {
self.entry_count_at_level(level) * core::mem::size_of::<D>()
}
pub const fn table_alignment<D>(&self, level: isize) -> usize {
// D8.2.5.2
// For the VMSAv8-64 translation system, if the translation table has fewer than eight
// entries and an OA size greater than 48 bits is used, then the table is aligned to 64
// bytes. Otherwise, the translation table is aligned to the size of that translation table.
let alignment = self.table_size::<D>(level);
if VA_BITS > 48 && alignment < 64 {
64
} else {
alignment
}
}
pub const fn block_size_at_level(&self, level: isize) -> usize {
1 << self.total_bits_at_level(level)
}
pub const fn total_bits_at_level(&self, level: isize) -> usize {
assert!(self.initial_lookup_level() <= level && level <= 3);
match self {
TranslationGranule::Granule4k => match level {
-1 => 48,
0 => 39,
1 => 30,
2 => 21,
3 => 12,
_ => panic!("Invalid translation level"),
},
TranslationGranule::Granule16k => match level {
0 => 47,
1 => 36,
2 => 25,
3 => 14,
_ => panic!("Invalid translation level"),
},
TranslationGranule::Granule64k => match level {
1 => 42,
2 => 29,
3 => 16,
_ => panic!("Invalid translation level"),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! validate_initial_lookup_level {
( $granule:expr, $tnsz_min:literal, $tnsz_max:literal, $level:literal ) => {
assert_eq!(
$level,
TranslationGranule::<{ 64 - $tnsz_min }>::initial_lookup_level(&$granule)
);
assert_eq!(
$level,
TranslationGranule::<{ 64 - $tnsz_max }>::initial_lookup_level(&$granule)
);
};
}
#[test]
fn test_initial_lookup_level() {
// Table D8-16 4KB granule, determining stage 1 initial lookup level
#[cfg(feature = "feat_lpa2")]
validate_initial_lookup_level!(TranslationGranule::Granule4k, 12, 15, -1);
validate_initial_lookup_level!(TranslationGranule::Granule4k, 16, 24, 0);
validate_initial_lookup_level!(TranslationGranule::Granule4k, 25, 33, 1);
validate_initial_lookup_level!(TranslationGranule::Granule4k, 34, 39, 2);
#[cfg(feature = "feat_ttst")]
validate_initial_lookup_level!(TranslationGranule::Granule4k, 40, 42, 2);
#[cfg(feature = "feat_ttst")]
validate_initial_lookup_level!(TranslationGranule::Granule4k, 43, 48, 3);
//// Table D8-26 16KB granule, determining stage 1 initial lookup level
#[cfg(feature = "feat_lpa2")]
validate_initial_lookup_level!(TranslationGranule::Granule16k, 12, 15, 0);
validate_initial_lookup_level!(TranslationGranule::Granule16k, 16, 16, 0);
validate_initial_lookup_level!(TranslationGranule::Granule16k, 17, 27, 1);
validate_initial_lookup_level!(TranslationGranule::Granule16k, 28, 38, 2);
validate_initial_lookup_level!(TranslationGranule::Granule16k, 39, 39, 3);
#[cfg(feature = "feat_ttst")]
validate_initial_lookup_level!(TranslationGranule::Granule16k, 40, 48, 3);
//
//// D8.2.10.1 VMSAv8-64 Stage 1 address translation using the 64KB translation granule
#[cfg(feature = "feat_lva")]
validate_initial_lookup_level!(TranslationGranule::Granule64k, 12, 15, 1);
validate_initial_lookup_level!(TranslationGranule::Granule64k, 16, 21, 1);
validate_initial_lookup_level!(TranslationGranule::Granule64k, 22, 34, 2);
validate_initial_lookup_level!(TranslationGranule::Granule64k, 35, 39, 3);
#[cfg(feature = "feat_ttst")]
validate_initial_lookup_level!(TranslationGranule::Granule64k, 40, 47, 3);
}
#[test]
fn test_entry_count_at_level() {
// Table D8-14 4KB granule translation table properties at each lookup level
#[cfg(feature = "feat_lpa2")]
{
let granule = TranslationGranule::<52>::Granule4k;
assert_eq!(16, granule.entry_count_at_level(-1));
assert_eq!(512, granule.entry_count_at_level(0));
assert_eq!(512, granule.entry_count_at_level(1));
assert_eq!(512, granule.entry_count_at_level(2));
assert_eq!(512, granule.entry_count_at_level(3));
}
#[cfg(not(feature = "feat_lpa2"))]
{
let granule = TranslationGranule::<48>::Granule4k;
assert_eq!(512, granule.entry_count_at_level(0));
assert_eq!(512, granule.entry_count_at_level(1));
assert_eq!(512, granule.entry_count_at_level(2));
assert_eq!(512, granule.entry_count_at_level(3));
}
let granule = TranslationGranule::<42>::Granule4k;
assert_eq!(8, granule.entry_count_at_level(0));
assert_eq!(512, granule.entry_count_at_level(1));
assert_eq!(512, granule.entry_count_at_level(2));
assert_eq!(512, granule.entry_count_at_level(3));
let granule = TranslationGranule::<32>::Granule4k;
assert_eq!(4, granule.entry_count_at_level(1));
assert_eq!(512, granule.entry_count_at_level(2));
assert_eq!(512, granule.entry_count_at_level(3));
let granule = TranslationGranule::<26>::Granule4k;
assert_eq!(32, granule.entry_count_at_level(2));
assert_eq!(512, granule.entry_count_at_level(3));
// Table D8-24 16KB granule translation table properties at each lookup level
#[cfg(feature = "feat_lpa2")]
{
let granule = TranslationGranule::<52>::Granule16k;
assert_eq!(32, granule.entry_count_at_level(0));
assert_eq!(2048, granule.entry_count_at_level(1));
assert_eq!(2048, granule.entry_count_at_level(2));
assert_eq!(2048, granule.entry_count_at_level(3));
}
#[cfg(not(feature = "feat_lpa2"))]
{
let granule = TranslationGranule::<48>::Granule16k;
assert_eq!(2, granule.entry_count_at_level(0));
assert_eq!(2048, granule.entry_count_at_level(1));
assert_eq!(2048, granule.entry_count_at_level(2));
assert_eq!(2048, granule.entry_count_at_level(3));
}
let granule = TranslationGranule::<40>::Granule16k;
assert_eq!(16, granule.entry_count_at_level(1));
assert_eq!(2048, granule.entry_count_at_level(2));
assert_eq!(2048, granule.entry_count_at_level(3));
let granule = TranslationGranule::<30>::Granule16k;
assert_eq!(32, granule.entry_count_at_level(2));
assert_eq!(2048, granule.entry_count_at_level(3));
// Table D8-33 64KB granule translation table properties at each lookup level
#[cfg(feature = "feat_lva")]
{
let granule = TranslationGranule::<52>::Granule64k;
assert_eq!(1024, granule.entry_count_at_level(1));
}
let granule = TranslationGranule::<48>::Granule64k;
#[cfg(not(feature = "feat_lva"))]
{
assert_eq!(64, granule.entry_count_at_level(1));
}
assert_eq!(8192, granule.entry_count_at_level(2));
assert_eq!(8192, granule.entry_count_at_level(3));
let granule = TranslationGranule::<40>::Granule64k;
assert_eq!(2048, granule.entry_count_at_level(2));
assert_eq!(8192, granule.entry_count_at_level(3));
}
#[test]
fn test_granule_size_at_level() {
// Table D8-15 4KB granule block descriptor properties at each lookup level
#[cfg(feature = "feat_lpa2")]
{
let granule = TranslationGranule::<52>::Granule4k;
assert_eq!(256 << 40, granule.block_size_at_level(-1));
}
let granule = TranslationGranule::<48>::Granule4k;
assert_eq!(512 << 30, granule.block_size_at_level(0));
assert_eq!(1 << 30, granule.block_size_at_level(1));
assert_eq!(2 << 20, granule.block_size_at_level(2));
assert_eq!(4 << 10, granule.block_size_at_level(3));
// Table D8-25 16KB granule block descriptor properties at each lookup level
let granule = TranslationGranule::<48>::Granule16k;
assert_eq!(128 << 40, granule.block_size_at_level(0));
assert_eq!(64 << 30, granule.block_size_at_level(1));
assert_eq!(32 << 20, granule.block_size_at_level(2));
assert_eq!(16 << 10, granule.block_size_at_level(3));
// Table D8-34 64KB granule block descriptor properties at each lookup level
let granule = TranslationGranule::<48>::Granule64k;
assert_eq!(4 << 40, granule.block_size_at_level(1));
assert_eq!(512 << 20, granule.block_size_at_level(2));
assert_eq!(64 << 10, granule.block_size_at_level(3));
}
}