Skip to content

Commit

Permalink
Aarch64 PTE flags bilge conversion [WiP]
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanRoyer committed Dec 20, 2023
1 parent 92295d7 commit 3b44e92
Showing 1 changed file with 154 additions and 177 deletions.
331 changes: 154 additions & 177 deletions kernel/pte_flags/src/pte_flags_aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,195 +1,172 @@
//! The aarch64-specific definitions of PTE flags.
use crate::PteFlags;
use bitflags::bitflags;
use bilge::bilge;

/// A mask for the bits of a page table entry that contain the physical frame address.
pub const PTE_FRAME_MASK: u64 = 0x0000_FFFF_FFFF_F000;

// Ensure that we never expose reserved bits [12:47] as part of the `PteFlagsAarch64` interface.
const _: () = assert!(PteFlagsAarch64::all().bits() & PTE_FRAME_MASK == 0);

bitflags! {
/// Page table entry (PTE) flags on aarch64.
///
/// **Note:** items beginning with an underscore `_` are not used in Theseus.
///
/// The designation of bits in each `PageTableEntry` is as such:
/// * Bits `[0:11]` (inclusive) are reserved by hardware for access flags, cacheability flags,
/// shareability flags, and TLB storage flags.
/// * Bits `[12:47]` (inclusive) are reserved by hardware to hold the physical frame address.
/// * Bits `[48:49]` (inclusive) are reserved as zero.
/// * Bits `[50:54]` (inclusive) are reserved by hardware for more access flags.
/// * Bits `[55:58]` (inclusive) are available for custom OS usage.
/// * Bits `[59:63]` (inclusive) are reserved by hardware for extended access flags.
/// Page table entry (PTE) flags on aarch64.
///
/// **Note:** items beginning with an underscore `_` are not used in Theseus.
///
/// The designation of bits in each `PageTableEntry` is as such:
/// * Bits `[0:11]` (inclusive) are reserved by hardware for access flags, cacheability flags,
/// shareability flags, and TLB storage flags.
/// * Bits `[12:47]` (inclusive) are reserved by hardware to hold the physical frame address.
/// * Bits `[48:49]` (inclusive) are reserved as zero.
/// * Bits `[50:54]` (inclusive) are reserved by hardware for more access flags.
/// * Bits `[55:58]` (inclusive) are available for custom OS usage.
/// * Bits `[59:63]` (inclusive) are reserved by hardware for extended access flags.
///
///
/// ## Assumed System Configuration
/// * The system has been configured to use 48-bit physical addresses
/// (aka "OA"s: Output Addresses).
/// * The system has been configured to use only a single translation stage, Stage 1.
/// * The [MAIR] index 0 has a Normal + Outer Shareable entry.
/// * The [MAIR] index 1 has a "DEVICE nGnRE" entry.
///
/// [MAIR]: https://docs.rs/cortex-a/latest/cortex_a/registers/MAIR_EL1/index.html
#[bitsize(64)]
#[derive(DebugBits, Copy, Clone, FromBits, FromBytes)]
struct PteFlagsAarch64 {
/// * If set, this page is currently "present" in memory.
/// * If not set, this page is not in memory, which could mean one of several things:
/// * The page is not mapped at all
/// * The page has been temporarily paged/swapped to disk
/// * The page is waiting to be mapped, i.e., for demand paging.
valid: bool,

descriptor_type: DescriptorType,

/// Indicates which MAIR register describes this page's cacheability.
mair_index: MairIndex,

/// * If set, this page is accessible in both Secure and Non-Secure execution levels.
/// * If not set, this page is accessible in only Secure execution levels.
_non_secure_access: bool,

/// * If set, userspace (unprivileged mode) can access this page.
/// * If not set, only kernelspace (privileged mode) can access this page.
_user_accessible: bool,

/// * If set, this page is read-only.
/// * If not set, this page is writable.
read_only: bool,

shareability: Shareability,

/// * The hardware will set this bit when the page is accessed.
/// * The OS can then clear this bit once it has acknowledged that the page was accessed,
/// if it cares at all about this information.
///
/// On aarch64, an "Access Flag Fault" may be raised if this bit is not set
/// when this page is first accessed and is trying to be cached in the TLB.
/// This fault can only occur when the Access Flag bit is `0` and the flag is being
/// managed by software.
///
/// Thus, Theseus currently *always* sets this bit by default.
accessed: bool,

/// * If set, this page is mapped into only one or less than all address spaces,
/// or is mapped differently across different address spaces,
/// and thus be flushed out of the TLB when switching address spaces (page tables).
/// * If not set, this page is mapped identically across all address spaces
/// (all root page tables) and doesn't need to be flushed out of the TLB
/// when switching to another address space (page table).
///
/// ## Assumed System Configuration
/// * The system has been configured to use 48-bit physical addresses
/// (aka "OA"s: Output Addresses).
/// * The system has been configured to use only a single translation stage, Stage 1.
/// * The [MAIR] index 0 has a Normal + Outer Shareable entry.
/// * The [MAIR] index 1 has a "DEVICE nGnRE" entry.
/// Note: Theseus is a single address space system, so this flag makes no difference.
_not_global: bool,

/// Bits `[12:47]` (inclusive) of the physical frame address
phys_addr: u36,

// reserved, zero
reserved: u2,

/// * If set, this page is considered a "Guarded Page",
/// which can be used to protect against executing instructions
/// that aren't the intended target of a branch (e.g., with `BTI` instruction).
///
/// This is only available if `FEAT_BTI` is implemented;
/// otherwise it is reserved as 0.
_guarded_page: bool,

/// * The hardware will set this bit when the page has been written to.
/// * The OS can then clear this bit once it has acknowledged that the page was written to,
/// which is primarily useful for paging/swapping to disk.
dirty: bool,

/// * If set, this translation table entry is part of a set that is contiguous in memory
/// with adjacent entries that also have this bit set.
/// * If not set, this translation table entry is not contiguous in memory
/// with entries that are adjancent to it.
///
/// [MAIR]: https://docs.rs/cortex-a/latest/cortex_a/registers/MAIR_EL1/index.html
#[doc(cfg(target_arch = "aarch64"))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PteFlagsAarch64: u64 {
/// * If set, this page is currently "present" in memory.
/// * If not set, this page is not in memory, which could mean one of several things:
/// * The page is not mapped at all
/// * The page has been temporarily paged/swapped to disk
/// * The page is waiting to be mapped, i.e., for demand paging.
const VALID = 1 << 0;

/// * If set, this represents a page descriptor.
/// * If not set, this represents a block descriptor.
const PAGE_DESCRIPTOR = 1 << 1;

/// Indicates the page's cacheability is described by MAIR Index 0.
///
/// Theseus uses this index for "normal" memory.
const _MAIR_INDEX_0 = 0 << 2;
/// This page maps "normal" memory, i.e., non-device memory.
///
/// Theseus uses `MAIR_INDEX_0` for this type of memory.
const NORMAL_MEMORY = Self::_MAIR_INDEX_0.bits();
/// Indicates the page's cacheability is described by MAIR Index 1.
///
/// Theseus uses this index for "device" memory.
const _MAIR_INDEX_1 = 1 << 2;
/// This page maps device memory, i.e., memory-mapped I/O registers.
///
/// Theseus uses `MAIR_INDEX_1` for this type of memory.
const DEVICE_MEMORY = Self::_MAIR_INDEX_1.bits();
/// Indicates the page's cacheability is described by MAIR Index 2.
///
/// This is unused in Theseus.
const _MAIR_INDEX_2 = 2 << 2;
/// Indicates the page's cacheability is described by MAIR Index 3.
///
/// This is unused in Theseus.
const _MAIR_INDEX_3 = 3 << 2;
/// Indicates the page's cacheability is described by MAIR Index 4.
///
/// This is unused in Theseus.
const _MAIR_INDEX_4 = 4 << 2;
/// Indicates the page's cacheability is described by MAIR Index 5.
///
/// This is unused in Theseus.
const _MAIR_INDEX_5 = 5 << 2;
/// Indicates the page's cacheability is described by MAIR Index 6.
///
/// This is unused in Theseus.
const _MAIR_INDEX_6 = 6 << 2;
/// Indicates the page's cacheability is described by MAIR Index 7.
///
/// This is unused in Theseus.
const _MAIR_INDEX_7 = 7 << 2;

/// * If set, this page is accessible in both Secure and Non-Secure execution levels.
/// * If not set, this page is accessible in only Secure execution levels.
///
/// This is unused in Theseus.
const _NON_SECURE_ACCESS = 1 << 5;

/// * If set, userspace (unprivileged mode) can access this page.
/// * If not set, only kernelspace (privileged mode) can access this page.
///
/// This is unused in Theseus because it is a single privilege level OS.
const _USER_ACCESSIBLE = 1 << 6;

/// * If set, this page is read-only.
/// * If not set, this page is writable.
const READ_ONLY = 1 << 7;

/// Indicates that only a single CPU core may access this page.
///
/// This is not used and not supported by Theseus; use [`Self::OUTER_SHAREABLE`].
const _NON_SHAREABLE = 0 << 8;
// Shareable `0b01` is reserved.
// const SHAREABLE_RSVD = 1 << 8;
/// Indicates that multiple CPUs from multiple clusters may access this page.
///
/// This is the default and the the only value used in Theseus (and most systems).
const OUTER_SHAREABLE = 2 << 8;
/// Multiple cores from the same
/// cluster can access this page.
/// Indicates that multiple CPUs from only a single cluster may access this page.
///
/// This is not used and not supported by Theseus; use [`Self::OUTER_SHAREABLE`].
const _INNER_SHAREABLE = 3 << 8;

/// * The hardware will set this bit when the page is accessed.
/// * The OS can then clear this bit once it has acknowledged that the page was accessed,
/// if it cares at all about this information.
///
/// On aarch64, an "Access Flag Fault" may be raised if this bit is not set
/// when this page is first accessed and is trying to be cached in the TLB.
/// This fault can only occur when the Access Flag bit is `0` and the flag is being
/// managed by software.
///
/// Thus, Theseus currently *always* sets this bit by default.
const ACCESSED = 1 << 10;

/// * If set, this page is mapped into only one or less than all address spaces,
/// or is mapped differently across different address spaces,
/// and thus be flushed out of the TLB when switching address spaces (page tables).
/// * If not set, this page is mapped identically across all address spaces
/// (all root page tables) and doesn't need to be flushed out of the TLB
/// when switching to another address space (page table).
///
/// Note: Theseus is a single address space system, so this flag makes no difference.
const _NOT_GLOBAL = 1 << 11;

/// * If set, this page is considered a "Guarded Page",
/// which can be used to protect against executing instructions
/// that aren't the intended target of a branch (e.g., with `BTI` instruction).
///
/// This is only available if `FEAT_BTI` is implemented;
/// otherwise it is reserved as 0.
///
/// This is currently not used in Theseus.
const _GUARDED_PAGE = 1 << 50;

/// * The hardware will set this bit when the page has been written to.
/// * The OS can then clear this bit once it has acknowledged that the page was written to,
/// which is primarily useful for paging/swapping to disk.
const DIRTY = 1 << 51;

/// * If set, this translation table entry is part of a set that is contiguous in memory
/// with adjacent entries that also have this bit set.
/// * If not set, this translation table entry is not contiguous in memory
/// with entries that are adjancent to it.
///
/// This is useful for reducing TLB pressure because the TLB entries for
/// multiple contiguous adjacent entries can be combined into one TLB entry.
///
/// This is currently not used in Theseus.
const _CONTIGUOUS = 1 << 52;

/// * If set, this page is not executable by privileged levels (kernel).
/// * If not set, this page is executable by privileged levels (kernel).
///
/// In Theseus, use [`Self::NOT_EXECUTABLE`] instead.
const _PRIV_EXEC_NEVER = 1 << 53;
/// * If set, this page is not executable by unprivileged levels (user).
/// * If not set, this page is executable by unprivileged levels (user).
///
/// In Theseus, use [`Self::NOT_EXECUTABLE`] instead.
const _USER_EXEC_NEVER = 1 << 54;
/// * If set, this page is not executable.
/// * If not set, this page is executable.
const NOT_EXECUTABLE = Self::_PRIV_EXEC_NEVER.bits() | Self::_USER_EXEC_NEVER.bits();

/// See [PteFlags::EXCLUSIVE].
/// We use bit 55 because it is available for custom OS usage on both x86_64 and aarch64.
const EXCLUSIVE = 1 << 55;
}
/// This is useful for reducing TLB pressure because the TLB entries for
/// multiple contiguous adjacent entries can be combined into one TLB entry.
_contiguous: bool,

executability: Executability,

/// See [PteFlags::EXCLUSIVE].
/// We use bit 55 because it is available for custom OS usage on both x86_64 and aarch64.
exclusive: bool,

// unused software bits
reserved: u3,

// more hardware bits
reserved: u5,
}

const SHAREABLE_BITS_MASK: PteFlagsAarch64 = PteFlagsAarch64::_INNER_SHAREABLE;
const MAIR_BITS_MASK: PteFlagsAarch64 = PteFlagsAarch64::_MAIR_INDEX_7;
#[bitsize(1)]
#[derive(Debug, FromBits)]
enum DescriptorType {
/// This descriptor maps a single page to a single frame
BlockDescriptor = 0,
/// This descriptor maps a block of virtual memory to a
/// block of physical memory (bigger than a page)
PageDescriptor = 1,
}

#[bitsize(3)]
#[derive(Debug, FromBits)]
enum MairIndex {
Normal = 0,
Device = 1,
#[fallback]
Reserved = 7,
}

#[bitsize(2)]
#[derive(Debug, FromBits)]
enum Shareability {
/// Only a single CPU core may access this page.
NonShareable = 0,
/// Indicates that multiple CPUs from multiple clusters may access this page.
OuterShareable = 2,
/// Indicates that multiple CPUs from only a single cluster may access this page.
InnerShareable = 3,
#[fallback]
Reserved = 1,
}

#[bitsize(2)]
#[derive(Debug, FromBits)]
enum Executability {
/// Both userspace and privileged contexts can execute code in this page
AlwaysExecutable = 0,
/// Only userspace context can execute code in this page
UserExecutable = 1,
/// Only privileged context can execute code in this page
PrivExecutable = 2,
/// No context can execute code in this page
NeverExecutable = 3,
}

/// See [`PteFlagsAarch64::new()`] for what bits are set by default.
impl Default for PteFlagsAarch64 {
Expand Down

0 comments on commit 3b44e92

Please sign in to comment.