Compare commits

...

3 Commits

Author SHA1 Message Date
2ace3f14e1 feat: initial paging support 2023-12-17 15:30:10 +08:00
c7c36e5665 chore: adjust boot_stack 2023-12-17 15:25:54 +08:00
5a11fbd169 feat: add phys/virt address type 2023-12-12 15:26:16 +08:00
14 changed files with 451 additions and 17 deletions

6
.idea/rust.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RsVcsConfiguration">
<option name="rustFmt" value="true" />
</component>
</project>

13
Cargo.lock generated
View File

@ -14,6 +14,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -35,7 +41,7 @@ version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
] ]
[[package]] [[package]]
@ -78,6 +84,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
name = "tiny_os" name = "tiny_os"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags 2.4.1",
"lazy_static", "lazy_static",
"log", "log",
"sbi-rt", "sbi-rt",
@ -91,7 +98,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dc00444796f6c71f47c85397a35e9c4dbf9901902ac02386940d178e2b78687" checksum = "6dc00444796f6c71f47c85397a35e9c4dbf9901902ac02386940d178e2b78687"
dependencies = [ dependencies = [
"bitflags", "bitflags 1.3.2",
"rustversion", "rustversion",
"x86", "x86",
] ]
@ -103,6 +110,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"bitflags", "bitflags 1.3.2",
"raw-cpuid", "raw-cpuid",
] ]

View File

@ -23,6 +23,7 @@ panic = "abort"
panic = "abort" panic = "abort"
[dependencies] [dependencies]
bitflags = "2.4.1"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] } lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
log = "0.4" log = "0.4"
sbi-rt = { version = "0.0.2", features = ["legacy"] } sbi-rt = { version = "0.0.2", features = ["legacy"] }

View File

@ -5,6 +5,6 @@ pub mod io;
pub mod lowlevel; pub mod lowlevel;
// Arch Level // Arch Level
#[cfg(feature = "arch_riscv64")] #[cfg(any(feature = "arch_riscv64", feature = "arch_riscv32"))]
#[path = "riscv/mod.rs"] #[path = "riscv/mod.rs"]
mod riscv; mod riscv;

View File

@ -6,7 +6,7 @@ unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! {
// no stack here // no stack here
const STACK_SIZE: usize = 4096 * 16; const STACK_SIZE: usize = 4096 * 16;
#[link_section = ".boot_stack"] #[link_section = ".bss.boot_stack"]
static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE];
core::arch::asm!( core::arch::asm!(
@ -20,6 +20,8 @@ unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! {
} }
extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! { extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! {
// TODO: multiboot
unsafe { zero_bss() } unsafe { zero_bss() }
// TODO: initialize page table // TODO: initialize page table
@ -28,16 +30,14 @@ extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! {
} }
extern "C" { extern "C" {
static mut __bss_start: u8; static mut __boot_stack_end: u8;
static mut __bss_end: u8; static mut __bss_end: u8;
} }
#[inline(always)]
unsafe fn zero_bss() { unsafe fn zero_bss() {
let mut cur = &mut __bss_start as *mut u8; let cur = &mut __boot_stack_end as *mut u8;
let end = &mut __bss_end as *mut u8; let end = &mut __bss_end as *mut u8;
while cur < end { core::slice::from_raw_parts_mut(cur, end.offset_from(cur) as usize).fill(0);
cur.write_volatile(0);
cur = cur.add(1);
}
} }

View File

@ -42,14 +42,12 @@ SECTIONS {
__data_end = .; __data_end = .;
} > DRAM } > DRAM
.stack : {
. = ALIGN(8);
*(.boot_stack)
} > DRAM
.bss : { .bss : {
. = ALIGN(8); . = ALIGN(8);
__bss_start = .; __bss_start = .;
*(.bss.boot_stack)
__boot_stack_end = .;
*(.bss .bss.*) *(.bss .bss.*)
*(.sbss .sbss.*) *(.sbss .sbss.*)
__bss_end = .; __bss_end = .;

1
src/arch/riscv/mm/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod page;

135
src/arch/riscv/mm/page.rs Normal file
View File

@ -0,0 +1,135 @@
use crate::mm::addr::{AddressOps, PhysAddr, VirtAddr};
use crate::mm::page;
use bitflags::bitflags;
use static_assertions::assert_eq_size;
const PAGE_SIZE: usize = 4096;
const PAGE_TABLE_ENTRIES: usize = 512;
const PG_OFFSET: u64 = 12;
const PPN_OFFSET: u64 = 10;
const PPN_BITS: u64 = 44;
const PTE_PPN_MASK: u64 = ((1 << PPN_BITS) - 1) << PPN_OFFSET;
const PA_PPN_MASK: u64 = ((1 << PPN_BITS) - 1) << PG_OFFSET;
bitflags! {
pub struct PTEFlags : u64 {
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 page::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);
}
attr
}
}
impl From<page::MapAttr> for PTEFlags {
fn from(attr: page::MapAttr) -> Self {
if attr.is_empty() {
return Self::empty();
}
let mut flags = Self::VALID;
if attr.contains(page::MapAttr::READABLE) {
flags.insert(Self::READABLE);
}
if attr.contains(page::MapAttr::WRITABLE) {
flags.insert(Self::WRITABLE);
}
if attr.contains(page::MapAttr::EXECUTABLE) {
flags.insert(Self::EXECUTABLE);
}
if attr.contains(page::MapAttr::USER_ACCESSIBLE) {
flags.insert(Self::USER_ACCESSIBLE);
}
flags
}
}
#[derive(Clone, Copy)]
struct PTE(u64);
impl page::PageTableEntry for PTE {
fn new_page(paddr: PhysAddr, attr: page::MapAttr) -> Self {
let flags = PTEFlags::from(attr);
let ppn = ((paddr.as_u64() & PA_PPN_MASK) >> PG_OFFSET) << PPN_OFFSET;
Self(ppn | flags.bits())
}
fn new_table(paddr: PhysAddr) -> Self {
let flags = PTEFlags::VALID;
let ppn = ((paddr.as_u64() & PA_PPN_MASK) >> PG_OFFSET) << PPN_OFFSET;
Self(ppn | flags.bits())
}
fn addr(&self) -> PhysAddr {
let ppn = (self.0 & PTE_PPN_MASK) >> PPN_OFFSET;
let paddr = ppn << PG_OFFSET;
PhysAddr::from(paddr as usize)
}
fn attr(&self) -> page::MapAttr {
let flags = PTEFlags::from_bits_truncate(self.0);
flags.into()
}
fn set_addr(&mut self, addr: PhysAddr) {
let ppn = ((addr.as_u64() & PA_PPN_MASK) >> PG_OFFSET) << PPN_OFFSET;
self.0 = (self.0 & !PTE_PPN_MASK) | ppn;
}
fn set_attr(&mut self, attr: page::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
}
}
// we'll implement sv39 paging scheme
pub struct Size4KiB;
impl page::PageSize for Size4KiB {
const SIZE: usize = 1 << 12; // 4KiB
}
pub struct Size2MiB;
impl page::PageSize for Size2MiB {
const SIZE: usize = 1 << 21; // 2MiB
}
pub struct Size1GiB;
impl page::PageSize for Size1GiB {
const SIZE: usize = 1 << 30; // 1GiB
}

View File

@ -7,4 +7,5 @@ mod layout;
pub mod entry; pub mod entry;
pub mod io; pub mod io;
pub mod lowlevel; pub mod lowlevel;
pub mod mm;
pub mod trap; pub mod trap;

5
src/arch/trap.rs Normal file
View File

@ -0,0 +1,5 @@
pub struct Trap;
pub trait Trampoline {
fn init();
}

View File

@ -2,7 +2,6 @@
#![feature(asm_const)] #![feature(asm_const)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(fmt_internals)]
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
extern crate static_assertions; extern crate static_assertions;
@ -16,5 +15,8 @@ pub mod lang;
// entrypoint // entrypoint
pub mod entry; pub mod entry;
// page table
pub mod mm;
// logging // logging
pub mod logging; pub mod logging;

223
src/mm/addr.rs Normal file
View File

@ -0,0 +1,223 @@
use core::fmt::{Debug, Formatter, LowerHex, Result, UpperHex};
use core::hash::Hash;
use core::ops::{Add, AddAssign, Sub, SubAssign};
#[inline(always)]
fn align_up(addr: usize, align: usize) -> usize {
(addr + align - 1) & !(align - 1)
}
#[inline(always)]
fn align_down(addr: usize, align: usize) -> usize {
addr & !(align - 1)
}
pub struct PhysAddr(pub usize);
pub struct VirtAddr(pub usize);
pub trait AddressOps {
fn as_u32(&self) -> u32;
fn as_u64(&self) -> u64;
fn as_usize(&self) -> usize;
fn align_up<T>(&self, align: T) -> Self
where
T: Into<usize>;
fn align_down<T>(&self, align: T) -> Self
where
T: Into<usize>;
fn is_aligned<T>(&self, align: T) -> bool
where
T: Into<usize> + Copy,
{
if align.into().is_power_of_two() {
self.as_usize() & (align.into() - 1) == 0
} else {
false
}
}
}
impl AddressOps for PhysAddr {
fn as_u32(&self) -> u32 {
self.0 as u32
}
fn as_u64(&self) -> u64 {
self.0 as u64
}
fn as_usize(&self) -> usize {
self.0
}
fn align_up<T>(&self, align: T) -> Self
where
T: Into<usize>,
{
PhysAddr(align_up(self.0, align.into()))
}
fn align_down<T>(&self, align: T) -> Self
where
T: Into<usize>,
{
PhysAddr(align_down(self.0, align.into()))
}
}
impl AddressOps for VirtAddr {
fn as_u32(&self) -> u32 {
self.0 as u32
}
fn as_u64(&self) -> u64 {
self.0 as u64
}
fn as_usize(&self) -> usize {
self.0
}
fn align_up<T>(&self, align: T) -> Self
where
T: Into<usize>,
{
VirtAddr(align_up(self.0, align.into()))
}
fn align_down<T>(&self, align: T) -> Self
where
T: Into<usize>,
{
VirtAddr(align_down(self.0, align.into()))
}
}
impl Add<usize> for PhysAddr {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs)
}
}
impl AddAssign<usize> for PhysAddr {
fn add_assign(&mut self, rhs: usize) {
*self = PhysAddr::from(self.0 + rhs);
}
}
impl Sub<usize> for PhysAddr {
type Output = PhysAddr;
fn sub(self, rhs: usize) -> Self::Output {
PhysAddr(self.0 - rhs)
}
}
impl SubAssign<usize> for PhysAddr {
fn sub_assign(&mut self, rhs: usize) {
*self = PhysAddr::from(self.0 - rhs);
}
}
impl Add<usize> for VirtAddr {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs)
}
}
impl AddAssign<usize> for VirtAddr {
fn add_assign(&mut self, rhs: usize) {
*self = VirtAddr::from(self.0 + rhs);
}
}
impl Sub<usize> for VirtAddr {
type Output = VirtAddr;
fn sub(self, rhs: usize) -> Self::Output {
VirtAddr(self.0 - rhs)
}
}
impl SubAssign<usize> for VirtAddr {
fn sub_assign(&mut self, rhs: usize) {
*self = VirtAddr::from(self.0 - rhs);
}
}
impl From<usize> for PhysAddr {
fn from(addr: usize) -> Self {
PhysAddr(addr)
}
}
impl From<PhysAddr> for usize {
fn from(addr: PhysAddr) -> Self {
addr.0
}
}
impl From<usize> for VirtAddr {
fn from(addr: usize) -> Self {
VirtAddr(addr)
}
}
impl From<VirtAddr> for usize {
fn from(addr: VirtAddr) -> Self {
addr.0
}
}
impl Debug for PhysAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "PhysAddr({:#x})", self.0)
}
}
impl LowerHex for PhysAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{:#x}", self.0)
}
}
impl UpperHex for PhysAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{:#X}", self.0)
}
}
impl Debug for VirtAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "VirtAddr({:#x})", self.0)
}
}
impl LowerHex for VirtAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{:#x}", self.0)
}
}
impl UpperHex for VirtAddr {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{:#X}", self.0)
}
}
impl Hash for PhysAddr {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl Hash for VirtAddr {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}

2
src/mm/mod.rs Normal file
View File

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

53
src/mm/page.rs Normal file
View File

@ -0,0 +1,53 @@
use crate::mm::addr::{AddressOps, PhysAddr, VirtAddr};
use bitflags::bitflags;
use core::marker::PhantomData;
#[derive(Debug)]
pub enum PagingError {
AddressNotAligned,
}
bitflags! {
pub struct MapAttr: usize {
const READABLE = 1 << 1;
const WRITABLE = 1 << 2;
const EXECUTABLE = 1 << 3;
const USER_ACCESSIBLE = 1 << 4;
}
}
pub trait PageTableEntry {
fn new_page(paddr: PhysAddr, attr: MapAttr) -> Self;
fn new_table(paddr: 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;
}
pub trait PageSize {
// Arch should implement this
// For example, 4KiB / 2MiB / 1GiB in sv39
const SIZE: usize;
}
pub struct Page<S: PageSize> {
start_address: VirtAddr,
size: PhantomData<S>,
}
impl<S: PageSize> Page<S> {
pub const SIZE: usize = S::SIZE;
pub fn new(addr: VirtAddr) -> Self {
Self {
start_address: addr.align_down(Self::SIZE),
size: PhantomData,
}
}
}