Compare commits

...

5 Commits

23 changed files with 506 additions and 44 deletions

5
kernel/Cargo.lock generated
View File

@ -75,7 +75,6 @@ dependencies = [
"spin 0.9.8", "spin 0.9.8",
"static_assertions", "static_assertions",
"uart_16550", "uart_16550",
"vspace",
] ]
[[package]] [[package]]
@ -236,10 +235,6 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "vspace"
version = "0.1.0"
[[package]] [[package]]
name = "x86" name = "x86"
version = "0.52.0" version = "0.52.0"

View File

@ -6,13 +6,20 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
default = ["arch_riscv64", "board_virt", "log_color"] default = ["riscv.board.virt", "log_color"]
arch_riscv64 = [] riscv = []
arch_riscv32 = []
board_default = [] "riscv.pagetable.sv32" = []
board_virt = [] "riscv.pagetable.sv39" = []
"riscv.pagetable.sv48" = []
"riscv.pagetable.sv57" = []
"riscv.riscv64" = ["riscv", "riscv.pagetable.sv39"]
"riscv.riscv32" = ["riscv", "riscv.pagetable.sv32"]
"riscv.board.default" = ["riscv.riscv64"]
"riscv.board.virt" = ["riscv.riscv64"]
log_color = [] log_color = []
@ -25,17 +32,18 @@ lto = "thin"
[dependencies] [dependencies]
api = { path = "../api" } api = { path = "../api" }
vspace = { path = "../lib/vspace" }
bitflags = "2.4.2" bitflags = "2.4"
cfg-if = "1.0.0" cfg-if = "1.0"
fdt = "0.1" fdt = "0.1"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] } lazy_static = { version = "1.4", features = ["spin_no_std"] }
log = "0.4" log = "0.4"
num-derive = "0.4" num-derive = "0.4"
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
riscv = { version = "0.11.1", features = ["s-mode"] } spin = "0.9"
sbi-rt = { version = "0.0.3" } static_assertions = "1.1"
spin = "0.9.8"
static_assertions = "1.1.0"
uart_16550 = "0.3" uart_16550 = "0.3"
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { version = "0.11", features = ["s-mode"] }
sbi-rt = { version = "0.0" }

View File

@ -1,7 +1,7 @@
pub use arch::*; pub use arch::*;
// Arch Level // Arch Level
#[cfg(any(feature = "arch_riscv64", feature = "arch_riscv32"))] #[cfg(feature = "riscv")]
#[path = "riscv/mod.rs"] #[path = "riscv/mod.rs"]
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod arch; mod arch;

View File

@ -1,4 +1,4 @@
use log::{error, warn}; use log::error;
use crate::plat::lowlevel::{Hardware, LowLevel}; use crate::plat::lowlevel::{Hardware, LowLevel};

View File

@ -1,10 +1,11 @@
use cfg_if::cfg_if; use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "board_virt")] { if #[cfg(feature = "riscv.board.virt")] {
#[path = "board/virt/mod.rs"] #[path = "board/virt/mod.rs"]
mod board; mod board;
} else { } else {
// fall back to default
#[path = "board/default/mod.rs"] #[path = "board/default/mod.rs"]
mod board; mod board;
} }
@ -19,3 +20,4 @@ mod lowlevel;
mod timer; mod timer;
mod tls; mod tls;
pub mod trap; pub mod trap;
pub mod vspace;

View File

@ -9,9 +9,9 @@ use crate::plat::timer::{Timer, TimerOps};
use crate::plat::trap::{Trap, TrapContextOps, TrapOps}; use crate::plat::trap::{Trap, TrapContextOps, TrapOps};
cfg_if! { cfg_if! {
if #[cfg(feature = "arch_riscv64")] { if #[cfg(feature = "riscv.riscv64")] {
core::arch::global_asm!(include_str!("./asm/trap64.S")); core::arch::global_asm!(include_str!("./asm/trap64.S"));
} else if #[cfg(feature = "arch_riscv32")] { } else if #[cfg(feature = "riscv.riscv32")] {
core::arch::global_asm!(include_str!("./asm/trap32.S")); core::arch::global_asm!(include_str!("./asm/trap32.S"));
} }
} }

View File

@ -0,0 +1,164 @@
use crate::vspace::addr::{AddressOps, PhysAddr};
use crate::vspace::paging::{EntryOps, MapAttr};
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
pub struct PTEFlags : usize {
const VALID = 1 << 0;
const READABLE = 1 << 1;
const WRITABLE = 1 << 2;
const EXECUTABLE = 1 << 3;
const USER_ACCESSIBLE = 1 << 4;
const GLOBAL = 1 << 5;
const ACCESSED = 1 << 6;
const DIRTY = 1 << 7;
const RSW = 1 << 8 | 1 << 9;
}
}
impl From<PTEFlags> for MapAttr {
fn from(flags: PTEFlags) -> Self {
let mut attr = Self::empty();
if flags.contains(PTEFlags::READABLE) {
attr.insert(Self::READABLE);
}
if flags.contains(PTEFlags::WRITABLE) {
attr.insert(Self::WRITABLE);
}
if flags.contains(PTEFlags::EXECUTABLE) {
attr.insert(Self::EXECUTABLE);
}
if flags.contains(PTEFlags::USER_ACCESSIBLE) {
attr.insert(Self::USER_ACCESSIBLE);
}
if flags.contains(PTEFlags::VALID)
&& !flags.contains(PTEFlags::READABLE | PTEFlags::WRITABLE | PTEFlags::EXECUTABLE)
{
attr.insert(Self::PAGE_TABLE);
}
attr
}
}
impl From<MapAttr> for PTEFlags {
fn from(attr: MapAttr) -> Self {
if attr.is_empty() {
return Self::empty();
}
let mut flags = Self::VALID;
if attr.contains(MapAttr::USER_ACCESSIBLE) {
flags.insert(Self::USER_ACCESSIBLE);
}
if attr.contains(MapAttr::PAGE_TABLE) {
// PAGE_TABLE <=> RWX.empty()
return flags;
}
if attr.contains(MapAttr::READABLE) {
flags.insert(Self::READABLE);
}
if attr.contains(MapAttr::WRITABLE) {
flags.insert(Self::WRITABLE);
}
if attr.contains(MapAttr::EXECUTABLE) {
flags.insert(Self::EXECUTABLE);
}
flags
}
}
#[cfg(not(feature = "riscv.pagetable.sv32"))]
assert_eq_size!(Entry, u64);
#[cfg(feature = "riscv.pagetable.sv32")]
assert_eq_size!(Entry, u32);
#[cfg(feature = "riscv.pagetable.sv39")]
impl PhysAddr {
const PA_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PG_OFFSET;
const PG_OFFSET: usize = 12;
const PPN_BITS: usize = 44;
const PPN_OFFSET: usize = 10;
const PTE_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PPN_OFFSET;
}
impl PhysAddr {
fn to_ppn_shifted(self) -> usize {
((self.as_usize() & Self::PA_PPN_MASK) >> Self::PG_OFFSET) << Self::PPN_OFFSET
}
fn from_pte(pte: usize) -> Self {
let ppn = (pte & Self::PTE_PPN_MASK) >> Self::PPN_OFFSET;
let paddr = ppn << Self::PG_OFFSET;
PhysAddr::from(paddr)
}
fn merge_pte(self, pte: usize) -> usize {
let ppn = self.to_ppn_shifted();
(pte & !Self::PTE_PPN_MASK) | ppn
}
}
#[derive(Clone, Copy, Default)]
pub struct Entry(usize);
impl EntryOps for Entry {
fn new_page(phys_addr: PhysAddr, attr: MapAttr) -> Self {
let flags = PTEFlags::from(attr);
let ppn = phys_addr.to_ppn_shifted();
Self(ppn | flags.bits())
}
fn new_table(phys_addr: PhysAddr) -> Self {
let flags = PTEFlags::VALID;
let ppn = phys_addr.to_ppn_shifted();
Self(ppn | flags.bits())
}
fn addr(&self) -> PhysAddr {
PhysAddr::from_pte(self.0)
}
fn attr(&self) -> MapAttr {
let flags = PTEFlags::from_bits_truncate(self.0);
flags.into()
}
fn set_addr(&mut self, addr: PhysAddr) {
self.0 = addr.merge_pte(self.0);
}
fn set_attr(&mut self, attr: MapAttr) {
let flags = PTEFlags::from(attr);
self.0 = (self.0 & !PTEFlags::all().bits()) | flags.bits();
}
fn is_valid(&self) -> bool {
self.0 & PTEFlags::VALID.bits() != 0
}
fn is_leaf(&self) -> bool {
let flags = PTEFlags::from_bits_truncate(self.0);
let valid = flags.contains(PTEFlags::VALID);
let rwx = flags & (PTEFlags::READABLE | PTEFlags::WRITABLE | PTEFlags::EXECUTABLE);
valid && rwx.bits() != 0
}
}
impl core::fmt::Debug for Entry {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PageTableEntry")
.field("addr", &self.addr())
.field("flag", &PTEFlags::from_bits_truncate(self.0))
.finish()
}
}

View File

@ -0,0 +1,6 @@
mod entry;
mod table;
mod utils;
pub use entry::Entry;
pub use table::Table;

View File

@ -0,0 +1,196 @@
use crate::utils::size::*;
use crate::vspace::addr::*;
use crate::vspace::paging::*;
use num_traits::ToPrimitive;
const PAGE_SIZE: usize = 4096;
#[cfg(feature = "riscv.pagetable.sv39")]
impl VirtAddr {
const PG_OFFSET: usize = 12;
const VPN_BITS: usize = 9;
const VPN_MASK: usize = (1 << Self::VPN_BITS) - 1;
}
impl VirtAddr {
fn to_vpn(self, level: TableLevel) -> usize {
self.0 >> (Self::PG_OFFSET + Self::VPN_BITS * level.to_usize().unwrap()) & Self::VPN_MASK
}
fn merge_vpn(&self, vpn: usize, size: TableLevel) -> Self {
let shift = Self::PG_OFFSET + Self::VPN_BITS * size.to_usize().unwrap();
let mask = Self::VPN_MASK << shift;
VirtAddr((self.0 & !mask) | ((vpn & Self::VPN_MASK) << shift))
}
fn lower_bits(self, level: usize) -> usize {
self.0 & ((1 << (Self::PG_OFFSET + Self::VPN_BITS * (level + 1))) - 1)
}
}
impl TableLevel {
pub fn is_aligned<A: AddressOps>(&self, addr: A) -> bool {
match self {
Self::Level0 => addr.is_aligned(4 * KIB),
#[cfg(feature = "riscv.pagetable.sv32")]
Self::Level1 => addr.is_aligned(4 * MIB),
#[cfg(not(feature = "riscv.pagetable.sv32"))]
Self::Level1 => addr.is_aligned(2 * MIB),
Self::Level2 => addr.is_aligned(1 * GIB),
Self::Level3 => addr.is_aligned(512 * GIB),
Self::Level4 => addr.is_aligned(256 * TIB),
}
}
}
#[repr(C, align(4096))]
pub struct Table {
entries: [Entry; PAGE_SIZE / core::mem::size_of::<Entry>()],
}
assert_eq_size!(Table, [u8; PAGE_SIZE]);
impl Table {
fn lookup_mut_internal(&mut self, vaddr: VirtAddr) -> (&mut Entry, TableLevel) {
// NOTE: we assume that this page table is the root page table
let mut cur = Self::MAX_PAGE_SIZE;
let mut table = self;
loop {
let vpn = vaddr.to_vpn(cur);
let entry = table.entries[vpn];
if !entry.is_valid() || entry.is_leaf() || cur.next().is_none() {
return (&mut table.entries[vpn], cur);
}
cur = cur.next().unwrap();
table = unsafe {
// NOTE: we assume that kernel space is 1:1 mapped
Self::new(entry.addr().as_usize().into())
};
}
}
}
impl TableOps for Table {
type Entry = Entry;
#[cfg(feature = "riscv.pagetable.sv39")]
const MAX_PAGE_SIZE: TableLevel = TableLevel::Level2;
unsafe fn new(location: PhysAddr) -> &'static mut Self {
assert!(location.is_aligned(PAGE_SIZE));
let ptr: *mut Self = location.into();
&mut *ptr
}
fn map(
&mut self,
from: VirtAddr,
to: PhysAddr,
attr: MapAttr,
level: TableLevel,
) -> PageResult {
assert!(from.is_aligned(PAGE_SIZE));
assert!(to.is_aligned(PAGE_SIZE));
assert!(level.is_aligned(from));
if !attr.contains(MapAttr::PAGE_TABLE) {
assert!(level.is_aligned(to));
}
let (entry, cur) = self.lookup_mut_internal(from);
if cur < level {
return Err(PageError::MissingEntry(cur));
}
if entry.is_valid() || cur > level {
return Err(PageError::AlreadyMapped(cur));
}
entry.set_addr(to);
entry.set_attr(attr);
Ok(())
}
fn unmap(&mut self, vaddr: VirtAddr) -> PageResult {
let (entry, level) = self.lookup_mut_internal(vaddr);
if !entry.is_valid() {
return Err(PageError::MissingEntry(level));
}
entry.set_addr(PhysAddr::default());
entry.set_attr(MapAttr::empty());
Ok(())
}
fn lookup(&mut self, vaddr: VirtAddr) -> Option<&Self::Entry> {
let (entry, _) = self.lookup_mut_internal(vaddr);
entry.is_valid().then_some(entry)
}
fn lookup_mut(&mut self, vaddr: VirtAddr) -> Option<&mut Self::Entry> {
let (entry, _) = self.lookup_mut_internal(vaddr);
entry.is_valid().then_some(entry)
}
fn translate(&mut self, vaddr: VirtAddr) -> Option<PhysAddr> {
let (entry, level) = self.lookup_mut_internal(vaddr);
entry
.is_valid()
.then_some(entry.addr())
.map(|p| p.as_usize() | vaddr.lower_bits(level.to_usize().unwrap()))
.map(|p| p.into())
}
}
impl Table {
fn debug_walk(
&self,
f: &mut core::fmt::Formatter,
base: VirtAddr,
level: TableLevel,
) -> core::fmt::Result {
macro_rules! print_one {
($($arg:tt)*) => {
for _ in level.to_usize().unwrap()..Self::MAX_PAGE_SIZE.to_usize().unwrap() {
write!(f, "\t")?;
}
writeln!(f, $($arg)*)?;
};
}
for (i, entry) in self.entries.into_iter().enumerate() {
if !entry.is_valid() {
continue;
}
print_one!(
"[{:>3}]: {:?} -> {:?} : {:?}",
i,
base.merge_vpn(i, level),
entry.addr(),
entry
);
if !entry.is_leaf() && level.next().is_some() {
let table = unsafe { Self::new(entry.addr().as_usize().into()) };
table.debug_walk(f, base.merge_vpn(i, level), level.next().unwrap())?;
}
}
Ok(())
}
}
impl core::fmt::Debug for Table {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
writeln!(f, "PageTable({:p}):", self)?;
self.debug_walk(f, VirtAddr(0), Self::MAX_PAGE_SIZE)
}
}

View File

@ -0,0 +1 @@

View File

@ -14,8 +14,12 @@
#![reexport_test_harness_main = "test_main"] #![reexport_test_harness_main = "test_main"]
#![cfg_attr(test, allow(dead_code))] #![cfg_attr(test, allow(dead_code))]
#[macro_use]
extern crate static_assertions; extern crate static_assertions;
#[macro_use]
extern crate num_derive;
mod arch; mod arch;
mod drivers; mod drivers;
mod entry; mod entry;
@ -24,6 +28,7 @@ mod logging;
mod objects; mod objects;
mod plat; mod plat;
mod utils; mod utils;
mod vspace;
// test infrastructure // test infrastructure
#[cfg(test)] #[cfg(test)]

View File

@ -1,4 +1,4 @@
use vspace::addr::PhysAddr; use crate::vspace::addr::PhysAddr;
extern "C" { extern "C" {
pub type ExternSymbol; pub type ExternSymbol;

View File

@ -1,2 +1,3 @@
pub mod extern_addr; pub mod extern_addr;
pub mod function_name; pub mod function_name;
pub mod size;

12
kernel/src/utils/size.rs Normal file
View File

@ -0,0 +1,12 @@
pub const B: usize = 1;
pub const KB: usize = B * 1000;
pub const MB: usize = KB * 1000;
pub const GB: usize = MB * 1000;
pub const TB: usize = GB * 1000;
pub const PB: usize = TB * 1000;
pub const KIB: usize = B * 1024;
pub const MIB: usize = KIB * 1024;
pub const GIB: usize = MIB * 1024;
pub const TIB: usize = GIB * 1024;
pub const PIB: usize = TIB * 1024;

View File

@ -13,10 +13,10 @@ pub fn align_down(addr: usize, align: usize) -> usize {
addr & !(align - 1) addr & !(align - 1)
} }
#[derive(Copy, Clone, PartialOrd, PartialEq)] #[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
pub struct PhysAddr(pub usize); pub struct PhysAddr(pub usize);
#[derive(Copy, Clone, PartialOrd, PartialEq)] #[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
pub struct VirtAddr(pub usize); pub struct VirtAddr(pub usize);
pub trait AddressOps { pub trait AddressOps {

2
kernel/src/vspace/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod addr;
pub mod paging;

View File

@ -0,0 +1,28 @@
use crate::vspace::addr::PhysAddr;
use bitflags::bitflags;
use core::fmt::Debug;
bitflags! {
#[derive(Debug, Copy, Clone)]
pub struct MapAttr: usize {
const PAGE_TABLE = 1 << 0;
const READABLE = 1 << 1;
const WRITABLE = 1 << 2;
const EXECUTABLE = 1 << 3;
const USER_ACCESSIBLE = 1 << 4;
}
}
pub trait EntryOps: Clone + Copy + Debug {
fn new_page(phys_addr: PhysAddr, attr: MapAttr) -> Self;
fn new_table(phys_addr: PhysAddr) -> Self;
fn addr(&self) -> PhysAddr;
fn attr(&self) -> MapAttr;
fn set_addr(&mut self, addr: PhysAddr);
fn set_attr(&mut self, attr: MapAttr);
fn is_valid(&self) -> bool;
fn is_leaf(&self) -> bool;
}

View File

@ -0,0 +1,9 @@
mod entry;
mod table;
pub use crate::arch::vspace::*;
pub use entry::*;
pub use table::*;
assert_impl_all!(Entry: EntryOps);
assert_impl_all!(Table: TableOps);

View File

@ -0,0 +1,48 @@
use super::{EntryOps, MapAttr};
use crate::vspace::addr::{PhysAddr, VirtAddr};
use core::fmt::Debug;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, FromPrimitive, ToPrimitive)]
pub enum TableLevel {
Level0 = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
}
impl TableLevel {
pub fn next(&self) -> Option<Self> {
match self {
Self::Level0 => None,
Self::Level1 => Some(Self::Level0),
Self::Level2 => Some(Self::Level1),
Self::Level3 => Some(Self::Level2),
Self::Level4 => Some(Self::Level3),
}
}
}
#[derive(Debug)]
pub enum PageError {
AlreadyMapped(TableLevel),
MissingEntry(TableLevel),
}
pub type PageResult<T = ()> = Result<T, PageError>;
pub trait TableOps: Debug {
type Entry: EntryOps;
const MAX_PAGE_SIZE: TableLevel;
unsafe fn new(location: PhysAddr) -> &'static mut Self;
fn map(&mut self, from: VirtAddr, to: PhysAddr, attr: MapAttr, level: TableLevel)
-> PageResult;
fn unmap(&mut self, vaddr: VirtAddr) -> PageResult;
fn lookup(&mut self, vaddr: VirtAddr) -> Option<&Self::Entry>;
fn lookup_mut(&mut self, vaddr: VirtAddr) -> Option<&mut Self::Entry>;
fn translate(&mut self, vaddr: VirtAddr) -> Option<PhysAddr>;
}

View File

@ -1,7 +1,8 @@
#![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_std)]
// reference: https://man.archlinux.org/man/cpio.5.en#New_ASCII_Format // Reference:
// Reference: https://github.com/jcreekmore/cpio-rs/blob/master/src/newc.rs // - https://man.archlinux.org/man/cpio.5.en#New_ASCII_Format
// - https://github.com/jcreekmore/cpio-rs/blob/master/src/newc.rs
const HEADER_LEN: usize = 110; const HEADER_LEN: usize = 110;
const MAGIC_NEW_ASCII: [u8; 6] = *b"070701"; const MAGIC_NEW_ASCII: [u8; 6] = *b"070701";

7
lib/vspace/Cargo.lock generated
View File

@ -1,7 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "vspace"
version = "0.1.0"

View File

@ -1,6 +0,0 @@
[package]
name = "vspace"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -1,3 +0,0 @@
#![no_std]
pub mod addr;