Compare commits

..

5 Commits

27 changed files with 212 additions and 213 deletions

8
kernel/Cargo.lock generated
View File

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

View File

@ -6,14 +6,20 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["arch_riscv64", "board_virt", "log_color"]
default = ["riscv.board.virt", "log_color"]
arch_riscv64 = ["vspace/riscv_sv39"]
# TODO: riscv32 not supported yet
arch_riscv32 = []
riscv = []
board_default = []
board_virt = []
"riscv.pagetable.sv32" = []
"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 = []
@ -26,7 +32,6 @@ lto = "thin"
[dependencies]
api = { path = "../api" }
vspace = { path = "../lib/vspace", default-features = false }
bitflags = "2.4"
cfg-if = "1.0"
@ -35,8 +40,10 @@ lazy_static = { version = "1.4", features = ["spin_no_std"] }
log = "0.4"
num-derive = "0.4"
num-traits = { version = "0.2", default-features = false }
riscv = { version = "0.11", features = ["s-mode"] }
sbi-rt = { version = "0.0" }
spin = "0.9"
static_assertions = "1.1"
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::*;
// Arch Level
#[cfg(any(feature = "arch_riscv64", feature = "arch_riscv32"))]
#[cfg(feature = "riscv")]
#[path = "riscv/mod.rs"]
#[allow(clippy::module_inception)]
mod arch;

View File

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

View File

@ -1,10 +1,11 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "board_virt")] {
if #[cfg(feature = "riscv.board.virt")] {
#[path = "board/virt/mod.rs"]
mod board;
} else {
// fall back to default
#[path = "board/default/mod.rs"]
mod board;
}
@ -19,3 +20,4 @@ mod lowlevel;
mod timer;
mod tls;
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};
cfg_if! {
if #[cfg(feature = "arch_riscv64")] {
if #[cfg(feature = "riscv.riscv64")] {
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"));
}
}

View File

@ -1,10 +1,10 @@
use crate::addr::{AddressOps, PhysAddr};
use crate::paging::{MapAttr, PageTableEntryOps};
use crate::vspace::addr::{AddressOps, PhysAddr};
use crate::vspace::paging::{EntryOps, MapAttr};
use bitflags::bitflags;
bitflags! {
#[derive(Debug)]
pub struct PTEFlags : u64 {
pub struct PTEFlags : usize {
const VALID = 1 << 0;
const READABLE = 1 << 1;
const WRITABLE = 1 << 2;
@ -73,46 +73,52 @@ impl From<MapAttr> for PTEFlags {
}
}
#[cfg(feature = "riscv_sv39")]
#[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: u64 = ((1 << Self::PPN_BITS) - 1) << Self::PG_OFFSET;
const PG_OFFSET: u64 = 12;
const PPN_BITS: u64 = 44;
const PPN_OFFSET: u64 = 10;
const PTE_PPN_MASK: u64 = ((1 << Self::PPN_BITS) - 1) << Self::PPN_OFFSET;
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) -> u64 {
((self.as_u64() & Self::PA_PPN_MASK) >> Self::PG_OFFSET) << Self::PPN_OFFSET
fn to_ppn_shifted(self) -> usize {
((self.as_usize() & Self::PA_PPN_MASK) >> Self::PG_OFFSET) << Self::PPN_OFFSET
}
fn from_pte(pte: u64) -> Self {
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 as usize)
PhysAddr::from(paddr)
}
fn merge_pte(self, pte: u64) -> u64 {
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 PageTableEntry(u64);
pub struct Entry(usize);
impl PageTableEntryOps for PageTableEntry {
fn new_page(paddr: PhysAddr, attr: MapAttr) -> Self {
impl EntryOps for Entry {
fn new_page(phys_addr: PhysAddr, attr: MapAttr) -> Self {
let flags = PTEFlags::from(attr);
let ppn = paddr.to_ppn_shifted();
let ppn = phys_addr.to_ppn_shifted();
Self(ppn | flags.bits())
}
fn new_table(paddr: PhysAddr) -> Self {
fn new_table(phys_addr: PhysAddr) -> Self {
let flags = PTEFlags::VALID;
let ppn = paddr.to_ppn_shifted();
let ppn = phys_addr.to_ppn_shifted();
Self(ppn | flags.bits())
}
@ -148,7 +154,7 @@ impl PageTableEntryOps for PageTableEntry {
}
}
impl core::fmt::Debug for PageTableEntry {
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())

View File

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

View File

@ -1,11 +1,11 @@
use crate::addr::{AddressOps, PhysAddr, VirtAddr};
use crate::paging::{
MapAttr, PageError, PageResult, PageSize, PageTableEntry, PageTableEntryOps, PageTableOps,
};
use crate::utils::size::*;
use crate::vspace::addr::*;
use crate::vspace::paging::*;
use num_traits::ToPrimitive;
const PAGE_SIZE: usize = 4096;
#[cfg(feature = "riscv_sv39")]
#[cfg(feature = "riscv.pagetable.sv39")]
impl VirtAddr {
const PG_OFFSET: usize = 12;
const VPN_BITS: usize = 9;
@ -13,14 +13,12 @@ impl VirtAddr {
}
impl VirtAddr {
fn to_vpn(self, size: PageSize) -> usize {
let level = size.to_level();
assert!(level <= 3, "invalid level: {}", level);
self.0 >> (Self::PG_OFFSET + Self::VPN_BITS * level) & Self::VPN_MASK
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: PageSize) -> Self {
let shift = Self::PG_OFFSET + Self::VPN_BITS * size.to_level();
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))
}
@ -30,14 +28,30 @@ impl VirtAddr {
}
}
#[repr(C, align(4096))]
pub struct PageTable {
// Assume at least SV39 paging
entries: [PageTableEntry; 512],
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),
}
}
}
impl PageTable {
fn lookup_mut_internal(&mut self, vaddr: VirtAddr) -> (&mut PageTableEntry, PageSize) {
#[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;
@ -53,39 +67,46 @@ impl PageTable {
cur = cur.next().unwrap();
table = unsafe {
// NOTE: we assume that kernel space is 1:1 mapped
Self::from_va(entry.addr().as_usize().into())
Self::new(entry.addr().as_usize().into())
};
}
}
}
impl PageTableOps for PageTable {
type Entry = PageTableEntry;
impl TableOps for Table {
type Entry = Entry;
#[cfg(feature = "riscv_sv39")]
const MAX_PAGE_SIZE: PageSize = PageSize::Giga;
#[cfg(feature = "riscv.pagetable.sv39")]
const MAX_PAGE_SIZE: TableLevel = TableLevel::Level2;
unsafe fn from_va(from: VirtAddr) -> &'static mut Self {
let ptr: *mut Self = from.into();
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, size: PageSize) -> PageResult {
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!(size.is_aligned(from.as_usize()));
assert!(level.is_aligned(from));
if !attr.contains(MapAttr::PAGE_TABLE) {
assert!(size.is_aligned(to.as_usize()));
assert!(level.is_aligned(to));
}
let (entry, cur) = self.lookup_mut_internal(from);
if cur.to_level() < size.to_level() {
return Err(PageError::MissingEntry);
if cur < level {
return Err(PageError::MissingEntry(cur));
}
if entry.is_valid() || cur.to_level() > size.to_level() {
return Err(PageError::AlreadyMapped);
if entry.is_valid() || cur > level {
return Err(PageError::AlreadyMapped(cur));
}
entry.set_addr(to);
@ -95,10 +116,10 @@ impl PageTableOps for PageTable {
}
fn unmap(&mut self, vaddr: VirtAddr) -> PageResult {
let (entry, _) = self.lookup_mut_internal(vaddr);
let (entry, level) = self.lookup_mut_internal(vaddr);
if !entry.is_valid() {
return Err(PageError::MissingEntry);
return Err(PageError::MissingEntry(level));
}
entry.set_addr(PhysAddr::default());
@ -107,9 +128,9 @@ impl PageTableOps for PageTable {
Ok(())
}
fn lookup(&mut self, vaddr: VirtAddr) -> Option<Self::Entry> {
fn lookup(&mut self, vaddr: VirtAddr) -> Option<&Self::Entry> {
let (entry, _) = self.lookup_mut_internal(vaddr);
entry.is_valid().then_some(*entry)
entry.is_valid().then_some(entry)
}
fn lookup_mut(&mut self, vaddr: VirtAddr) -> Option<&mut Self::Entry> {
@ -118,26 +139,26 @@ impl PageTableOps for PageTable {
}
fn translate(&mut self, vaddr: VirtAddr) -> Option<PhysAddr> {
let (entry, size) = self.lookup_mut_internal(vaddr);
let (entry, level) = self.lookup_mut_internal(vaddr);
entry
.is_valid()
.then_some(entry.addr())
.map(|p| p.as_usize() | vaddr.lower_bits(size.to_level()))
.map(|p| p.as_usize() | vaddr.lower_bits(level.to_usize().unwrap()))
.map(|p| p.into())
}
}
impl PageTable {
impl Table {
fn debug_walk(
&self,
f: &mut core::fmt::Formatter,
base: VirtAddr,
size: PageSize,
level: TableLevel,
) -> core::fmt::Result {
macro_rules! w {
macro_rules! print_one {
($($arg:tt)*) => {
for _ in size.to_level()..Self::MAX_PAGE_SIZE.to_level() {
for _ in level.to_usize().unwrap()..Self::MAX_PAGE_SIZE.to_usize().unwrap() {
write!(f, "\t")?;
}
writeln!(f, $($arg)*)?;
@ -149,17 +170,17 @@ impl PageTable {
continue;
}
w!(
print_one!(
"[{:>3}]: {:?} -> {:?} : {:?}",
i,
base.merge_vpn(i, size),
base.merge_vpn(i, level),
entry.addr(),
entry
);
if !entry.is_leaf() && size.next().is_some() {
let table = unsafe { Self::from_va(entry.addr().as_usize().into()) };
table.debug_walk(f, base.merge_vpn(i, size), size.next().unwrap())?;
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())?;
}
}
@ -167,7 +188,7 @@ impl PageTable {
}
}
impl core::fmt::Debug for PageTable {
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"]
#![cfg_attr(test, allow(dead_code))]
#[macro_use]
extern crate static_assertions;
#[macro_use]
extern crate num_derive;
mod arch;
mod drivers;
mod entry;
@ -24,6 +28,7 @@ mod logging;
mod objects;
mod plat;
mod utils;
mod vspace;
// test infrastructure
#[cfg(test)]

View File

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

View File

@ -1,2 +1,3 @@
pub mod extern_addr;
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)
}
#[derive(Copy, Clone, PartialOrd, PartialEq)]
#[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
pub struct PhysAddr(pub usize);
#[derive(Copy, Clone, PartialOrd, PartialEq)]
#[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
pub struct VirtAddr(pub usize);
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

@ -1,4 +1,4 @@
use crate::addr::PhysAddr;
use crate::vspace::addr::PhysAddr;
use bitflags::bitflags;
use core::fmt::Debug;
@ -13,9 +13,9 @@ bitflags! {
}
}
pub trait PageTableEntryOps: Clone + Copy + Debug {
fn new_page(paddr: PhysAddr, attr: MapAttr) -> Self;
fn new_table(paddr: PhysAddr) -> Self;
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;

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)]
// reference: https://man.archlinux.org/man/cpio.5.en#New_ASCII_Format
// Reference: https://github.com/jcreekmore/cpio-rs/blob/master/src/newc.rs
// Reference:
// - 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 MAGIC_NEW_ASCII: [u8; 6] = *b"070701";

16
lib/vspace/Cargo.lock generated
View File

@ -1,16 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "vspace"
version = "0.1.0"
dependencies = [
"bitflags",
]

View File

@ -1,13 +0,0 @@
[package]
name = "vspace"
version = "0.1.0"
edition = "2021"
[features]
default = ["arch_riscv", "riscv_sv39"]
arch_riscv = []
riscv_sv39 = ["arch_riscv"]
[dependencies]
bitflags = "2.4"

View File

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

View File

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

View File

@ -1,5 +0,0 @@
mod entry;
mod table;
pub use entry::PageTableEntry;
pub use table::PageTable;

View File

@ -1,7 +0,0 @@
mod arch;
mod entry;
mod table;
pub use arch::*;
pub use entry::*;
pub use table::*;

View File

@ -1,63 +0,0 @@
use super::{MapAttr, PageTableEntryOps};
use crate::addr::{PhysAddr, VirtAddr};
use core::fmt::Debug;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageSize {
Kilo,
Mega,
Giga,
Tera,
}
impl PageSize {
pub fn is_aligned(&self, addr: usize) -> bool {
match self {
Self::Kilo => addr % (4 * 1024) == 0,
Self::Mega => addr % (2 * 1024 * 1024) == 0,
Self::Giga => addr % (1 * 1024 * 1024 * 1024) == 0,
Self::Tera => addr % (512 * 1024 * 1024 * 1024 * 1024) == 0,
}
}
pub fn to_level(&self) -> usize {
match self {
Self::Kilo => 0,
Self::Mega => 1,
Self::Giga => 2,
Self::Tera => 3,
}
}
pub fn next(&self) -> Option<Self> {
match self {
Self::Kilo => None,
Self::Mega => Some(Self::Kilo),
Self::Giga => Some(Self::Mega),
Self::Tera => Some(Self::Giga),
}
}
}
#[derive(Debug)]
pub enum PageError {
AlreadyMapped,
MissingEntry,
}
pub type PageResult<T = ()> = Result<T, PageError>;
pub trait PageTableOps: Debug {
type Entry: PageTableEntryOps;
const MAX_PAGE_SIZE: PageSize;
unsafe fn from_va(from: VirtAddr) -> &'static mut Self;
fn map(&mut self, from: VirtAddr, to: PhysAddr, attr: MapAttr, size: PageSize) -> 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>;
}