diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs index 7a22a5a9d8..caa1bb666a 100644 --- a/src/arch/aarch64.rs +++ b/src/arch/aarch64.rs @@ -2,6 +2,7 @@ use crate::{ Arch, MemoryArea, PhysicalAddress, + TableKind, VirtualAddress, }; @@ -11,16 +12,26 @@ 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; // PML4, PDP, PD, PT + 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 << 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_WRITABLE: usize = 1 << 1; - const ENTRY_FLAG_USER: usize = 1 << 2; - const ENTRY_FLAG_HUGE: usize = 1 << 7; - const ENTRY_FLAG_GLOBAL: usize = 1 << 8; - const ENTRY_FLAG_NO_EXEC: usize = 1 << 63; + 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; const PHYS_OFFSET: usize = Self::PAGE_NEGATIVE_MASK + (Self::PAGE_ADDRESS_SIZE >> 1); // PML4 slot 256 and onwards @@ -30,17 +41,45 @@ impl Arch for AArch64Arch { #[inline(always)] unsafe fn invalidate(address: VirtualAddress) { - unimplemented!() + //TODO: can one address be invalidated? + Self::invalidate_all(); + } + + #[inline(always)] + unsafe fn invalidate_all() { + asm!("tlbi vmalle1is"); } #[inline(always)] unsafe fn table() -> PhysicalAddress { - unimplemented!() + 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) { - unimplemented!() + //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(); } } diff --git a/src/arch/emulate.rs b/src/arch/emulate.rs index 65db01049d..6f0342808d 100644 --- a/src/arch/emulate.rs +++ b/src/arch/emulate.rs @@ -27,7 +27,8 @@ impl Arch for EmulateArch { 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_NO_EXEC: usize = X8664Arch::ENTRY_FLAG_NO_EXEC; @@ -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 72b8eef1c5..ceecab5c41 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -25,7 +25,8 @@ pub trait Arch: Clone + Copy { 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_NO_EXEC: usize; diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 4768a2b9bd..19ff13883f 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -14,10 +14,11 @@ 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 = ENTRY_FLAG_PRESENT; - const ENTRY_FLAG_DEFAULT_TABLE: usize = ENTRY_FLAG_PRESENT; + 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; // Not used: const ENTRY_FLAG_HUGE: usize = 1 << 7; // Not used: const ENTRY_FLAG_GLOBAL: usize = 1 << 8; 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..432c7fc44c --- /dev/null +++ b/src/page/flags.rs @@ -0,0 +1,61 @@ +use core::marker::PhantomData; + +use crate::Arch; + +#[derive(Clone, Copy, Debug)] +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 ee9f7b9277..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_DEFAULT_PAGE); + 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_DEFAULT_TABLE)); + 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;