Refactor, use trait for arch differences
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
// Import values from x86_64
|
||||
pub use crate::arch::x86_64::*;
|
||||
|
||||
use core::{
|
||||
mem,
|
||||
ptr,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::{
|
||||
Arch,
|
||||
PhysicalAddress,
|
||||
MemoryArea,
|
||||
};
|
||||
|
||||
|
||||
pub struct EmulateArch;
|
||||
|
||||
impl Arch for EmulateArch {
|
||||
unsafe fn init() -> &'static [MemoryArea] {
|
||||
// Create machine with PAGE_ENTRIES pages identity mapped (2 MiB on x86_64)
|
||||
// Pages over 1 MiB will be mapped writable
|
||||
let mut machine = Machine::new(MEMORY_SIZE);
|
||||
|
||||
// PML4 link to PDP
|
||||
let pml4 = 0;
|
||||
let pdp = pml4 + PAGE_SIZE;
|
||||
let flags = ENTRY_FLAG_WRITABLE | ENTRY_FLAG_PRESENT;
|
||||
machine.write_phys::<usize>(pml4, pdp | flags);
|
||||
|
||||
// Recursive mapping
|
||||
machine.write_phys::<usize>(pml4 + (PAGE_ENTRIES - 1) * PAGE_ENTRY_SIZE, pml4 | flags);
|
||||
|
||||
// PDP link to PD
|
||||
let pd = pdp + PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pdp, pd | flags);
|
||||
|
||||
// PD link to PT
|
||||
let pt = pd + PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pd, pt | flags);
|
||||
|
||||
// PT links to frames
|
||||
for i in 0..PAGE_ENTRIES {
|
||||
let page = i * PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pt + i * PAGE_ENTRY_SIZE, page | flags);
|
||||
}
|
||||
|
||||
MACHINE = Some(machine);
|
||||
|
||||
// Set table to pml4
|
||||
EmulateArch::set_table(pml4);
|
||||
|
||||
&MEMORY_AREAS
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn read<T>(address: usize) -> T {
|
||||
MACHINE.as_ref().unwrap().read(address)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write<T>(address: usize, value: T) {
|
||||
MACHINE.as_mut().unwrap().write(address, value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate(address: usize) {
|
||||
MACHINE.as_mut().unwrap().invalidate(address);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn invalidate_all() {
|
||||
MACHINE.as_mut().unwrap().invalidate_all();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn table() -> usize {
|
||||
MACHINE.as_mut().unwrap().get_table()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_table(address: usize) {
|
||||
MACHINE.as_mut().unwrap().set_table(address);
|
||||
}
|
||||
}
|
||||
|
||||
const MEGABYTE: usize = 1024 * 1024;
|
||||
const MEMORY_SIZE: usize = 64 * MEGABYTE;
|
||||
static MEMORY_AREAS: [MemoryArea; 1] = [
|
||||
MemoryArea {
|
||||
base: PhysicalAddress::new(MEGABYTE),
|
||||
size: MEMORY_SIZE,
|
||||
}
|
||||
];
|
||||
|
||||
static mut MACHINE: Option<Machine> = None;
|
||||
|
||||
struct Machine {
|
||||
memory: Box<[u8]>,
|
||||
map: BTreeMap<usize, usize>,
|
||||
table_addr: usize,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
fn new(memory_size: usize) -> Self {
|
||||
Self {
|
||||
memory: vec![0; memory_size].into_boxed_slice(),
|
||||
map: BTreeMap::new(),
|
||||
table_addr: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_phys<T>(&self, phys: usize) -> T {
|
||||
let size = mem::size_of::<T>();
|
||||
if phys + size <= self.memory.len() {
|
||||
unsafe {
|
||||
ptr::read(self.memory.as_ptr().add(phys) as *const T)
|
||||
}
|
||||
} else {
|
||||
panic!("read_phys: 0x{:X} size 0x{:X} outside of memory", phys, size);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_phys<T>(&mut self, phys: usize, value: T) {
|
||||
let size = mem::size_of::<T>();
|
||||
if phys + size <= self.memory.len() {
|
||||
unsafe {
|
||||
ptr::write(self.memory.as_mut_ptr().add(phys) as *mut T, value);
|
||||
}
|
||||
} else {
|
||||
panic!("write_phys: 0x{:X} size 0x{:X} outside of memory", phys, size);
|
||||
}
|
||||
}
|
||||
|
||||
fn translate(&self, virt: usize) -> Option<(usize, usize)> {
|
||||
let page = virt & PAGE_ADDRESS_MASK;
|
||||
let offset = virt & PAGE_OFFSET_MASK;
|
||||
let phys = self.map.get(&page)?;
|
||||
Some((
|
||||
(phys & ENTRY_ADDRESS_MASK) + offset,
|
||||
phys & ENTRY_FLAGS_MASK,
|
||||
))
|
||||
}
|
||||
|
||||
fn read<T>(&self, virt: usize) -> T {
|
||||
//TODO: allow reading past page boundaries
|
||||
let size = mem::size_of::<T>();
|
||||
if (virt & PAGE_ADDRESS_MASK) != ((virt + (size - 1)) & PAGE_ADDRESS_MASK) {
|
||||
panic!("read: 0x{:X} size 0x{:X} passes page boundary", virt, size);
|
||||
}
|
||||
|
||||
if let Some((phys, _flags)) = self.translate(virt) {
|
||||
self.read_phys(phys)
|
||||
} else {
|
||||
panic!("read: 0x{:X} size 0x{:X} not present", virt, size);
|
||||
}
|
||||
}
|
||||
|
||||
fn write<T>(&mut self, virt: usize, value: T) {
|
||||
//TODO: allow writing past page boundaries
|
||||
let size = mem::size_of::<T>();
|
||||
if (virt & PAGE_ADDRESS_MASK) != ((virt + (size - 1)) & PAGE_ADDRESS_MASK) {
|
||||
panic!("write: 0x{:X} size 0x{:X} passes page boundary", virt, size);
|
||||
}
|
||||
|
||||
if let Some((phys, flags)) = self.translate(virt) {
|
||||
if flags & ENTRY_FLAG_WRITABLE != 0 {
|
||||
self.write_phys(phys, value);
|
||||
} else {
|
||||
panic!("write: 0x{:X} size 0x{:X} not writable", virt, size);
|
||||
}
|
||||
} else {
|
||||
panic!("write: 0x{:X} size 0x{:X} not present", virt, size);
|
||||
}
|
||||
}
|
||||
|
||||
fn invalidate(&mut self, _address: usize) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn invalidate_all(&mut self) {
|
||||
self.map.clear();
|
||||
|
||||
// PML4
|
||||
let a4 = self.table_addr;
|
||||
for i4 in 0..PAGE_ENTRIES {
|
||||
let e3 = self.read_phys::<usize>(a4 + i4 * PAGE_ENTRY_SIZE);
|
||||
let f3 = e3 & ENTRY_FLAGS_MASK;
|
||||
if f3 & ENTRY_FLAG_PRESENT == 0 { continue; }
|
||||
|
||||
// Page directory pointer
|
||||
let a3 = e3 & ENTRY_ADDRESS_MASK;
|
||||
for i3 in 0..PAGE_ENTRIES {
|
||||
let e2 = self.read_phys::<usize>(a3 + i3 * PAGE_ENTRY_SIZE);
|
||||
let f2 = e2 & ENTRY_FLAGS_MASK;
|
||||
if f2 & ENTRY_FLAG_PRESENT == 0 { continue; }
|
||||
|
||||
// Page directory
|
||||
let a2 = e2 & ENTRY_ADDRESS_MASK;
|
||||
for i2 in 0..PAGE_ENTRIES {
|
||||
let e1 = self.read_phys::<usize>(a2 + i2 * PAGE_ENTRY_SIZE);
|
||||
let f1 = e1 & ENTRY_FLAGS_MASK;
|
||||
if f1 & ENTRY_FLAG_PRESENT == 0 { continue; }
|
||||
|
||||
// Page table
|
||||
let a1 = e1 & ENTRY_ADDRESS_MASK;
|
||||
for i1 in 0..PAGE_ENTRIES {
|
||||
let e = self.read_phys::<usize>(a1 + i1 * PAGE_ENTRY_SIZE);
|
||||
let f = e & ENTRY_FLAGS_MASK;
|
||||
if f & ENTRY_FLAG_PRESENT == 0 { continue; }
|
||||
|
||||
// Page
|
||||
let a = e & ENTRY_ADDRESS_MASK;
|
||||
let page =
|
||||
if i4 >= 256 { 0xFFFF_0000_0000_0000 } else { 0 } |
|
||||
(i4 << 39) |
|
||||
(i3 << 30) |
|
||||
(i2 << 21) |
|
||||
(i1 << 12);
|
||||
println!("map 0x{:X} to 0x{:X}, 0x{:X}", page, a, f);
|
||||
self.map.insert(page, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_table(&self) -> usize {
|
||||
self.table_addr
|
||||
}
|
||||
|
||||
fn set_table(&mut self, address: usize) {
|
||||
self.table_addr = address;
|
||||
self.invalidate_all();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use core::ptr;
|
||||
|
||||
use crate::MemoryArea;
|
||||
|
||||
pub mod emulate;
|
||||
pub mod x86_64;
|
||||
|
||||
pub trait Arch {
|
||||
unsafe fn init() -> &'static [MemoryArea];
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn read<T>(address: usize) -> T {
|
||||
ptr::read(address as *const T)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn write<T>(address: usize, value: T) {
|
||||
ptr::write(address as *mut T, value)
|
||||
}
|
||||
|
||||
unsafe fn invalidate(address: usize);
|
||||
|
||||
unsafe fn invalidate_all();
|
||||
|
||||
unsafe fn table() -> usize;
|
||||
|
||||
unsafe fn set_table(address: usize);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use core::mem;
|
||||
|
||||
//TODO: should these be constants?
|
||||
pub const PAGE_SHIFT: usize = 12; // 4096 bytes
|
||||
pub const PAGE_SIZE: usize = 1 << PAGE_SHIFT;
|
||||
pub const PAGE_OFFSET_MASK: usize = PAGE_SIZE - 1;
|
||||
pub const PAGE_ADDRESS_MASK: usize = !PAGE_OFFSET_MASK;
|
||||
pub const PAGE_ENTRY_SIZE: usize = mem::size_of::<usize>();
|
||||
pub const PAGE_ENTRIES: usize = PAGE_SIZE / PAGE_ENTRY_SIZE;
|
||||
pub const PAGE_ENTRY_SHIFT: usize = 9; // 512 entries
|
||||
pub const PAGE_ENTRY_MASK: usize = (1 << PAGE_ENTRY_SHIFT) - 1;
|
||||
pub const PAGE_LEVELS: usize = 4; // PML4, PDP, PD, PT
|
||||
|
||||
pub const ENTRY_FLAG_PRESENT: usize = 1 << 0;
|
||||
pub const ENTRY_FLAG_WRITABLE: usize = 1 << 1;
|
||||
pub const ENTRY_FLAG_USER: usize = 1 << 2;
|
||||
pub const ENTRY_FLAG_HUGE: usize = 1 << 7;
|
||||
pub const ENTRY_FLAG_GLOBAL: usize = 1 << 8;
|
||||
pub const ENTRY_FLAG_NO_EXEC: usize = 1 << 63;
|
||||
pub const ENTRY_ADDRESS_MASK: usize = PAGE_ADDRESS_MASK;
|
||||
pub const ENTRY_FLAGS_MASK: usize = !ENTRY_ADDRESS_MASK;
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
pub use crate::{
|
||||
arch::Arch,
|
||||
arch::emulate::*,
|
||||
page::{
|
||||
PageEntry,
|
||||
PageTable
|
||||
},
|
||||
};
|
||||
|
||||
mod arch;
|
||||
mod page;
|
||||
|
||||
// Physical memory address
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PhysicalAddress(usize);
|
||||
|
||||
impl PhysicalAddress {
|
||||
pub const fn new(address: usize) -> Self {
|
||||
Self(address)
|
||||
}
|
||||
|
||||
pub unsafe fn data(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const fn aligned(&self) -> bool {
|
||||
self.0 & PAGE_OFFSET_MASK == 0
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual memory address
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct VirtualAddress(usize);
|
||||
|
||||
impl VirtualAddress {
|
||||
pub const fn new(address: usize) -> Self {
|
||||
Self(address)
|
||||
}
|
||||
|
||||
pub unsafe fn data(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const fn aligned(&self) -> bool {
|
||||
self.0 & PAGE_OFFSET_MASK == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemoryArea {
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
}
|
||||
+20
-418
@@ -1,370 +1,13 @@
|
||||
use core::{
|
||||
mem,
|
||||
ptr,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use rmm::*;
|
||||
|
||||
//TODO: should this be a constant?
|
||||
pub const PAGE_SHIFT: usize = 12;
|
||||
pub const PAGE_SIZE: usize = 1 << PAGE_SHIFT;
|
||||
pub const PAGE_OFFSET_MASK: usize = PAGE_SIZE - 1;
|
||||
pub const PAGE_ADDRESS_MASK: usize = !PAGE_OFFSET_MASK;
|
||||
pub const PAGE_ENTRY_SIZE: usize = mem::size_of::<usize>();
|
||||
pub const PAGE_ENTRIES: usize = PAGE_SIZE / PAGE_ENTRY_SIZE;
|
||||
pub const PAGE_ENTRY_SHIFT: usize = 9;
|
||||
pub const PAGE_ENTRY_MASK: usize = (1 << PAGE_ENTRY_SHIFT) - 1;
|
||||
pub const PAGE_LEVELS: usize = 4;
|
||||
|
||||
// Physical memory address
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PhysicalAddress(usize);
|
||||
|
||||
impl PhysicalAddress {
|
||||
pub const fn new(address: usize) -> Self {
|
||||
Self(address)
|
||||
}
|
||||
|
||||
pub const fn aligned(&self) -> bool {
|
||||
self.0 & PAGE_OFFSET_MASK == 0
|
||||
}
|
||||
}
|
||||
|
||||
// Physical memory frame
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct Frame(PhysicalAddress);
|
||||
|
||||
impl Frame {
|
||||
pub fn new(address: PhysicalAddress) -> Option<Self> {
|
||||
if address.aligned() {
|
||||
Some(Frame(address))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual memory address
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct VirtualAddress(usize);
|
||||
|
||||
impl VirtualAddress {
|
||||
pub const fn new(address: usize) -> Self {
|
||||
Self(address)
|
||||
}
|
||||
|
||||
pub const fn aligned(&self) -> bool {
|
||||
self.0 & PAGE_OFFSET_MASK == 0
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual memory page
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct Page(VirtualAddress);
|
||||
|
||||
impl Page {
|
||||
pub fn new(address: VirtualAddress) -> Option<Self> {
|
||||
if address.aligned() {
|
||||
Some(Self(address))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ENTRY_PRESENT: usize = 1 << 0;
|
||||
const ENTRY_WRITABLE: usize = 1 << 1;
|
||||
const ENTRY_ADDRESS_MASK: usize = PAGE_ADDRESS_MASK;
|
||||
const ENTRY_FLAGS_MASK: usize = !ENTRY_ADDRESS_MASK;
|
||||
|
||||
static mut MACHINE: Option<Machine> = None;
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_read<T>(address: usize) -> T {
|
||||
MACHINE.as_ref().unwrap().read(address)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_write<T>(address: usize, value: T) {
|
||||
MACHINE.as_mut().unwrap().write(address, value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_invalidate(address: usize) {
|
||||
MACHINE.as_mut().unwrap().invalidate(address);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_invalidate_all() {
|
||||
MACHINE.as_mut().unwrap().invalidate_all();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_get_table() -> usize {
|
||||
MACHINE.as_mut().unwrap().get_table()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn arch_set_table(address: usize) {
|
||||
MACHINE.as_mut().unwrap().set_table(address);
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
pub memory: Box<[u8]>,
|
||||
pub map: BTreeMap<usize, usize>,
|
||||
pub table_addr: usize,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new(memory_size: usize) -> Self {
|
||||
Self {
|
||||
memory: vec![0; memory_size].into_boxed_slice(),
|
||||
map: BTreeMap::new(),
|
||||
table_addr: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_phys<T>(&self, phys: usize) -> T {
|
||||
let size = mem::size_of::<T>();
|
||||
if phys + size <= self.memory.len() {
|
||||
unsafe {
|
||||
ptr::read(self.memory.as_ptr().add(phys) as *const T)
|
||||
}
|
||||
} else {
|
||||
panic!("read_phys: 0x{:X} size 0x{:X} outside of memory", phys, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_phys<T>(&mut self, phys: usize, value: T) {
|
||||
let size = mem::size_of::<T>();
|
||||
if phys + size <= self.memory.len() {
|
||||
unsafe {
|
||||
ptr::write(self.memory.as_mut_ptr().add(phys) as *mut T, value);
|
||||
}
|
||||
} else {
|
||||
panic!("write_phys: 0x{:X} size 0x{:X} outside of memory", phys, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate(&self, virt: usize) -> Option<(usize, usize)> {
|
||||
let page = virt & PAGE_ADDRESS_MASK;
|
||||
let offset = virt & PAGE_OFFSET_MASK;
|
||||
let phys = self.map.get(&page)?;
|
||||
Some((
|
||||
(phys & ENTRY_ADDRESS_MASK) + offset,
|
||||
phys & ENTRY_FLAGS_MASK,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn read<T>(&self, virt: usize) -> T {
|
||||
//TODO: allow reading past page boundaries
|
||||
let size = mem::size_of::<T>();
|
||||
if (virt & PAGE_ADDRESS_MASK) != ((virt + (size - 1)) & PAGE_ADDRESS_MASK) {
|
||||
panic!("read: 0x{:X} size 0x{:X} passes page boundary", virt, size);
|
||||
}
|
||||
|
||||
if let Some((phys, _flags)) = self.translate(virt) {
|
||||
self.read_phys(phys)
|
||||
} else {
|
||||
panic!("read: 0x{:X} size 0x{:X} not present", virt, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write<T>(&mut self, virt: usize, value: T) {
|
||||
//TODO: allow writing past page boundaries
|
||||
let size = mem::size_of::<T>();
|
||||
if (virt & PAGE_ADDRESS_MASK) != ((virt + (size - 1)) & PAGE_ADDRESS_MASK) {
|
||||
panic!("write: 0x{:X} size 0x{:X} passes page boundary", virt, size);
|
||||
}
|
||||
|
||||
if let Some((phys, flags)) = self.translate(virt) {
|
||||
if flags & ENTRY_WRITABLE != 0 {
|
||||
self.write_phys(phys, value);
|
||||
} else {
|
||||
panic!("write: 0x{:X} size 0x{:X} not writable", virt, size);
|
||||
}
|
||||
} else {
|
||||
panic!("write: 0x{:X} size 0x{:X} not present", virt, size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalidate(&mut self, _address: usize) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn invalidate_all(&mut self) {
|
||||
self.map.clear();
|
||||
|
||||
// PML4
|
||||
let a4 = self.table_addr;
|
||||
for i4 in 0..PAGE_ENTRIES {
|
||||
let e3 = self.read_phys::<usize>(a4 + i4 * PAGE_ENTRY_SIZE);
|
||||
let f3 = e3 & ENTRY_FLAGS_MASK;
|
||||
if f3 & ENTRY_PRESENT == 0 { continue; }
|
||||
|
||||
// Page directory pointer
|
||||
let a3 = e3 & ENTRY_ADDRESS_MASK;
|
||||
for i3 in 0..PAGE_ENTRIES {
|
||||
let e2 = self.read_phys::<usize>(a3 + i3 * PAGE_ENTRY_SIZE);
|
||||
let f2 = e2 & ENTRY_FLAGS_MASK;
|
||||
if f2 & ENTRY_PRESENT == 0 { continue; }
|
||||
|
||||
// Page directory
|
||||
let a2 = e2 & ENTRY_ADDRESS_MASK;
|
||||
for i2 in 0..PAGE_ENTRIES {
|
||||
let e1 = self.read_phys::<usize>(a2 + i2 * PAGE_ENTRY_SIZE);
|
||||
let f1 = e1 & ENTRY_FLAGS_MASK;
|
||||
if f1 & ENTRY_PRESENT == 0 { continue; }
|
||||
|
||||
// Page table
|
||||
let a1 = e1 & ENTRY_ADDRESS_MASK;
|
||||
for i1 in 0..PAGE_ENTRIES {
|
||||
let e = self.read_phys::<usize>(a1 + i1 * PAGE_ENTRY_SIZE);
|
||||
let f = e & ENTRY_FLAGS_MASK;
|
||||
if f & ENTRY_PRESENT == 0 { continue; }
|
||||
|
||||
// Page
|
||||
let a = e & ENTRY_ADDRESS_MASK;
|
||||
let page =
|
||||
if i4 >= 256 { 0xFFFF_0000_0000_0000 } else { 0 } |
|
||||
(i4 << 39) |
|
||||
(i3 << 30) |
|
||||
(i2 << 21) |
|
||||
(i1 << 12);
|
||||
println!("map 0x{:X} to 0x{:X}, 0x{:X}", page, a, f);
|
||||
self.map.insert(page, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_table(&self) -> usize {
|
||||
self.table_addr
|
||||
}
|
||||
|
||||
pub fn set_table(&mut self, address: usize) {
|
||||
self.table_addr = address;
|
||||
self.invalidate_all();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PageEntry(usize);
|
||||
|
||||
impl PageEntry {
|
||||
pub fn address(&self) -> PhysicalAddress {
|
||||
PhysicalAddress(self.0 & ENTRY_ADDRESS_MASK)
|
||||
}
|
||||
|
||||
pub fn present(&self) -> bool {
|
||||
self.0 & ENTRY_PRESENT != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageTable {
|
||||
base: VirtualAddress,
|
||||
phys: PhysicalAddress,
|
||||
level: usize
|
||||
}
|
||||
|
||||
impl PageTable {
|
||||
pub unsafe fn new(base: VirtualAddress, phys: PhysicalAddress, level: usize) -> Self {
|
||||
Self { base, phys, level }
|
||||
}
|
||||
|
||||
pub unsafe fn top() -> Self {
|
||||
Self::new(
|
||||
VirtualAddress::new(0),
|
||||
PhysicalAddress::new(arch_get_table()),
|
||||
PAGE_LEVELS - 1
|
||||
)
|
||||
}
|
||||
|
||||
pub fn base(&self) -> VirtualAddress {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn phys(&self) -> PhysicalAddress {
|
||||
self.phys
|
||||
}
|
||||
|
||||
pub fn level(&self) -> usize {
|
||||
self.level
|
||||
}
|
||||
|
||||
pub unsafe fn virt(&self) -> VirtualAddress {
|
||||
// Recursive mapping
|
||||
let mut addr = 0xFFFF_FFFF_FFFF_F000;
|
||||
for level in (self.level + 1 .. PAGE_LEVELS).rev() {
|
||||
let index = (self.base.0 >> (level * PAGE_ENTRY_SHIFT + PAGE_SHIFT)) & PAGE_ENTRY_MASK;
|
||||
addr <<= PAGE_ENTRY_SHIFT;
|
||||
addr |= index << PAGE_SHIFT;
|
||||
}
|
||||
VirtualAddress(addr)
|
||||
|
||||
// Identity mapping
|
||||
//VirtualAddress(self.phys.0)
|
||||
}
|
||||
|
||||
pub fn entry_base(&self, i: usize) -> Option<VirtualAddress> {
|
||||
if i < PAGE_ENTRIES {
|
||||
Some(VirtualAddress(
|
||||
self.base.0 + (i << (self.level * PAGE_ENTRY_SHIFT + PAGE_SHIFT))
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn entry_virt(&self, i: usize) -> Option<VirtualAddress> {
|
||||
if i < PAGE_ENTRIES {
|
||||
Some(VirtualAddress(
|
||||
self.virt().0 + i * PAGE_ENTRY_SIZE
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn entry(&self, i: usize) -> Option<PageEntry> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
Some(PageEntry(arch_read::<usize>(addr.0)))
|
||||
}
|
||||
|
||||
pub unsafe fn set_entry(&mut self, i: usize, entry: PageEntry) -> Option<()> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
arch_write::<usize>(addr.0, entry.0);
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub unsafe fn next(&self, i: usize) -> Option<Self> {
|
||||
if self.level > 0 {
|
||||
let entry = self.entry(i)?;
|
||||
if entry.present() {
|
||||
return Some(PageTable::new(
|
||||
self.entry_base(i)?,
|
||||
entry.address(),
|
||||
self.level - 1
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dump_tables(table: PageTable) {
|
||||
unsafe fn dump_tables<A: Arch>(table: PageTable<A>) {
|
||||
let level = table.level();
|
||||
for i in 0..PAGE_ENTRIES {
|
||||
if level == 0 {
|
||||
if let Some(entry) = table.entry(i) {
|
||||
if entry.present() {
|
||||
let base = table.entry_base(i).unwrap();
|
||||
println!("0x{:X}: 0x{:X}", base.0, entry.address().0);
|
||||
println!("0x{:X}: 0x{:X}", base.data(), entry.address().data());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -375,67 +18,26 @@ unsafe fn dump_tables(table: PageTable) {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemoryArea {
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
unsafe fn inner<A: Arch>() {
|
||||
let areas = A::init();
|
||||
|
||||
// Debug table
|
||||
dump_tables(PageTable::<A>::top());
|
||||
|
||||
let megabyte = 0x100000;
|
||||
|
||||
// Test read
|
||||
println!("0x{:X} = 0x{:X}", megabyte, A::read::<u8>(megabyte));
|
||||
|
||||
// Test write
|
||||
A::write::<u8>(megabyte, 0x5A);
|
||||
|
||||
// Test read
|
||||
println!("0x{:X} = 0x{:X}", megabyte, A::read::<u8>(megabyte));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let memory_size = 64 * 1024 * 1024;
|
||||
|
||||
unsafe {
|
||||
let megabyte = 0x100000;
|
||||
|
||||
// Create machine with PAGE_ENTRIES pages identity mapped (2 MiB on x86_64)
|
||||
// Pages over 1 MiB will be mapped writable
|
||||
{
|
||||
let mut machine = Machine::new(memory_size);
|
||||
|
||||
// PML4 link to PDP
|
||||
let pml4 = 0;
|
||||
let pdp = pml4 + PAGE_SIZE;
|
||||
let flags = ENTRY_WRITABLE | ENTRY_PRESENT;
|
||||
machine.write_phys::<usize>(pml4, pdp | flags);
|
||||
|
||||
// Recursive mapping
|
||||
machine.write_phys::<usize>(pml4 + (PAGE_ENTRIES - 1) * PAGE_ENTRY_SIZE, pml4 | flags);
|
||||
|
||||
// PDP link to PD
|
||||
let pd = pdp + PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pdp, pd | flags);
|
||||
|
||||
// PD link to PT
|
||||
let pt = pd + PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pd, pt | flags);
|
||||
|
||||
// PT links to frames
|
||||
for i in 0..PAGE_ENTRIES {
|
||||
let page = i * PAGE_SIZE;
|
||||
machine.write_phys::<usize>(pt + i * PAGE_ENTRY_SIZE, page | flags);
|
||||
}
|
||||
|
||||
MACHINE = Some(machine);
|
||||
|
||||
// Set table to pml4
|
||||
arch_set_table(pml4);
|
||||
}
|
||||
|
||||
// Debug table
|
||||
dump_tables(PageTable::top());
|
||||
|
||||
// Test read
|
||||
println!("0x{:X} = 0x{:X}", megabyte, arch_read::<u8>(megabyte));
|
||||
|
||||
// Test write
|
||||
arch_write::<u8>(megabyte, 0x5A);
|
||||
|
||||
// Test read
|
||||
println!("0x{:X} = 0x{:X}", megabyte, arch_read::<u8>(megabyte));
|
||||
|
||||
// Initialize memory allocator
|
||||
let areas = [MemoryArea {
|
||||
base: PhysicalAddress::new(megabyte),
|
||||
size: megabyte,
|
||||
}];
|
||||
inner::<EmulateArch>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::{
|
||||
PhysicalAddress,
|
||||
ENTRY_ADDRESS_MASK,
|
||||
ENTRY_FLAG_PRESENT,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PageEntry(usize);
|
||||
|
||||
impl PageEntry {
|
||||
pub unsafe fn new(data: usize) -> Self {
|
||||
Self(data)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn address(&self) -> PhysicalAddress {
|
||||
PhysicalAddress(self.0 & ENTRY_ADDRESS_MASK)
|
||||
}
|
||||
|
||||
pub fn present(&self) -> bool {
|
||||
self.0 & ENTRY_FLAG_PRESENT != 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
pub use self::{
|
||||
entry::PageEntry,
|
||||
table::PageTable,
|
||||
};
|
||||
|
||||
mod entry;
|
||||
mod table;
|
||||
@@ -0,0 +1,106 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
Arch,
|
||||
PhysicalAddress,
|
||||
VirtualAddress,
|
||||
PAGE_ENTRIES,
|
||||
PAGE_ENTRY_MASK,
|
||||
PAGE_ENTRY_SHIFT,
|
||||
PAGE_ENTRY_SIZE,
|
||||
PAGE_LEVELS,
|
||||
PAGE_SHIFT,
|
||||
};
|
||||
use super::PageEntry;
|
||||
|
||||
pub struct PageTable<A> {
|
||||
base: VirtualAddress,
|
||||
phys: PhysicalAddress,
|
||||
level: usize,
|
||||
phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: Arch> PageTable<A> {
|
||||
pub unsafe fn new(base: VirtualAddress, phys: PhysicalAddress, level: usize) -> Self {
|
||||
Self { base, phys, level, phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub unsafe fn top() -> Self {
|
||||
Self::new(
|
||||
VirtualAddress::new(0),
|
||||
PhysicalAddress::new(A::table()),
|
||||
PAGE_LEVELS - 1
|
||||
)
|
||||
}
|
||||
|
||||
pub fn base(&self) -> VirtualAddress {
|
||||
self.base
|
||||
}
|
||||
|
||||
pub fn phys(&self) -> PhysicalAddress {
|
||||
self.phys
|
||||
}
|
||||
|
||||
pub fn level(&self) -> usize {
|
||||
self.level
|
||||
}
|
||||
|
||||
pub unsafe fn virt(&self) -> VirtualAddress {
|
||||
// Recursive mapping
|
||||
let mut addr = 0xFFFF_FFFF_FFFF_F000;
|
||||
for level in (self.level + 1 .. PAGE_LEVELS).rev() {
|
||||
let index = (self.base.0 >> (level * PAGE_ENTRY_SHIFT + PAGE_SHIFT)) & PAGE_ENTRY_MASK;
|
||||
addr <<= PAGE_ENTRY_SHIFT;
|
||||
addr |= index << PAGE_SHIFT;
|
||||
}
|
||||
VirtualAddress(addr)
|
||||
|
||||
// Identity mapping
|
||||
//VirtualAddress(self.phys.0)
|
||||
}
|
||||
|
||||
pub fn entry_base(&self, i: usize) -> Option<VirtualAddress> {
|
||||
if i < PAGE_ENTRIES {
|
||||
Some(VirtualAddress(
|
||||
self.base.0 + (i << (self.level * PAGE_ENTRY_SHIFT + PAGE_SHIFT))
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn entry_virt(&self, i: usize) -> Option<VirtualAddress> {
|
||||
if i < PAGE_ENTRIES {
|
||||
Some(VirtualAddress(
|
||||
self.virt().0 + i * PAGE_ENTRY_SIZE
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn entry(&self, i: usize) -> Option<PageEntry> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
Some(PageEntry::new(A::read::<usize>(addr.0)))
|
||||
}
|
||||
|
||||
pub unsafe fn set_entry(&mut self, i: usize, entry: PageEntry) -> Option<()> {
|
||||
let addr = self.entry_virt(i)?;
|
||||
A::write::<usize>(addr.0, entry.data());
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub unsafe fn next(&self, i: usize) -> Option<Self> {
|
||||
if self.level > 0 {
|
||||
let entry = self.entry(i)?;
|
||||
if entry.present() {
|
||||
return Some(PageTable::new(
|
||||
self.entry_base(i)?,
|
||||
entry.address(),
|
||||
self.level - 1
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user