diff --git a/Cargo.lock b/Cargo.lock index 0a75750bd3..6c0539a709 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2447,6 +2447,15 @@ dependencies = [ "cc", ] +[[package]] +name = "pte_flags" +version = "0.1.0" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "static_assertions", +] + [[package]] name = "pwd" version = "0.1.0" diff --git a/kernel/pte_flags/Cargo.toml b/kernel/pte_flags/Cargo.toml new file mode 100644 index 0000000000..0512c10077 --- /dev/null +++ b/kernel/pte_flags/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pte_flags" +authors = ["Nathan Royer ", "Kevin Boos Self { + Self::new() + } +} + +impl PteFlags { + /// Returns a new `PteFlagsX86_64` with the default value, in which: + /// * `ACCESSED` is set. + /// * the `NOT_EXECUTABLE` bit is set. + /// + /// Note: the `ACCESSED` bit is set by default because Theseus + /// currently doesn't perform any paging/swapping of pages to disk, + /// which is what this bit is typically used for. + /// On aarch64, not setting this bit can cause an Access Flag Fault + /// (which is useful only for software-managed LRU paging algorithms), + /// so we just set that bit by default to avoid any faults + /// that we don't care about. + pub const fn new() -> Self { + Self::from_bits_truncate( + Self::ACCESSED.bits + | Self::NOT_EXECUTABLE.bits + ) + } + + /// Returns a copy of this `PteFlags` with the `VALID` bit set or cleared. + /// + /// * If `enable` is `true`, this PTE will be considered "present" and "valid", + /// meaning that the mapping from this page to a physical frame is valid + /// and that the translation of a virtual address in this page should succeed. + /// * If `enable` is `false`, this PTE will be considered "invalid", + /// and any attempt to access it for translation purposes will cause a page fault. + #[must_use] + #[doc(alias("present"))] + pub fn valid(mut self, enable: bool) -> Self { + self.set(Self::VALID, enable); + self + } + + /// Returns a copy of this `PteFlags` with the `WRITABLE` bit set or cleared. + /// + /// * If `enable` is `true`, this will be writable. + /// * If `enable` is `false`, this will be read-only. + #[must_use] + #[doc(alias("read_only"))] + pub fn writable(mut self, enable: bool) -> Self { + self.set(Self::WRITABLE, enable); + self + } + + /// Returns a copy of this `PteFlags` with the `NOT_EXECUTABLE` bit cleared or set. + /// + /// * If `enable` is `true`, this page will be executable (`NOT_EXECUTABLE` will be cleared). + /// * If `enable` is `false`, this page will be non-executable, which is the default + /// (`NOT_EXECUTABLE` will be set). + #[must_use] + #[doc(alias("no_exec"))] + pub fn executable(mut self, enable: bool) -> Self { + self.set(Self::NOT_EXECUTABLE, !enable); + self + } + + /// Returns a copy of this `PteFlags` with the `DEVICE_MEMORY` bit set or cleared. + /// + /// * If `enable` is `true`, this will be non-cacheable device memory. + /// * If `enable` is `false`, this will be "normal" memory, the default. + #[must_use] + #[doc(alias("cache", "cacheable", "non-cacheable"))] + pub fn device_memory(mut self, enable: bool) -> Self { + self.set(Self::DEVICE_MEMORY, enable); + self + } + + /// Returns a copy of this `PteFlags` with the `EXCLUSIVE` bit set or cleared. + /// + /// * If `enable` is `true`, this page will exclusively map its frame. + /// * If `enable` is `false`, this page will NOT exclusively map its frame. + #[must_use] + pub fn exclusive(mut self, enable: bool) -> Self { + self.set(Self::EXCLUSIVE, enable); + self + } + + /// Returns a copy of this `PteFlags` with the `ACCESSED` bit set or cleared. + /// + /// Typically this is used to clear the `ACCESSED` bit, in order to indicate + /// that the OS has "acknowledged" the fact that this page was accessed + /// since the last time it checked. + /// + /// * If `enable` is `true`, this page will be marked as accessed. + /// * If `enable` is `false`, this page will be marked as not accessed. + #[must_use] + pub fn accessed(mut self, enable: bool) -> Self { + self.set(Self::ACCESSED, enable); + self + } + + /// Returns a copy of this `PteFlags` with the `DIRTY` bit set or cleared. + /// + /// Typically this is used to clear the `DIRTY` bit, in order to indicate + /// that the OS has "acknowledged" the fact that this page was written to + /// since the last time it checked. + /// This bit is typically set by the hardware. + /// + /// * If `enable` is `true`, this page will be marked as dirty. + /// * If `enable` is `false`, this page will be marked as clean. + #[must_use] + pub fn dirty(mut self, enable: bool) -> Self { + self.set(Self::DIRTY, enable); + self + } + + #[doc(alias("present"))] + pub fn is_valid(&self) -> bool { + self.contains(Self::VALID) + } + + #[doc(alias("read_only"))] + pub fn is_writable(&self) -> bool { + self.contains(Self::WRITABLE) + } + + #[doc(alias("no_exec"))] + pub fn is_executable(&self) -> bool { + !self.contains(Self::NOT_EXECUTABLE) + } + + #[doc(alias("cache", "cacheable", "non-cacheable"))] + pub fn is_device_memory(&self) -> bool { + self.contains(Self::DEVICE_MEMORY) + } + + pub fn is_dirty(&self) -> bool { + self.contains(Self::DIRTY) + } + + pub fn is_accessed(&self) -> bool { + self.contains(Self::ACCESSED) + } + + pub fn is_exclusive(&self) -> bool { + self.contains(Self::EXCLUSIVE) + } +} diff --git a/kernel/pte_flags/src/pte_flags_aarch64.rs b/kernel/pte_flags/src/pte_flags_aarch64.rs new file mode 100644 index 0000000000..de87c11a51 --- /dev/null +++ b/kernel/pte_flags/src/pte_flags_aarch64.rs @@ -0,0 +1,227 @@ +//! The aarch64-specific definitions of PTE flags. + +use crate::PteFlags; +use bitflags::bitflags; +use static_assertions::const_assert_eq; + +/// 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 ` interface. +const_assert_eq!(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. + /// + /// + /// ## 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 "DEVICE nGnRE" entry, + /// and [MAIR] index 1 has a Normal + Outer Shareable entry. + /// + /// [MAIR]: https://docs.rs/cortex-a/latest/cortex_a/registers/MAIR_EL1/index.html + #[doc(cfg(target_arch = "aarch64"))] + 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 device memory, i.e., memory-mapped I/O registers. + /// Theseus uses `MAIR_INDEX_0` for this type of memory. + const DEVICE_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 "normal" memory, i.e., non-device memory. + /// Theseus uses `MAIR_INDEX_1` for this type of memory. + const NORMAL_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. + 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. + 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. + 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. + 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). + 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). + const USER_EXEC_NEVER = 1 << 54; + 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; + } +} + +/// See [`PteFlagsAarch64::new()`] for what bits are set by default. +impl Default for PteFlagsAarch64 { + fn default() -> Self { + Self::new() + } +} + +impl PteFlagsAarch64 { + /// The mask of bits that should be overwritten with default values + /// when converting a generic `PteFlags` into a specific `PteFlagsAarch64`. + /// Currently this includes: + /// * The two bits `[8:9]` for shareability. + pub const OVERWRITTEN_BITS_FOR_CONVERSION: PteFlagsAarch64 = + PteFlagsAarch64::_INNER_SHAREABLE; + + + /// Returns a new `PteFlagsAarch64` with the default value, in which: + /// * `NORMAL_MEMORY` (not `DEVICE_MEMORY`) is set. + /// * `OUTER_SHAREABLE` is set. + /// * `READ_ONLY` is set. + /// * `ACCESSED` is set. + /// * `NOT_GLOBAL` is set. + /// * the `NOT_EXECUTABLE` bits are set. + /// + /// Note: the `ACCESSED` bit is set by default because Theseus + /// currently doesn't perform any paging/swapping of pages to disk, + /// which is what this bit is typically used for. + /// On aarch64, not setting this bit can cause an Access Flag Fault + /// (which is useful only for software-managed LRU paging algorithms), + /// so we just set that bit by default to avoid any faults + /// that we don't care about. + pub const fn new() -> Self { + Self::from_bits_truncate( + Self::NORMAL_MEMORY.bits + | Self::OUTER_SHAREABLE.bits + | Self::READ_ONLY.bits + | Self::ACCESSED.bits + | Self::_NOT_GLOBAL.bits + | Self::NOT_EXECUTABLE.bits + ) + } +} + +impl From for PteFlagsAarch64 { + /// When converting from `PteFlags` to `PteFlagsAarch64`, the bits given by + /// [`PteFlagsAarch64::OVERWRITTEN_BITS_FOR_CONVERSION`] will be overwritten + /// with a default value. + /// + /// Currently, this includes: + /// * `OUTER_SHAREABLE` will be set. + fn from(general: PteFlags) -> Self { + let mut specific = Self::from_bits_truncate(general.bits()); + specific.toggle(super::WRITABLE_BIT | super::GLOBAL_BIT); + specific &= !Self::OVERWRITTEN_BITS_FOR_CONVERSION; // clear the masked bits + specific |= Self::OUTER_SHAREABLE; // set the masked bits to their default + specific + } +} + +impl From for PteFlags { + fn from(mut specific: PteFlagsAarch64) -> Self { + specific.toggle(super::WRITABLE_BIT | super::GLOBAL_BIT); + Self::from_bits_truncate(specific.bits()) + } +} diff --git a/kernel/pte_flags/src/pte_flags_x86_64.rs b/kernel/pte_flags/src/pte_flags_x86_64.rs new file mode 100644 index 0000000000..208eeab620 --- /dev/null +++ b/kernel/pte_flags/src/pte_flags_x86_64.rs @@ -0,0 +1,109 @@ +//! The x86_64-specific definitions of PTE flags. + +use crate::PteFlags; +use bitflags::bitflags; +use static_assertions::const_assert_eq; + +/// A mask for the bits of a page table entry that contain the physical frame address. +pub const PTE_FRAME_MASK: u64 = 0x000_FFFFFFFFFF_000; + +// Ensure that we never expose reserved bits [12:51] as part of the ` interface. +const_assert_eq!(PteFlagsX86_64::all().bits() & PTE_FRAME_MASK, 0); + + +bitflags! { + /// Page table entry (PTE) flags on x86_64. + /// + /// **Note:** items beginning with an underscore `_` are not used in Theseus. + /// + /// The designation of bits in each `PageTableEntry` is as such: + /// * Bits `[0:8]` (inclusive) are reserved by hardware for access flags. + /// * Bits `[9:11]` (inclusive) are available for custom OS usage. + /// * Bits `[12:51]` (inclusive) are reserved by hardware to hold the physical frame address. + /// * Bits `[52:62]` (inclusive) are available for custom OS usage. + /// * Bit `63` is reserved by hardware for access flags (noexec). + #[doc(cfg(target_arch = "x86_64"))] + pub struct PteFlagsX86_64: 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 page is writable. + /// * If not set, this page is read-only. + const WRITABLE = 1 << 1; + /// * If set, userspace (ring 3) can access this page. + /// * If not set, only kernelspace (ring 0) can access this page. + const _USER_ACCESSIBLE = 1 << 2; + /// * If set, writes to this page go directly to memory. + /// * It not set, writes are first written to the CPU cache, and then written to memory. + /// This is also known as "write-back". + const _WRITE_THROUGH = 1 << 3; + /// * If set, this page's content is never cached, neither for read nor writes. + /// * If not set, this page's content is cached as normal, both for read nor writes. + const NO_CACHE = 1 << 4; + /// An alias for `NO_CACHE` in order to ease compatibility with aarch64. + const DEVICE_MEMORY = Self::NO_CACHE.bits; + /// * 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. + const ACCESSED = 1 << 5; + /// * 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 << 6; + /// * If set, this page table entry represents a "huge" page. + /// This bit may be used as follows: + /// * For a P4-level PTE, it must be not set. + /// * If set for a P3-level PTE, it means this PTE maps a 1GiB huge page. + /// * If set for a P2-level PTE, it means this PTE maps a 1MiB huge page. + /// * For a P1-level PTE, it must be not set. + /// * If not set, this is a normal 4KiB page mapping. + const HUGE_PAGE = 1 << 7; + /// * If 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). + /// * If not 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). + /// + /// Note: Theseus is a single address space system, so this flag makes no difference. + const _GLOBAL = 1 << 8; + + /// 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; + + /// * If set, this page is not executable. + /// * If not set, this page is executable. + const NOT_EXECUTABLE = 1 << 63; + } +} + +/// See [`PteFlagsX86_64::new()`] for what bits are set by default. +impl Default for PteFlagsX86_64 { + fn default() -> Self { + Self::new() + } +} + +impl PteFlagsX86_64 { + /// Returns a new `PteFlagsX86_64` with the default value, in which + /// only the `NOT_EXECUTABLE` bit is set. + pub const fn new() -> Self { + Self::NOT_EXECUTABLE + } +} + +impl From for PteFlagsX86_64 { + fn from(general: PteFlags) -> Self { + Self::from_bits_truncate(general.bits()) + } +} + +impl From for PteFlags { + fn from(specific: PteFlagsX86_64) -> Self { + Self::from_bits_truncate(specific.bits()) + } +}