Merge remote-tracking branch 'origin/aarch64-rebase'

This commit is contained in:
Jeremy Soller
2021-04-13 19:12:48 -06:00
9 changed files with 213 additions and 20 deletions
+111
View File
@@ -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);
}
}
+7 -6
View File
@@ -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::<usize>(PhysicalAddress::new(pml4 + 256 * Self::PAGE_ENTRY_SIZE), pdp | flags);
// PDP link to PD
@@ -200,7 +201,7 @@ impl<A: Arch> Machine<A> {
}
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<A: Arch> Machine<A> {
}
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);
+6 -3
View File
@@ -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;
+6 -3
View File
@@ -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
+12 -2
View File
@@ -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);
+3 -2
View File
@@ -10,6 +10,7 @@ use rmm::{
FrameAllocator,
FrameCount,
MemoryArea,
PageFlags,
PageFlushAll,
PageMapper,
PageTable,
@@ -190,7 +191,7 @@ unsafe fn new_tables<A: Arch>(areas: &'static [MemoryArea]) {
let flush = mapper.map_phys(
virt,
phys,
A::ENTRY_FLAG_WRITABLE
PageFlags::<A>::new().write(true)
).expect("failed to map page to frame");
flush.ignore(); // Not the active table
}
@@ -238,7 +239,7 @@ unsafe fn new_tables<A: Arch>(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::<A>::new().user(true).write(true)
).expect("failed to map page");
flush_all.consume(flush);
}
+61
View File
@@ -0,0 +1,61 @@
use core::marker::PhantomData;
use crate::Arch;
#[derive(Clone, Copy)]
pub struct PageFlags<A> {
data: usize,
phantom: PhantomData<A>,
}
impl<A: Arch> PageFlags<A> {
#[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)
}
}
+5 -4
View File
@@ -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<PageFlush<A>> {
pub unsafe fn map(&mut self, virt: VirtualAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
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<PageFlush<A>> {
pub unsafe fn map_phys(&mut self, virt: VirtualAddress, phys: PhysicalAddress, flags: PageFlags<A>) -> Option<PageFlush<A>> {
//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)?
}
};
+2
View File
@@ -1,11 +1,13 @@
pub use self::{
entry::*,
flags::*,
flush::*,
mapper::*,
table::*,
};
mod entry;
mod flags;
mod flush;
mod mapper;
mod table;