diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs new file mode 100644 index 0000000000..758db81e4e --- /dev/null +++ b/src/arch/aarch64.rs @@ -0,0 +1,111 @@ +use crate::{ + Arch, + MemoryArea, + PhysicalAddress, + TableKind, + VirtualAddress, +}; + +#[derive(Clone, Copy)] +pub struct AArch64Arch; + +impl Arch for AArch64Arch { + const PAGE_SHIFT: usize = 12; // 4096 bytes + const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries, 8 bytes each + const PAGE_LEVELS: usize = 4; // L0, L1, L2, L3 + + //TODO + const ENTRY_ADDRESS_SHIFT: usize = 52; + const ENTRY_FLAG_DEFAULT_PAGE: usize + = Self::ENTRY_FLAG_PRESENT + | 1 << 1 // Page flag + | 1 << 10 // Access flag + ; + const ENTRY_FLAG_DEFAULT_TABLE: usize + = Self::ENTRY_FLAG_PRESENT + | 1 << 1 // Table flag + | 1 << 10 // Access flag + ; + const ENTRY_FLAG_PRESENT: usize = 1 << 0; + const ENTRY_FLAG_READONLY: usize = 1 << 7; + const ENTRY_FLAG_READWRITE: usize = 0; + const ENTRY_FLAG_USER: usize = 1 << 6; + // This sets both userspace and privileged execute never + //TODO: Separate the two? + const ENTRY_FLAG_NO_EXEC: usize = 0b11 << 53; + + //TODO: adjust to match x86_64? + const PHYS_OFFSET: usize = 0xfffffe0000000000; + + unsafe fn init() -> &'static [MemoryArea] { + unimplemented!("AArch64Arch::init unimplemented"); + } + + #[inline(always)] + unsafe fn invalidate(address: VirtualAddress) { + //TODO: can one address be invalidated? + Self::invalidate_all(); + } + + #[inline(always)] + unsafe fn invalidate_all() { + asm!("tlbi vmalle1is"); + } + + #[inline(always)] + unsafe fn table() -> PhysicalAddress { + let address: usize; + //TODO: set this dynamically + let table_kind = TableKind::Kernel; + match table_kind { + TableKind::User => { + asm!("mrs {0}, ttbr0_el1", out(reg) address); + }, + TableKind::Kernel => { + asm!("mrs {0}, ttbr1_el1", out(reg) address); + } + } + PhysicalAddress::new(address) + } + + #[inline(always)] + unsafe fn set_table(address: PhysicalAddress) { + //TODO: set this dynamically + let table_kind = TableKind::Kernel; + match table_kind { + TableKind::User => { + asm!("msr ttbr0_el1, {0}", in(reg) address.data()); + }, + TableKind::Kernel => { + asm!("msr ttbr1_el1, {0}", in(reg) address.data()); + } + } + //TODO: Does this need to be called? + Self::invalidate_all(); + } +} + +#[cfg(test)] +mod tests { + use crate::Arch; + use super::AArch64Arch; + + #[test] + fn constants() { + assert_eq!(AArch64Arch::PAGE_SIZE, 4096); + assert_eq!(AArch64Arch::PAGE_OFFSET_MASK, 0xFFF); + assert_eq!(AArch64Arch::PAGE_ADDRESS_SHIFT, 48); + assert_eq!(AArch64Arch::PAGE_ADDRESS_SIZE, 0x0001_0000_0000_0000); + assert_eq!(AArch64Arch::PAGE_ADDRESS_MASK, 0x0000_FFFF_FFFF_F000); + assert_eq!(AArch64Arch::PAGE_ENTRY_SIZE, 8); + assert_eq!(AArch64Arch::PAGE_ENTRIES, 512); + assert_eq!(AArch64Arch::PAGE_ENTRY_MASK, 0x1FF); + assert_eq!(AArch64Arch::PAGE_NEGATIVE_MASK, 0xFFFF_0000_0000_0000); + + assert_eq!(AArch64Arch::ENTRY_ADDRESS_SIZE, 0x0010_0000_0000_0000); + assert_eq!(AArch64Arch::ENTRY_ADDRESS_MASK, 0x000F_FFFF_FFFF_F000); + assert_eq!(AArch64Arch::ENTRY_FLAGS_MASK, 0xFFF0_0000_0000_0FFF); + + assert_eq!(AArch64Arch::PHYS_OFFSET, 0xFFFF_8000_0000_0000); + } +} diff --git a/src/arch/emulate.rs b/src/arch/emulate.rs index 38b51c1e65..6f0342808d 100644 --- a/src/arch/emulate.rs +++ b/src/arch/emulate.rs @@ -24,11 +24,12 @@ impl Arch for EmulateArch { const PAGE_LEVELS: usize = X8664Arch::PAGE_LEVELS; const ENTRY_ADDRESS_SHIFT: usize = X8664Arch::ENTRY_ADDRESS_SHIFT; + const ENTRY_FLAG_DEFAULT_PAGE: usize = X8664Arch::ENTRY_FLAG_DEFAULT_PAGE; + const ENTRY_FLAG_DEFAULT_TABLE: usize = X8664Arch::ENTRY_FLAG_DEFAULT_TABLE; const ENTRY_FLAG_PRESENT: usize = X8664Arch::ENTRY_FLAG_PRESENT; - const ENTRY_FLAG_WRITABLE: usize = X8664Arch::ENTRY_FLAG_WRITABLE; + const ENTRY_FLAG_READONLY: usize = X8664Arch::ENTRY_FLAG_READONLY; + const ENTRY_FLAG_READWRITE: usize = X8664Arch::ENTRY_FLAG_READWRITE; const ENTRY_FLAG_USER: usize = X8664Arch::ENTRY_FLAG_USER; - const ENTRY_FLAG_HUGE: usize = X8664Arch::ENTRY_FLAG_HUGE; - const ENTRY_FLAG_GLOBAL: usize = X8664Arch::ENTRY_FLAG_GLOBAL; const ENTRY_FLAG_NO_EXEC: usize = X8664Arch::ENTRY_FLAG_NO_EXEC; const PHYS_OFFSET: usize = X8664Arch::PHYS_OFFSET; @@ -40,7 +41,7 @@ impl Arch for EmulateArch { // PML4 index 256 (PHYS_OFFSET) link to PDP let pml4 = 0; let pdp = pml4 + Self::PAGE_SIZE; - let flags = Self::ENTRY_FLAG_WRITABLE | Self::ENTRY_FLAG_PRESENT; + let flags = Self::ENTRY_FLAG_READWRITE | Self::ENTRY_FLAG_PRESENT; machine.write_phys::(PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE), pdp | flags); // PDP link to PD @@ -200,7 +201,7 @@ impl Machine { } if let Some((phys, flags)) = self.translate(virt) { - if flags & A::ENTRY_FLAG_WRITABLE != 0 { + if flags & A::ENTRY_FLAG_READWRITE != 0 { self.write_phys(phys, value); } else { panic!("write: 0x{:X} size 0x{:X} not writable", virt_data, size); @@ -218,7 +219,7 @@ impl Machine { } if let Some((phys, flags)) = self.translate(virt) { - if flags & A::ENTRY_FLAG_WRITABLE != 0 { + if flags & A::ENTRY_FLAG_READWRITE != 0 { self.write_phys_bytes(phys, value, count); } else { panic!("write_bytes: 0x{:X} count 0x{:X} not writable", virt_data, count); diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 27681785ce..ceecab5c41 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -6,10 +6,12 @@ use crate::{ VirtualAddress, }; +pub use self::aarch64::AArch64Arch; #[cfg(feature = "std")] pub use self::emulate::EmulateArch; pub use self::x86_64::X8664Arch; +mod aarch64; #[cfg(feature = "std")] mod emulate; mod x86_64; @@ -20,11 +22,12 @@ pub trait Arch: Clone + Copy { const PAGE_LEVELS: usize; const ENTRY_ADDRESS_SHIFT: usize; + const ENTRY_FLAG_DEFAULT_PAGE: usize; + const ENTRY_FLAG_DEFAULT_TABLE: usize; const ENTRY_FLAG_PRESENT: usize; - const ENTRY_FLAG_WRITABLE: usize; + const ENTRY_FLAG_READONLY: usize; + const ENTRY_FLAG_READWRITE: usize; const ENTRY_FLAG_USER: usize; - const ENTRY_FLAG_HUGE: usize; - const ENTRY_FLAG_GLOBAL: usize; const ENTRY_FLAG_NO_EXEC: usize; const PHYS_OFFSET: usize; diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 9225ee8d0a..19ff13883f 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -14,11 +14,14 @@ impl Arch for X8664Arch { const PAGE_LEVELS: usize = 4; // PML4, PDP, PD, PT const ENTRY_ADDRESS_SHIFT: usize = 52; + const ENTRY_FLAG_DEFAULT_PAGE: usize = Self::ENTRY_FLAG_PRESENT; + const ENTRY_FLAG_DEFAULT_TABLE: usize = Self::ENTRY_FLAG_PRESENT; const ENTRY_FLAG_PRESENT: usize = 1 << 0; - const ENTRY_FLAG_WRITABLE: usize = 1 << 1; + const ENTRY_FLAG_READONLY: usize = 0; + const ENTRY_FLAG_READWRITE: usize = 1 << 1; const ENTRY_FLAG_USER: usize = 1 << 2; - const ENTRY_FLAG_HUGE: usize = 1 << 7; - const ENTRY_FLAG_GLOBAL: usize = 1 << 8; + // Not used: const ENTRY_FLAG_HUGE: usize = 1 << 7; + // Not used: const ENTRY_FLAG_GLOBAL: usize = 1 << 8; const ENTRY_FLAG_NO_EXEC: usize = 1 << 63; const PHYS_OFFSET: usize = Self::PAGE_NEGATIVE_MASK + (Self::PAGE_ADDRESS_SIZE >> 1); // PML4 slot 256 and onwards diff --git a/src/lib.rs b/src/lib.rs index a4ae108f9e..81d047f23a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,17 @@ pub const MEGABYTE: usize = KILOBYTE * KILOBYTE; pub const GIGABYTE: usize = KILOBYTE * MEGABYTE; pub const TERABYTE: usize = KILOBYTE * GIGABYTE; -// Physical memory address +/// Specific table to be used, needed on some architectures +//TODO: Use this throughout the code +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum TableKind { + /// Userspace page table + User, + /// Kernel page table + Kernel, +} + +/// Physical memory address #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[repr(transparent)] pub struct PhysicalAddress(usize); @@ -39,7 +49,7 @@ impl PhysicalAddress { } } -// Virtual memory address +/// Virtual memory address #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[repr(transparent)] pub struct VirtualAddress(usize); diff --git a/src/main.rs b/src/main.rs index f6b94891da..6264e7bf35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use rmm::{ FrameAllocator, FrameCount, MemoryArea, + PageFlags, PageFlushAll, PageMapper, PageTable, @@ -190,7 +191,7 @@ unsafe fn new_tables(areas: &'static [MemoryArea]) { let flush = mapper.map_phys( virt, phys, - A::ENTRY_FLAG_WRITABLE + PageFlags::::new().write(true) ).expect("failed to map page to frame"); flush.ignore(); // Not the active table } @@ -238,7 +239,7 @@ unsafe fn new_tables(areas: &'static [MemoryArea]) { let virt = VirtualAddress::new(MEGABYTE + i * A::PAGE_SIZE); let flush = mapper.map( virt, - A::ENTRY_FLAG_USER | A::ENTRY_FLAG_WRITABLE + PageFlags::::new().user(true).write(true) ).expect("failed to map page"); flush_all.consume(flush); } diff --git a/src/page/flags.rs b/src/page/flags.rs new file mode 100644 index 0000000000..91a181dd60 --- /dev/null +++ b/src/page/flags.rs @@ -0,0 +1,61 @@ +use core::marker::PhantomData; + +use crate::Arch; + +#[derive(Clone, Copy)] +pub struct PageFlags { + data: usize, + phantom: PhantomData, +} + +impl PageFlags { + #[inline(always)] + pub fn new() -> Self { + unsafe { + Self::from_data( + // Flags set to present, kernel space, read-only, no-execute by default + A::ENTRY_FLAG_DEFAULT_PAGE | + A::ENTRY_FLAG_READONLY | + A::ENTRY_FLAG_NO_EXEC + ) + } + } + + #[inline(always)] + pub unsafe fn from_data(data: usize) -> Self { + Self { data, phantom: PhantomData } + } + + #[inline(always)] + pub fn data(&self) -> usize { + self.data + } + + #[inline(always)] + pub fn custom_flag(mut self, flag: usize, value: bool) -> Self { + if value { + self.data |= flag; + } else { + self.data &= !flag; + } + self + } + + #[inline(always)] + pub fn user(self, value: bool) -> Self { + self.custom_flag(A::ENTRY_FLAG_USER, value) + } + + #[inline(always)] + pub fn write(self, value: bool) -> Self { + // Architecture may use readonly or readwrite, support either + self.custom_flag(A::ENTRY_FLAG_READONLY, !value) + .custom_flag(A::ENTRY_FLAG_READWRITE, value) + } + + #[inline(always)] + pub fn execute(self, value: bool) -> Self { + //TODO: write xor execute? + self.custom_flag(A::ENTRY_FLAG_NO_EXEC, !value) + } +} diff --git a/src/page/mapper.rs b/src/page/mapper.rs index 524e170719..6617547ca7 100644 --- a/src/page/mapper.rs +++ b/src/page/mapper.rs @@ -4,6 +4,7 @@ use crate::{ Arch, FrameAllocator, PageEntry, + PageFlags, PageFlush, PageTable, PhysicalAddress, @@ -47,15 +48,15 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { ) } - pub unsafe fn map(&mut self, virt: VirtualAddress, flags: usize) -> Option> { + pub unsafe fn map(&mut self, virt: VirtualAddress, flags: PageFlags) -> Option> { let phys = self.allocator.allocate_one()?; self.map_phys(virt, phys, flags) } - pub unsafe fn map_phys(&mut self, virt: VirtualAddress, phys: PhysicalAddress, flags: usize) -> Option> { + pub unsafe fn map_phys(&mut self, virt: VirtualAddress, phys: PhysicalAddress, flags: PageFlags) -> Option> { //TODO: verify virt and phys are aligned //TODO: verify flags have correct bits - let entry = PageEntry::new(phys.data() | flags | A::ENTRY_FLAG_PRESENT); + let entry = PageEntry::new(phys.data() | flags.data()); let mut table = self.table(); loop { let i = table.index_of(virt)?; @@ -70,7 +71,7 @@ impl<'f, A: Arch, F: FrameAllocator> PageMapper<'f, A, F> { None => { let next_phys = self.allocator.allocate_one()?; //TODO: correct flags? - table.set_entry(i, PageEntry::new(next_phys.data() | A::ENTRY_FLAG_WRITABLE | A::ENTRY_FLAG_PRESENT)); + table.set_entry(i, PageEntry::new(next_phys.data() | A::ENTRY_FLAG_READWRITE | A::ENTRY_FLAG_DEFAULT_TABLE)); table.next(i)? } }; diff --git a/src/page/mod.rs b/src/page/mod.rs index 48a07e717d..bcf360b322 100644 --- a/src/page/mod.rs +++ b/src/page/mod.rs @@ -1,11 +1,13 @@ pub use self::{ entry::*, + flags::*, flush::*, mapper::*, table::*, }; mod entry; +mod flags; mod flush; mod mapper; mod table;