diff --git a/src/arch/aarch64.rs b/src/arch/aarch64.rs index f9896e3879..be42f31aab 100644 --- a/src/arch/aarch64.rs +++ b/src/arch/aarch64.rs @@ -84,6 +84,11 @@ impl Arch for AArch64Arch { //TODO: Does this need to be called? Self::invalidate_all(); } + + fn virt_is_valid(address: VirtualAddress) -> bool { + // FIXME + true + } } #[cfg(test)] diff --git a/src/arch/emulate.rs b/src/arch/emulate.rs index 35098e2828..a73bb084c8 100644 --- a/src/arch/emulate.rs +++ b/src/arch/emulate.rs @@ -101,6 +101,10 @@ impl Arch for EmulateArch { unsafe fn set_table(address: PhysicalAddress) { MACHINE.as_mut().unwrap().set_table(address); } + fn virt_is_valid(_address: VirtualAddress) -> bool { + // TODO: Don't see why an emulated arch would have any problems with canonicalness... + true + } } const MEMORY_SIZE: usize = 64 * MEGABYTE; diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 3c9d0e0c31..7194e16cec 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -3,6 +3,7 @@ use core::ptr; use crate::{ MemoryArea, PhysicalAddress, + TableKind, VirtualAddress, }; @@ -84,4 +85,6 @@ pub trait Arch: Clone + Copy { unsafe fn phys_to_virt(phys: PhysicalAddress) -> VirtualAddress { VirtualAddress::new(phys.data() + Self::PHYS_OFFSET) } + + fn virt_is_valid(address: VirtualAddress) -> bool; } diff --git a/src/arch/riscv64/sv39.rs b/src/arch/riscv64/sv39.rs index e8cb7cd8f4..7b14130b5b 100644 --- a/src/arch/riscv64/sv39.rs +++ b/src/arch/riscv64/sv39.rs @@ -2,6 +2,7 @@ use crate::{ Arch, MemoryArea, PhysicalAddress, + TableKind, VirtualAddress, }; @@ -58,6 +59,13 @@ impl Arch for RiscV64Sv39Arch { (address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment) asm!("csrw satp, {0}", in(reg) satp); } + + fn virt_is_valid(address: VirtualAddress) -> bool { + const MASK: usize = 0xFFFF_FFC0_0000_0000; + let masked = address.data() & MASK; + + masked == MASK || masked == 0 + } } #[cfg(test)] @@ -83,4 +91,29 @@ mod tests { assert_eq!(RiscV64Sv39Arch::PHYS_OFFSET, 0xFFFF_FE00_0000_0000); } + #[test] + fn is_canonical() { + use super::VirtualAddress; + + #[track_caller] + fn yes(addr: usize) { + assert!(RiscV64Sv39Arch::virt_is_valid(VirtualAddress::new(addr))); + } + #[track_caller] + fn no(addr: usize) { + assert!(!RiscV64Sv39Arch::virt_is_valid(VirtualAddress::new(addr))); + } + + yes(0xFFFF_FFFF_FFFF_FFFF); + yes(0xFFFF_FFF0_1337_1337); + no(0x0000_0F00_0000_0000); + no(0x1337_0000_0000_0000); + no(1 << 38); + yes(1 << 37); + + // Check for off-by-one errors. + yes(0xFFFF_FFC0_0000_0000 | (1 << 37)); + yes(0xFFFF_FFE0_0000_0000 | (1 << 37)); + no(0xFFFF_FF80_0000_0000 | (1 << 37)); + } } diff --git a/src/arch/riscv64/sv48.rs b/src/arch/riscv64/sv48.rs index f49076cc66..5e1eff9d59 100644 --- a/src/arch/riscv64/sv48.rs +++ b/src/arch/riscv64/sv48.rs @@ -2,6 +2,7 @@ use crate::{ Arch, MemoryArea, PhysicalAddress, + TableKind, VirtualAddress, }; @@ -58,6 +59,14 @@ impl Arch for RiscV64Sv48Arch { (address.data() >> Self::PAGE_SHIFT); // Convert to PPN (TODO: ensure alignment) asm!("csrw satp, {0}", in(reg) satp); } + fn virt_is_valid(address: VirtualAddress) -> bool { + // RISC-V SV48 uses 48-bit sign-extended addresses, identical to 4-level paging on x86_64. + let mask = 0xFFFF_8000_0000_0000; + let masked = address.data() & mask; + + masked == mask + || masked == 0 + } } #[cfg(test)] @@ -83,4 +92,24 @@ mod tests { assert_eq!(RiscV64Sv48Arch::PHYS_OFFSET, 0xFFFF_FE00_0000_0000); } + #[test] + fn is_canonical() { + use super::VirtualAddress; + + // Close to identical when compared to x86_64 test. + fn yes(address: usize) { + assert!(RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(address))); + } + fn no(address: usize) { + assert!(!RiscV64Sv48Arch::virt_is_valid(VirtualAddress::new(address))); + } + + yes(0xFFFF_8000_1337_1337); + yes(0xFFFF_FFFF_FFFF_FFFF); + yes(0x0000_0000_0000_0042); + yes(0x0000_7FFF_FFFF_FFFF); + no(0x1337_0000_0000_0000); + no(0x1337_8000_0000_0000); + no(0x0000_8000_0000_0000); + } } diff --git a/src/arch/x86_64.rs b/src/arch/x86_64.rs index 3b0a2572da..da100f7bee 100644 --- a/src/arch/x86_64.rs +++ b/src/arch/x86_64.rs @@ -47,12 +47,30 @@ impl Arch for X8664Arch { unsafe fn set_table(address: PhysicalAddress) { asm!("mov cr3, {0}", in(reg) address.data()); } + + fn virt_is_valid(address: VirtualAddress) -> bool { + // On x86_64, an address is valid if and only if it is canonical. It may still point to + // unmapped memory, but will always be valid once translated via the page table has + // suceeded. + address.is_canonical() + } +} + +impl VirtualAddress { + #[cfg(any(doc, target_arch = "x86_64"))] + #[doc(cfg(target_arch = "x86_64"))] + pub fn is_canonical(self) -> bool { + let masked = self.data() & 0xFFFF_8000_0000_0000; + // TODO: 5-level paging + masked == 0xFFFF_8000_0000_0000 + || masked == 0 + } } #[cfg(test)] mod tests { use crate::Arch; - use super::X8664Arch; + use super::{VirtualAddress, X8664Arch}; #[test] fn constants() { @@ -72,4 +90,21 @@ mod tests { assert_eq!(X8664Arch::PHYS_OFFSET, 0xFFFF_8000_0000_0000); } + #[test] + fn is_canonical() { + fn yes(address: usize) { + assert!(VirtualAddress::new(address).is_canonical()); + } + fn no(address: usize) { + assert!(!VirtualAddress::new(address).is_canonical()); + } + + yes(0xFFFF_8000_1337_1337); + yes(0xFFFF_FFFF_FFFF_FFFF); + yes(0x0000_0000_0000_0042); + yes(0x0000_7FFF_FFFF_FFFF); + no(0x1337_0000_0000_0000); + no(0x1337_8000_0000_0000); + no(0x0000_8000_0000_0000); + } } diff --git a/src/lib.rs b/src/lib.rs index 487e089e88..58d27a564b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature(asm)] +#![feature(asm, doc_cfg)] pub use crate::{ allocator::*,