page_table_entry/arch/
loongarch64.rs

1//! loongarch64 page table entries.
2//!
3//! Loongarch64 Multi-level page table
4//!
5//! <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#section-multi-level-page-table-structure-supported-by-page-walking>
6
7use crate::{GenericPTE, MappingFlags};
8use core::fmt;
9use memory_addr::PhysAddr;
10
11bitflags::bitflags! {
12    /// Page-table entry flags.
13    ///
14    /// <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#tlb-refill-exception-entry-low-order-bits>
15    #[derive(Debug)]
16    pub struct PTEFlags: u64 {
17        /// Whether the PTE is valid.
18        const V = 1 << 0;
19        /// Indicates the virtual page has been written since the last time the
20        /// D bit was cleared.
21        const D = 1 << 1;
22        /// Privilege Level Low Bit
23        const PLVL = 1 << 2;
24        /// Privilege Level High Bit
25        const PLVH = 1 << 3;
26        /// Memory Access Type (MAT) of the page table entry.
27        ///
28        /// <https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#section-memory-access-types>
29        ///
30        /// 0 - SUC, 1 - CC, 2 - WUC
31        ///
32        /// 0 for strongly-ordered uncached,
33        ///
34        /// 1 for coherent cached,
35        ///
36        /// 2 for weakly-ordered uncached, and 3 for reserved.
37        ///
38        /// Memory Access Type Low Bit
39        /// controls the type of access
40        const MATL = 1 << 4;
41        /// Memory Access Type High Bit
42        const MATH = 1 << 5;
43        /// Designates a global mapping OR Whether the page is huge page.
44        const GH = 1 << 6;
45        /// Whether the physical page is exist.
46        const P = 1 << 7;
47        /// Whether the page is writable.
48        const W = 1 << 8;
49        /// Designates a global mapping when using huge page.
50        const G = 1 << 12;
51        /// Whether the page is not readable.
52        const NR = 1 << 61;
53        /// Whether the page is not executable.
54        const NX = 1 << 62;
55        /// Whether the privilege Level is restricted. When RPLV is 0, the PTE
56        /// can be accessed by any program with privilege Level highter than PLV.
57        const RPLV = 1 << 63;
58    }
59}
60
61impl From<PTEFlags> for MappingFlags {
62    fn from(f: PTEFlags) -> Self {
63        if !f.contains(PTEFlags::V) {
64            return Self::empty();
65        }
66        let mut ret = Self::empty();
67        if !f.contains(PTEFlags::NR) {
68            ret |= Self::READ;
69        }
70        if f.contains(PTEFlags::W) {
71            ret |= Self::WRITE;
72        }
73        if !f.contains(PTEFlags::NX) {
74            ret |= Self::EXECUTE;
75        }
76        if f.contains(PTEFlags::PLVL | PTEFlags::PLVH) {
77            ret |= Self::USER;
78        }
79        if !f.contains(PTEFlags::MATL) {
80            if f.contains(PTEFlags::MATH) {
81                ret |= Self::UNCACHED;
82            } else {
83                ret |= Self::DEVICE;
84            }
85        }
86        ret
87    }
88}
89
90impl From<MappingFlags> for PTEFlags {
91    fn from(f: MappingFlags) -> Self {
92        if f.is_empty() {
93            return Self::empty();
94        }
95        let mut ret = Self::V | Self::P;
96        if !f.contains(MappingFlags::READ) {
97            ret |= Self::NR;
98        }
99        if f.contains(MappingFlags::WRITE) {
100            ret |= Self::W;
101        }
102        if !f.contains(MappingFlags::EXECUTE) {
103            ret |= Self::NX;
104        }
105        if f.contains(MappingFlags::USER) {
106            ret |= Self::PLVH | Self::PLVL;
107        }
108        if !f.contains(MappingFlags::DEVICE) {
109            if f.contains(MappingFlags::UNCACHED) {
110                // weakly-ordered uncached
111                ret |= Self::MATH;
112            } else {
113                // coherent cached,
114                ret |= Self::MATL;
115            }
116        }
117        ret
118    }
119}
120
121/// page table entry for loongarch64 system
122#[derive(Clone, Copy)]
123#[repr(transparent)]
124pub struct LA64PTE(u64);
125
126impl LA64PTE {
127    const PHYS_ADDR_MASK: u64 = 0x0000_ffff_ffff_f000; // bits 12..48
128
129    /// Creates an empty descriptor with all bits set to zero.
130    pub const fn empty() -> Self {
131        Self(0)
132    }
133}
134
135impl GenericPTE for LA64PTE {
136    fn new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) -> Self {
137        let mut flags = PTEFlags::from(flags) | PTEFlags::D;
138        if is_huge {
139            flags |= PTEFlags::GH;
140        }
141        Self(flags.bits() | ((paddr.as_usize()) as u64 & Self::PHYS_ADDR_MASK))
142    }
143    fn new_table(paddr: PhysAddr) -> Self {
144        Self((paddr.as_usize() as u64) & Self::PHYS_ADDR_MASK)
145    }
146    fn paddr(&self) -> PhysAddr {
147        PhysAddr::from((self.0 & Self::PHYS_ADDR_MASK) as usize)
148    }
149    fn flags(&self) -> MappingFlags {
150        PTEFlags::from_bits_truncate(self.0).into()
151    }
152    fn set_paddr(&mut self, paddr: PhysAddr) {
153        self.0 = (self.0 & !Self::PHYS_ADDR_MASK) | (paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK)
154    }
155    fn set_flags(&mut self, flags: MappingFlags, is_huge: bool) {
156        let mut flags = PTEFlags::from(flags) | PTEFlags::D;
157        if is_huge {
158            flags |= PTEFlags::GH;
159        }
160        self.0 = (self.0 & Self::PHYS_ADDR_MASK) | flags.bits();
161    }
162    fn bits(self) -> usize {
163        self.0 as usize
164    }
165    fn is_unused(&self) -> bool {
166        self.0 == 0
167    }
168    fn is_present(&self) -> bool {
169        PTEFlags::from_bits_truncate(self.0).contains(PTEFlags::P)
170    }
171    fn is_huge(&self) -> bool {
172        PTEFlags::from_bits_truncate(self.0).contains(PTEFlags::GH)
173    }
174    fn clear(&mut self) {
175        self.0 = 0
176    }
177}
178
179impl fmt::Debug for LA64PTE {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        let mut f = f.debug_struct("LA64PTE");
182        f.field("raw", &self.0)
183            .field("paddr", &self.paddr())
184            .field("flags", &self.flags())
185            .finish()
186    }
187}