blob: c3ed68f49e4dcab8b38a75fc2e1b8171eee168a7 [file] [log] [blame]
// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Memory descriptor
use packed_struct::prelude::*;
use core::cell::UnsafeCell;
use core::ptr;
use crate::MemoryAttributesIndex;
use super::address::PhysicalAddress;
use super::TranslationGranule;
/// Memory shareability
#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Shareability {
#[default]
NonShareable = 0b00,
Outer = 0b10,
Inner = 0b11,
}
/// Data access permission
#[allow(non_camel_case_types)]
#[derive(PrimitiveEnum_u8, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum DataAccessPermissions {
#[default]
ReadWrite_None = 0b00,
ReadWrite_ReadWrite = 0b01,
ReadOnly_None = 0b10,
ReadOnly_ReadOnly = 0b11,
}
/// Memory attributes
#[derive(PackedStruct, Clone, Debug, PartialEq, Eq, Default)]
#[packed_struct(size_bytes = "8", bit_numbering = "lsb0")]
pub struct Attributes {
#[packed_field(bits = "54")]
pub uxn: bool,
#[packed_field(bits = "53")]
pub pxn: bool,
#[packed_field(bits = "52")]
pub contiguous: bool,
#[packed_field(bits = "11")]
pub not_global: bool,
#[packed_field(bits = "10")]
pub access_flag: bool,
#[packed_field(bits = "9..=8", ty = "enum")]
pub shareability: Shareability,
#[packed_field(bits = "7..=6", ty = "enum")]
pub data_access_permissions: DataAccessPermissions,
#[packed_field(bits = "5")]
pub non_secure: bool,
#[packed_field(bits = "4..=2", ty = "enum")]
pub mem_attr_index: MemoryAttributesIndex,
}
impl From<Attributes> for u64 {
fn from(attributes: Attributes) -> Self {
u64::from_be_bytes(attributes.pack().unwrap())
}
}
impl From<u64> for Attributes {
fn from(bits: u64) -> Self {
Self::unpack(&bits.to_be_bytes()).unwrap()
}
}
/// Next level attributes
#[derive(PackedStruct, Clone, Debug, PartialEq, Eq, Default)]
#[packed_struct(size_bytes = "8", bit_numbering = "lsb0")]
pub struct NextLevelAttributes {
#[packed_field(bits = "63")]
ns_table: bool,
#[packed_field(bits = "62..=61")]
ap_table: Integer<u8, packed_bits::Bits<2>>,
#[packed_field(bits = "60")]
xn_table: bool,
#[packed_field(bits = "59")]
pxn_table: bool,
}
impl From<NextLevelAttributes> for u64 {
fn from(attributes: NextLevelAttributes) -> Self {
u64::from_be_bytes(attributes.pack().unwrap())
}
}
impl From<u64> for NextLevelAttributes {
fn from(bits: u64) -> Self {
Self::unpack(&bits.to_be_bytes()).unwrap()
}
}
/// Memory descriptor type
#[derive(PartialEq, Eq, Debug)]
pub enum DescriptorType {
Invalid,
Block,
Table,
}
/// Memory descriptor of a memory translation table
#[repr(C)]
#[derive(Default)]
pub struct Descriptor {
cell: UnsafeCell<u64>,
}
impl Descriptor {
const ATTR_MASK: u64 = 0xfff8_0000_0000_0ffc;
const DESCRIPTOR_TYPE_MASK: u64 = 0b11;
const INVALID_DESCRIPTOR_VALUE: u64 = 0x0;
const NEXT_ATTR_MASK: u64 = 0xf800_0000_0000_0000;
const TABLE_BIT: u64 = 0b10;
const TA_MASK: u64 = 0x0000_ffff_ffff_f000;
const VALID_BIT: u64 = 0b01;
/// Query descriptor type
pub fn get_descriptor_type(&self, level: isize) -> DescriptorType {
assert!(level <= 3);
let desc_type_bits = self.get() & Self::DESCRIPTOR_TYPE_MASK;
if desc_type_bits & Self::VALID_BIT != 0 {
if level == 3 {
assert_eq!(Self::TABLE_BIT, desc_type_bits & Self::TABLE_BIT);
DescriptorType::Block
} else if desc_type_bits & Self::TABLE_BIT != 0 {
DescriptorType::Table
} else {
DescriptorType::Block
}
} else {
DescriptorType::Invalid
}
}
// Invalid descriptor functions
/// Check if it is a valid descriptor
pub fn is_valid(&self) -> bool {
self.get() & Self::VALID_BIT != 0
}
// Block descriptor functions
/// Set block descriptor
pub fn set_block_descriptor<const VA_BITS: usize>(
&mut self,
granule: TranslationGranule<VA_BITS>,
level: isize,
output_address: PhysicalAddress,
attributes: Attributes,
) {
let attr: u64 = attributes.into();
assert!(level <= 3);
assert!(self.get_descriptor_type(level) != DescriptorType::Table);
assert_eq!(0, attr & !Self::ATTR_MASK);
let oa_granule_mask = !(granule.block_size_at_level(level) - 1);
// Figure D8-14 VMSAv8-64 Block descriptor formats
let oa_bits = match granule {
// 4KB and 16KB granules, 52-bit OA
#[cfg(feature = "feat_lpa2")]
TranslationGranule::Granule4k | TranslationGranule::Granule16k => {
let oa_mask = oa_granule_mask & 0x000f_ffff_ffff_f000;
assert_eq!(0, output_address.0 & !oa_mask);
let address = output_address.0 & oa_mask;
// OA[49:n] remains in place, OA[51:50] is mapped to [9:8]
let lsbs = address & 0x0003_ffff_ffff_f000;
let msbs = ((address >> 50) & 0x3) << 8;
lsbs | msbs
}
// 64KB granule, 52-bit OA
#[cfg(feature = "feat_lpa")]
TranslationGranule::Granule64k => {
let oa_mask = oa_granule_mask & 0x000f_ffff_ffff_0000;
assert_eq!(0, output_address.0 & !oa_mask);
let address = output_address.0 & oa_mask;
// OA[47:n] remains in place, OA[51:48] is mapped to [15:12]
let lsbs = address & 0x0000_ffff_ffff_0000;
let msbs = ((address >> 48) & 0xf) << 12;
lsbs | msbs
}
// 4KB, 16KB, and 64KB granules, 48-bit OA
#[cfg(not(all(feature = "feat_lpa", feature = "feat_lpa2")))]
_ => {
let oa_mask = oa_granule_mask & 0x0000_ffff_ffff_f000;
assert_eq!(0, output_address.0 & !oa_mask);
output_address.0 & oa_mask
}
};
let table_bit = if level < 3 { 0 } else { Self::TABLE_BIT };
self.set(Self::VALID_BIT | table_bit | oa_bits as u64 | attr);
}
/// Get output address from the block descriptor
pub fn get_block_output_address<const VA_BITS: usize>(
&self,
granule: TranslationGranule<VA_BITS>,
level: isize,
) -> PhysicalAddress {
assert!(level <= 3);
assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
let oa_granule_mask = !(granule.block_size_at_level(level) - 1);
let descriptor_value = self.get();
// Figure D8-14 VMSAv8-64 Block descriptor formats
let pa = match granule {
// 4KB and 16KB granules, 52-bit OA
#[cfg(feature = "feat_lpa2")]
TranslationGranule::Granule4k | TranslationGranule::Granule16k => {
let oa_mask = oa_granule_mask & 0x000f_ffff_ffff_f000;
// OA[49:n] remains in place, OA[51:50] is mapped from [9:8]
let lsbs = descriptor_value & 0x0003_ffff_ffff_f000;
let msbs = ((descriptor_value >> 8) & 0x3) << 50;
(lsbs | msbs) as usize & oa_mask
}
// 64KB granule, 52-bit OA
#[cfg(feature = "feat_lpa")]
TranslationGranule::Granule64k => {
let oa_mask = oa_granule_mask & 0x000f_ffff_ffff_0000;
// OA[47:n] remains in place, OA[51:48] is mapped from [15:12]
let lsbs = descriptor_value & 0x0003_ffff_ffff_0000;
let msbs = ((descriptor_value >> 12) & 0xf) << 48;
(lsbs | msbs) as usize & oa_mask
}
// 4KB, 16KB, and 64KB granules, 48-bit OA
#[cfg(not(all(feature = "feat_lpa", feature = "feat_lpa2")))]
_ => {
let oa_mask = oa_granule_mask & 0x0000_ffff_ffff_f000;
descriptor_value as usize & oa_mask
}
};
PhysicalAddress(pa)
}
/// Set the attributes of the block descriptor
pub fn set_block_attributes(&mut self, level: isize, attributes: Attributes) {
assert!(level <= 3);
let attr: u64 = attributes.into();
assert_eq!(0, attr & !Self::ATTR_MASK);
assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
self.modify(|d| (d & !Self::ATTR_MASK) | attr);
}
/// Get the attributes of the block descriptor
pub fn get_block_attributes(&self, level: isize) -> Attributes {
assert!(level <= 3);
assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
Attributes::from(self.get() & Self::ATTR_MASK)
}
/// Set block descriptor to invalid
pub fn set_block_descriptor_to_invalid(&mut self, level: isize) {
assert!(level <= 3);
assert_eq!(DescriptorType::Block, self.get_descriptor_type(level));
self.set(Self::INVALID_DESCRIPTOR_VALUE)
}
/// Set block or invalid descriptor to invalid
pub fn set_block_or_invalid_descriptor_to_invalid(&mut self, level: isize) {
assert!(level <= 3);
assert!(DescriptorType::Table != self.get_descriptor_type(level));
self.set(Self::INVALID_DESCRIPTOR_VALUE)
}
/// Set table descriptor
pub fn set_table_descriptor(
&mut self,
level: isize,
next_level_table: PhysicalAddress,
next_level_attributes: Option<NextLevelAttributes>,
) {
assert!(level <= 2);
assert!(self.get_descriptor_type(level) != DescriptorType::Table);
let table_addr = next_level_table.0 as u64;
assert_eq!(0, table_addr & !Self::TA_MASK);
let mut raw_desc_value = Self::VALID_BIT | Self::TABLE_BIT | table_addr;
if let Some(next_attr) = next_level_attributes {
let next_attr_bits: u64 = next_attr.into();
assert_eq!(0, next_attr_bits & !Self::NEXT_ATTR_MASK);
raw_desc_value |= next_attr_bits;
}
self.set(raw_desc_value);
}
/// Get next level table
pub fn get_next_level_table(&self, level: isize) -> PhysicalAddress {
assert!(level <= 2);
assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
PhysicalAddress((self.get() & Self::TA_MASK) as usize)
}
/// Get next level attributes
pub fn get_next_level_attributes(&self, level: isize) -> NextLevelAttributes {
assert!(level <= 2);
assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
NextLevelAttributes::from(self.get() & Self::NEXT_ATTR_MASK)
}
/// Set table descriptor to invalid
pub fn set_table_descriptor_to_invalid(&mut self, level: isize) -> PhysicalAddress {
assert!(level <= 2);
assert_eq!(DescriptorType::Table, self.get_descriptor_type(level));
let pa = PhysicalAddress((self.get() & Self::TA_MASK) as usize);
self.set(Self::INVALID_DESCRIPTOR_VALUE);
pa
}
/// Get raw descriptor value
fn get(&self) -> u64 {
unsafe { ptr::read_volatile(self.cell.get()) }
}
/// Set raw descriptor value
fn set(&mut self, value: u64) {
// Safety: The cell should point to a valid address and the assembly code is just a data
// barrier.
unsafe {
ptr::write_volatile(self.cell.get(), value);
#[cfg(target_arch = "aarch64")]
core::arch::asm!("dsb ishst");
}
}
/// Modify raw descriptor value
fn modify<F>(&mut self, f: F)
where
F: Fn(u64) -> u64,
{
self.set(f(self.get()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_attributes() {
let attributes = Attributes::default();
assert_eq!(0u64, attributes.into());
let attributes = Attributes {
uxn: true,
..Default::default()
};
assert_eq!(1u64 << 54, attributes.into());
let attributes = Attributes {
pxn: true,
..Default::default()
};
assert_eq!(1u64 << 53, attributes.into());
let attributes = Attributes {
contiguous: true,
..Default::default()
};
assert_eq!(1u64 << 52, attributes.into());
let attributes = Attributes {
not_global: true,
..Default::default()
};
assert_eq!(1u64 << 11, attributes.into());
let attributes = Attributes {
access_flag: true,
..Default::default()
};
assert_eq!(1u64 << 10, attributes.into());
let attributes = Attributes {
non_secure: true,
..Default::default()
};
assert_eq!(1u64 << 5, attributes.into());
let attributes = Attributes {
mem_attr_index: MemoryAttributesIndex::Normal_IWBWA_OWBWA,
..Default::default()
};
assert_eq!(
(MemoryAttributesIndex::Normal_IWBWA_OWBWA as u64) << 2,
attributes.into()
);
let attributes: Attributes = 0.into();
assert!(!attributes.uxn);
assert!(!attributes.pxn);
assert!(!attributes.contiguous);
assert!(!attributes.not_global);
assert!(!attributes.access_flag);
assert_eq!(Shareability::NonShareable, attributes.shareability);
assert_eq!(
DataAccessPermissions::ReadWrite_None,
attributes.data_access_permissions
);
assert!(!attributes.non_secure);
assert_eq!(
MemoryAttributesIndex::Device_nGnRnE,
attributes.mem_attr_index
);
}
#[test]
fn test_next_level_attributes() {
let next_level_attributes = NextLevelAttributes::default();
assert_eq!(0u64, next_level_attributes.into());
let next_level_attributes = NextLevelAttributes {
ns_table: true,
..Default::default()
};
assert_eq!(1u64 << 63, next_level_attributes.into());
let next_level_attributes = NextLevelAttributes {
ap_table: 3.into(),
..Default::default()
};
assert_eq!(3u64 << 61, next_level_attributes.into());
let next_level_attributes = NextLevelAttributes {
xn_table: true,
..Default::default()
};
assert_eq!(1u64 << 60, next_level_attributes.into());
let next_level_attributes = NextLevelAttributes {
pxn_table: true,
..Default::default()
};
assert_eq!(1u64 << 59, next_level_attributes.into());
let next_level_attributes: NextLevelAttributes = 0.into();
assert!(!next_level_attributes.ns_table);
assert_eq!(0u8, next_level_attributes.ap_table.into());
assert!(!next_level_attributes.xn_table);
assert!(!next_level_attributes.pxn_table);
let next_level_attributes: NextLevelAttributes = u64::MAX.into();
assert!(next_level_attributes.ns_table);
assert_eq!(3u8, next_level_attributes.ap_table.into());
assert!(next_level_attributes.xn_table);
assert!(next_level_attributes.pxn_table);
}
#[test]
fn test_descriptor_get_type() {
let descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
assert_eq!(DescriptorType::Invalid, descriptor.get_descriptor_type(1));
let descriptor = Descriptor {
cell: UnsafeCell::new(1),
};
assert_eq!(DescriptorType::Block, descriptor.get_descriptor_type(1));
let descriptor = Descriptor {
cell: UnsafeCell::new(3),
};
assert_eq!(DescriptorType::Table, descriptor.get_descriptor_type(1));
let descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
assert_eq!(DescriptorType::Invalid, descriptor.get_descriptor_type(3));
let descriptor = Descriptor {
cell: UnsafeCell::new(3),
};
assert_eq!(DescriptorType::Block, descriptor.get_descriptor_type(3));
}
#[test]
fn test_descriptor_is_valid() {
let descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
assert!(!descriptor.is_valid());
let descriptor = Descriptor {
cell: UnsafeCell::new(1),
};
assert!(descriptor.is_valid());
}
#[test]
fn test_descriptor_set_block_to_block_again() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(1),
};
descriptor.set_block_descriptor::<48>(
TranslationGranule::Granule4k,
1,
PhysicalAddress(0),
Attributes::default(),
);
assert_eq!(0x1, descriptor.get());
}
#[test]
#[should_panic]
fn test_descriptor_set_block_invalid_oa() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_block_descriptor::<48>(
TranslationGranule::Granule4k,
1,
PhysicalAddress(1 << 63),
Attributes::default(),
);
}
#[test]
fn test_descriptor_block() {
let granule: TranslationGranule<48> = TranslationGranule::Granule4k;
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_block_descriptor(
granule,
1,
PhysicalAddress(0x0000000f_c0000000),
Attributes {
uxn: true,
..Default::default()
},
);
assert_eq!(0x0040000f_c0000001, descriptor.get());
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_block_descriptor(
granule,
3,
PhysicalAddress(0x0000000f_fffff000),
Attributes {
uxn: true,
..Default::default()
},
);
assert_eq!(0x0040000f_fffff003, descriptor.get());
assert_eq!(
PhysicalAddress(0x0000000f_fffff000),
descriptor.get_block_output_address(granule, 3)
);
assert_eq!(
Attributes {
uxn: true,
..Default::default()
},
descriptor.get_block_attributes(3)
);
descriptor.set_block_attributes(
3,
Attributes {
pxn: true,
..Default::default()
},
);
assert_eq!(
Attributes {
pxn: true,
..Default::default()
},
descriptor.get_block_attributes(3)
);
}
#[test]
#[should_panic]
fn test_descriptor_invalid_block_to_invalid() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_block_descriptor_to_invalid(0);
}
#[test]
fn test_descriptor_block_to_invalid() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(3),
};
descriptor.set_block_descriptor_to_invalid(3);
assert_eq!(0, descriptor.get());
}
#[test]
fn test_descriptor_block_or_invalid_block_to_invalid() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_block_or_invalid_descriptor_to_invalid(0);
assert_eq!(0, descriptor.get());
let mut descriptor = Descriptor {
cell: UnsafeCell::new(3),
};
descriptor.set_block_or_invalid_descriptor_to_invalid(3);
assert_eq!(0, descriptor.get());
}
#[test]
#[should_panic]
fn test_descriptor_level3_to_table() {
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_table_descriptor(3, PhysicalAddress(0), None);
}
#[test]
fn test_descriptor_block_to_table() {
let next_level_table = PhysicalAddress(0x1000);
let mut descriptor = Descriptor {
cell: UnsafeCell::new(1),
};
descriptor.set_table_descriptor(0, next_level_table, None);
assert_eq!(0x1003, descriptor.get());
}
#[test]
#[should_panic]
fn test_descriptor_table_non_aligned() {
let next_level_table = PhysicalAddress(0x800);
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_table_descriptor(0, next_level_table, None);
}
#[test]
fn test_descriptor_table() {
let next_level_table = PhysicalAddress(0x0000_000c_ba98_7000);
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_table_descriptor(0, next_level_table, None);
assert_eq!(0x0000_000c_ba98_7003, descriptor.get());
}
#[test]
fn test_descriptor_table_next_level_attr() {
const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
let next_level_table = PhysicalAddress(0x0000_000c_ba98_7000);
let mut descriptor = Descriptor {
cell: UnsafeCell::new(0),
};
descriptor.set_table_descriptor(
0,
next_level_table,
Some(NextLevelAttributes {
ns_table: true,
..Default::default()
}),
);
assert_eq!(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003, descriptor.get());
}
#[test]
fn test_descriptor_table_get_next_level_table() {
const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
let descriptor = Descriptor {
cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
};
assert_eq!(NEXT_LEVEL_ADDR, descriptor.get_next_level_table(0).0 as u64);
}
#[test]
fn test_descriptor_table_get_next_level_attr() {
const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
let descriptor = Descriptor {
cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
};
assert_eq!(
NextLevelAttributes {
ns_table: true,
..Default::default()
},
descriptor.get_next_level_attributes(0)
);
}
#[test]
fn test_descriptor_table_set_to_invalid() {
const NEXT_LEVEL_ADDR: u64 = 0x0000_000c_ba98_7000;
let mut descriptor = Descriptor {
cell: UnsafeCell::new(NEXT_LEVEL_ADDR | 0x8000_0000_0000_0003),
};
assert_eq!(
NEXT_LEVEL_ADDR,
descriptor.set_table_descriptor_to_invalid(0).0 as u64
);
assert_eq!(0, descriptor.get());
}
#[test]
fn test_descriptor_raw_interface() {
let cell_value = 0x01234567_89abcdefu64;
let cell_new_value = 0x12345678_9abcdef0u64;
let mut descriptor = Descriptor {
cell: UnsafeCell::new(cell_value),
};
assert_eq!(cell_value, descriptor.get());
descriptor.set(cell_new_value);
assert_eq!(cell_new_value, descriptor.get());
descriptor.modify(|d| d + 1);
assert_eq!(cell_new_value + 1, descriptor.get());
}
}