mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
Compare commits
9 Commits
bf6297c5ac
...
93c4d3c5fb
Author | SHA1 | Date | |
---|---|---|---|
93c4d3c5fb | |||
c066097c82 | |||
797652c2b8 | |||
6565beef4c | |||
ecb8b7a54f | |||
39dab16baa | |||
00b7c7d7df | |||
3e13d1ce52 | |||
e50105e797 |
@ -1,4 +1,4 @@
|
||||
pub const TIMER_TICKS: u64 = 100_000; // 100ms
|
||||
pub const TIMER_TICKS: u64 = 100_000;
|
||||
|
||||
// devices
|
||||
pub const UART0_BASE: usize = 0x1000_0000;
|
||||
|
@ -79,7 +79,7 @@ impl TrapContextOps for TrapContext {
|
||||
T::Interrupt(I::SupervisorTimer) => {
|
||||
Timer::do_tick();
|
||||
if !from_kernel {
|
||||
tcb.timer_tick();
|
||||
tcb.do_tick();
|
||||
}
|
||||
},
|
||||
T::Exception(E::UserEnvCall) => {
|
||||
@ -110,16 +110,16 @@ impl TrapContextOps for TrapContext {
|
||||
}
|
||||
|
||||
fn get_reg(&self, index: usize) -> usize {
|
||||
// x10 ~ x17: Function arguments
|
||||
// x9(a0) ~ x16(a7): Function arguments
|
||||
assert!(index < 8, "TrapContext get_reg index out of range");
|
||||
self.gprs[index + 10]
|
||||
self.gprs[index + 9]
|
||||
}
|
||||
|
||||
fn set_reg(&mut self, index: usize, value: usize) {
|
||||
// x10 ~ x17: Function arguments
|
||||
// x10 ~ x11: Function return values
|
||||
// x9(a0) ~ x16(a7): Function arguments
|
||||
// x9(a0) ~ x10(a1): Function return values
|
||||
assert!(index < 8, "TrapContext set_reg index out of range");
|
||||
self.gprs[index + 10] = value;
|
||||
self.gprs[index + 9] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,12 @@ trait GenericPhysAddrPage: AddressOps {
|
||||
self.extract_ppn() << Self::PPN_OFFSET
|
||||
}
|
||||
|
||||
fn from_ppn(ppn: usize) -> usize {
|
||||
let addr = ppn << Self::PG_OFFSET;
|
||||
let bits = Self::PG_OFFSET + Self::PPN_BITS;
|
||||
sign_extend(addr, bits)
|
||||
}
|
||||
|
||||
fn from_pte(pte: usize) -> usize {
|
||||
let addr = ((pte & Self::PTE_PPN_MASK) >> Self::PPN_OFFSET) << Self::PG_OFFSET;
|
||||
let bits = Self::PPN_BITS + Self::PG_OFFSET;
|
||||
|
@ -21,6 +21,10 @@ impl PhysAddrPage for PhysAddr {
|
||||
GenericPhysAddrPage::extract_ppn_shifted(self)
|
||||
}
|
||||
|
||||
fn from_ppn(ppn: usize) -> PhysAddr {
|
||||
PhysAddr::from(<PhysAddr as GenericPhysAddrPage>::from_ppn(ppn))
|
||||
}
|
||||
|
||||
fn from_pte(pte: usize) -> PhysAddr {
|
||||
PhysAddr::from(<PhysAddr as GenericPhysAddrPage>::from_pte(pte))
|
||||
}
|
||||
|
@ -150,9 +150,7 @@ pub unsafe fn setup_kernel_paging() {
|
||||
pub unsafe fn install_kernel_pagetable() {
|
||||
info!("Setting up new kernel pagetable");
|
||||
|
||||
let vaddr = KERNEL_PAGETABLE.lock().as_ref().expect("No kernel pagetable found").vaddr();
|
||||
let paddr = mmap_virt_to_phys(vaddr);
|
||||
|
||||
let paddr = KERNEL_PAGETABLE.lock().as_ref().expect("No kernel pagetable found").paddr();
|
||||
install_pagetable(paddr)
|
||||
}
|
||||
|
||||
@ -174,7 +172,7 @@ pub fn copy_kernel_pagetable(root: &CapEntry) {
|
||||
let mut root = TableCap::try_from(root).expect("Invalid vspace cap");
|
||||
let to: Table<Level0> = root.as_object_mut();
|
||||
|
||||
for (i, (from, to)) in from.entries.iter().zip(to.entries.iter_mut()).enumerate() {
|
||||
for (from, to) in from.entries.iter().zip(to.entries.iter_mut()) {
|
||||
if !from.is_valid() {
|
||||
to.clear();
|
||||
continue;
|
||||
|
@ -1,4 +1,7 @@
|
||||
use crate::{arch::layout::PAGE_SIZE, vspace::*};
|
||||
use crate::{
|
||||
arch::layout::{mmap_virt_to_phys, PAGE_SIZE},
|
||||
vspace::*,
|
||||
};
|
||||
use utils::addr::*;
|
||||
|
||||
impl<'a, T: TableLevel> TableOps<'a, T> for Table<'a, T> {
|
||||
@ -15,6 +18,10 @@ impl<'a, T: TableLevel> TableOps<'a, T> for Table<'a, T> {
|
||||
VirtAddr::from(self.entries.as_ptr())
|
||||
}
|
||||
|
||||
fn paddr(&self) -> PhysAddr {
|
||||
unsafe { mmap_virt_to_phys(self.vaddr()) }
|
||||
}
|
||||
|
||||
fn map(&mut self, from: VirtAddr, to: PhysAddr, attr: MapAttr) -> PageResult {
|
||||
if !from.is_aligned(T::LEVEL_SIZE) || !to.is_aligned(PAGE_SIZE) {
|
||||
return Err(PageError::NotAligned);
|
||||
|
@ -12,7 +12,7 @@ pub static ALLOC_COUNT: core::sync::atomic::AtomicUsize = core::sync::atomic::At
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn alloc_callback() {
|
||||
let cnt = ALLOC_COUNT.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
|
||||
ALLOC_COUNT.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, tracer::trace_callback(log = true, callback = alloc_callback))]
|
||||
@ -100,3 +100,12 @@ pub unsafe fn install_pagetable(addr: PhysAddr) {
|
||||
riscv::register::satp::set(page_table_mode(), 0, addr.extract_ppn());
|
||||
riscv::asm::sfence_vma_all();
|
||||
}
|
||||
|
||||
pub unsafe fn get_current_pagetable() -> Table<'static, Level0> {
|
||||
let satp = riscv::register::satp::read();
|
||||
let paddr = PhysAddr::from_ppn(satp.ppn());
|
||||
if paddr == PhysAddr(0) {
|
||||
panic!("get_current_pagetable() could only be called with MMU enabled!");
|
||||
}
|
||||
Table::new(mmap_phys_to_virt(paddr))
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::plat::console::{set_console, ConsoleDevice, ConsoleDriver, CONSOLE};
|
||||
use crate::plat::lowlevel::{Hardware, LowLevel};
|
||||
use crate::plat::timer::{Timer, TimerOps};
|
||||
use crate::plat::trap::{Trap, TrapOps};
|
||||
use crate::scheduler::SCHEDULER;
|
||||
use crate::scheduler::{IDLE_THREAD, SCHEDULER};
|
||||
use core::cell::Cell;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use fdt::Fdt;
|
||||
@ -35,6 +35,9 @@ pub fn rust_main() -> ! {
|
||||
Trap::init();
|
||||
Timer::init();
|
||||
|
||||
SCHEDULER.init();
|
||||
SCHEDULER.add(&IDLE_THREAD);
|
||||
|
||||
SCHEDULER.schedule();
|
||||
|
||||
loop {
|
||||
|
@ -4,6 +4,21 @@ use core::fmt::Write;
|
||||
use core::sync::atomic::Ordering;
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {{
|
||||
use core::fmt::Write;
|
||||
use crate::plat::console::CONSOLE;
|
||||
CONSOLE.lock().write_fmt(format_args!($($arg)*)).unwrap();
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($msg:expr) => { print!(concat!($msg, "\n")) };
|
||||
($fmt:expr, $($arg:tt)*) => { print!(concat!($fmt, "\n"), $($arg)*) };
|
||||
}
|
||||
|
||||
struct SimpleLogger;
|
||||
|
||||
impl Log for SimpleLogger {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::objects::*;
|
||||
use core::cell::Cell;
|
||||
use core::fmt::{write, Debug};
|
||||
use core::fmt::Debug;
|
||||
use uapi::cap::ObjectType;
|
||||
use utils::addr::PhysAddr;
|
||||
use utils::{
|
||||
|
@ -1,8 +1,5 @@
|
||||
use super::{
|
||||
cap::{CapEntry, RawCap},
|
||||
Cap, KernelObject,
|
||||
};
|
||||
use crate::arch::layout::mmap_phys_to_virt;
|
||||
use crate::objects::*;
|
||||
use core::fmt::Debug;
|
||||
use uapi::{cap::ObjectType, fault::LookupFailure};
|
||||
use utils::addr::{AddressOps, PhysAddr};
|
||||
|
@ -1,9 +1,4 @@
|
||||
use super::{cap::RawCap, Cap, KernelObject};
|
||||
use crate::{
|
||||
arch::layout::{mmap_phys_to_virt, PAGE_SIZE},
|
||||
objects::cap::CapEntry,
|
||||
vspace::*,
|
||||
};
|
||||
use crate::{arch::layout::mmap_phys_to_virt, objects::*, vspace::*};
|
||||
use core::fmt::Debug;
|
||||
use uapi::{
|
||||
cap::ObjectType,
|
||||
|
@ -31,10 +31,10 @@ pub mod untyped;
|
||||
pub use cap::{CapEntry, RawCap};
|
||||
pub use cnode::{CNodeCap, CNodeObject};
|
||||
pub use frame::{FrameCap, FrameObject};
|
||||
pub use null::{NullCap, NullObject};
|
||||
pub use null::NullCap;
|
||||
pub use table::{TableCap, TableObject};
|
||||
pub use tcb::{TcbCap, TcbObject};
|
||||
pub use untyped::{UntypedCap, UntypedObject};
|
||||
pub use untyped::UntypedCap;
|
||||
|
||||
/// Cap is the high-level wrapper of RawCap, it's a typed reference to RawCap (which is untyped in Rust)
|
||||
/// For the typed objects, we should bound it with an empty traits `KernelObject`
|
||||
|
@ -1,5 +1,4 @@
|
||||
use super::cap::RawCap;
|
||||
use super::{Cap, KernelObject};
|
||||
use crate::objects::*;
|
||||
use core::fmt::Debug;
|
||||
use uapi::cap::ObjectType;
|
||||
use utils::addr::PhysAddr;
|
||||
|
@ -1,5 +1,4 @@
|
||||
use super::{cap::RawCap, Cap, KernelObject};
|
||||
use crate::{arch::layout::mmap_phys_to_virt, vspace::*};
|
||||
use crate::{arch::layout::mmap_phys_to_virt, objects::*, vspace::*};
|
||||
use core::fmt::Debug;
|
||||
use uapi::{
|
||||
cap::ObjectType,
|
||||
|
@ -1,14 +1,8 @@
|
||||
use super::{
|
||||
cap::{CapEntry, RawCap},
|
||||
cnode::CNodeCap,
|
||||
null::NullCap,
|
||||
Cap, KernelObject,
|
||||
};
|
||||
use crate::{
|
||||
arch::{layout::mmap_phys_to_virt, trap::TrapContext},
|
||||
plat::trap::TrapContextOps,
|
||||
};
|
||||
use crate::arch::{layout::mmap_phys_to_virt, trap::TrapContext, vspace::*};
|
||||
use crate::{objects::*, plat::trap::TrapContextOps, vspace::*};
|
||||
use core::fmt::Debug;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use log::debug;
|
||||
use uapi::{cap::ObjectType, error::SysResult, fault::Fault, syscall::MessageInfo};
|
||||
use utils::{
|
||||
addr::*,
|
||||
@ -16,7 +10,9 @@ use utils::{
|
||||
LinkHelperImpl,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub static TID_ALLOCATOR: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ThreadState {
|
||||
Inactive,
|
||||
Running,
|
||||
@ -38,6 +34,7 @@ pub struct TcbObject {
|
||||
|
||||
state: ThreadState,
|
||||
time_tick: usize,
|
||||
tid: usize,
|
||||
pub link: Link<Self>,
|
||||
}
|
||||
|
||||
@ -58,19 +55,29 @@ impl TcbObject {
|
||||
fault_handler: CapEntry::new(NullCap::mint()),
|
||||
state: ThreadState::Inactive,
|
||||
time_tick: 0,
|
||||
tid: TID_ALLOCATOR.fetch_add(1, Ordering::SeqCst),
|
||||
link: Link::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_idle_thread(&mut self) {
|
||||
self.trapframe.configure_idle_thread();
|
||||
self.vspace = CapEntry::new(TableCap::mint(unsafe { get_current_pagetable().paddr() }));
|
||||
self.state = ThreadState::Idle;
|
||||
}
|
||||
|
||||
pub fn tid(&self) -> usize {
|
||||
self.tid
|
||||
}
|
||||
|
||||
pub fn state(&self) -> ThreadState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn schedulable(&self) -> bool {
|
||||
self.state != ThreadState::Blocked && self.state != ThreadState::Inactive
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, state: ThreadState) {
|
||||
self.state = state;
|
||||
}
|
||||
@ -83,17 +90,18 @@ impl TcbObject {
|
||||
self.time_tick = timeslice;
|
||||
}
|
||||
|
||||
pub fn timer_tick(&mut self) {
|
||||
pub fn do_tick(&mut self) {
|
||||
self.time_tick = self.time_tick.saturating_sub(1);
|
||||
}
|
||||
|
||||
pub fn install_vspace(&self) {
|
||||
// setup pagetable
|
||||
todo!("TcbObject::install_vspace");
|
||||
pub fn activate_vspace(&self) {
|
||||
let cap = TableCap::try_from(&self.vspace).expect("activate_vspace: invalid cap");
|
||||
let paddr = cap.as_object::<Level0>().paddr();
|
||||
unsafe { install_pagetable(paddr) }
|
||||
}
|
||||
|
||||
pub fn activate(&mut self) {
|
||||
self.install_vspace();
|
||||
self.activate_vspace();
|
||||
self.trapframe.restore();
|
||||
self.trapframe.handle_trap(false);
|
||||
}
|
||||
@ -106,9 +114,9 @@ impl TcbObject {
|
||||
let cap_ptr = self.trapframe.get_reg(1);
|
||||
let cap = cspace.resolve_address_bits(cap_ptr, usize::BITS as usize);
|
||||
|
||||
todo!("handle_syscall");
|
||||
debug!("handle_syscall: info={:?}, cap={:?}", info, cap);
|
||||
|
||||
Ok(())
|
||||
todo!("handle_syscall");
|
||||
};
|
||||
|
||||
handle_syscall_inner().unwrap();
|
||||
|
@ -1,7 +1,4 @@
|
||||
use super::cap::RawCap;
|
||||
use super::cnode::{CNodeCap, CNodeObject};
|
||||
use super::null::NullCap;
|
||||
use super::{Cap, FrameCap, KernelObject, TableCap, TcbCap};
|
||||
use crate::objects::*;
|
||||
use crate::vspace::MapAttr;
|
||||
use core::fmt::Debug;
|
||||
use uapi::cap::ObjectType;
|
||||
|
@ -12,7 +12,7 @@ use utils::{addr::*, bin::prev_power_of_two};
|
||||
|
||||
const ROOT_CNODE_SIZE: usize = 128;
|
||||
static mut ROOT_CNODE: Lazy<[CapEntry; ROOT_CNODE_SIZE]> =
|
||||
Lazy::new(|| core::array::from_fn(|i| CapEntry::new(NullCap::mint())));
|
||||
Lazy::new(|| core::array::from_fn(|_| CapEntry::new(NullCap::mint())));
|
||||
|
||||
pub fn setup_root_server(fdt: &Fdt) {
|
||||
// Root Server is using statically allocated ROOT_CNODE
|
||||
@ -216,9 +216,13 @@ fn create_objects(cnode_obj: &mut CNodeObject, end_idx: usize) {
|
||||
}
|
||||
|
||||
fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> usize {
|
||||
let table: Table<Level0> = TableCap::try_from(&cnode_obj[CNodeSlot::VSpaceCap as usize])
|
||||
.unwrap()
|
||||
.as_object_mut();
|
||||
let mut table = {
|
||||
let root = &cnode_obj[CNodeSlot::VSpaceCap as usize];
|
||||
let root_vaddr = TableCap::try_from(root).unwrap().as_object::<Level0>().vaddr();
|
||||
|
||||
// use unsafe to workaround borrow checker
|
||||
unsafe { Table::new(root_vaddr) }
|
||||
};
|
||||
|
||||
let mut rootfs = unsafe {
|
||||
let chosen = fdt.find_node("/chosen").expect("/chosen is required");
|
||||
@ -255,7 +259,6 @@ fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> u
|
||||
})
|
||||
.expect("init: root server not found");
|
||||
|
||||
let root_start = VirtAddr::from(root_entry.data().as_ptr());
|
||||
let root_elf = ElfBytes::<AnyEndian>::minimal_parse(root_entry.data()).expect("init: invalid root server");
|
||||
|
||||
let mut nxt_slot = end_idx;
|
||||
@ -272,7 +275,6 @@ fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> u
|
||||
phdr.p_vaddr + phdr.p_memsz
|
||||
);
|
||||
|
||||
let seg_start = root_start + phdr.p_offset as usize;
|
||||
let seg_addr = VirtAddr::from(phdr.p_vaddr);
|
||||
|
||||
// Currently, we only support loading segments that are aligned to PAGE_SIZE
|
||||
@ -286,6 +288,7 @@ fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> u
|
||||
dest.fill(0);
|
||||
} else {
|
||||
let len = min(phdr.p_filesz as usize - offset, dest.len());
|
||||
let offset = phdr.p_offset as usize + offset;
|
||||
let src = &root_entry.data()[offset..offset + len];
|
||||
dest[..len].copy_from_slice(src);
|
||||
dest[len..].fill(0);
|
||||
@ -313,10 +316,6 @@ fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> u
|
||||
copy(cur, mem);
|
||||
|
||||
// map frame to vspace
|
||||
let root = &cnode_obj[CNodeSlot::VSpaceCap as usize];
|
||||
let root_vaddr = TableCap::try_from(root).unwrap().as_object::<Level0>().vaddr();
|
||||
let mut root = unsafe { Table::new(root_vaddr) }; // use unsafe to workaround borrow checker
|
||||
|
||||
let vaddr = seg_addr + cur;
|
||||
let paddr = unsafe { mmap_virt_to_phys(VirtAddr::from(mem as *const _ as *const u8)) };
|
||||
let attr = {
|
||||
@ -348,7 +347,7 @@ fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> u
|
||||
unsafe { mmap_virt_to_phys(table.vaddr()) }
|
||||
};
|
||||
|
||||
map_range(alloc_page, &mut root, vaddr, paddr, block_size, attr);
|
||||
map_range(alloc_page, &mut table, vaddr, paddr, block_size, attr);
|
||||
|
||||
// update pointer
|
||||
cur += block_size;
|
||||
|
@ -2,17 +2,17 @@ use crate::objects::*;
|
||||
use core::ptr::NonNull;
|
||||
use log::error;
|
||||
use spin::lazy::Lazy;
|
||||
use utils::linked_list::Link;
|
||||
use utils::{container_of, linked_list::Link};
|
||||
|
||||
#[thread_local]
|
||||
static IDLE_THREAD: Lazy<TcbObject> = Lazy::new(|| {
|
||||
pub static IDLE_THREAD: Lazy<TcbObject> = Lazy::new(|| {
|
||||
let mut idle_thread = TcbObject::new();
|
||||
idle_thread.configure_idle_thread();
|
||||
idle_thread
|
||||
});
|
||||
|
||||
#[thread_local]
|
||||
pub static SCHEDULER: Lazy<Scheduler> = Lazy::new(|| Scheduler::new(&IDLE_THREAD));
|
||||
pub static SCHEDULER: Scheduler = Scheduler::new();
|
||||
|
||||
// TODO: add a shared buffer to transfer TCB between cores
|
||||
|
||||
@ -24,14 +24,14 @@ pub struct Scheduler {
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn new(idle_thread: &TcbObject) -> Self {
|
||||
// link idle_thread
|
||||
idle_thread.link.set_prev(Some(NonNull::from(idle_thread)));
|
||||
idle_thread.link.set_next(Some(NonNull::from(idle_thread)));
|
||||
|
||||
Self {
|
||||
head: idle_thread.link.clone(),
|
||||
pub const fn new() -> Self {
|
||||
Self { head: Link::new() }
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
let head = unsafe { Some(NonNull::from(&*container_of!(&self.head, TcbObject, link))) };
|
||||
self.head.set_next(head);
|
||||
self.head.set_prev(head);
|
||||
}
|
||||
|
||||
pub fn add(&self, tcb: &TcbObject) {
|
||||
@ -40,20 +40,18 @@ impl Scheduler {
|
||||
|
||||
pub fn schedule(&self) {
|
||||
while let Some(next) = self.head.next_mut() {
|
||||
// TODO: also need to check whether it is schedulable
|
||||
if next.timetick() > 0 {
|
||||
// Available to run, activate it
|
||||
if next.timetick() > 0 && next.schedulable() {
|
||||
next.activate();
|
||||
} else if next.timetick() == 0 {
|
||||
// No time left, refill time tick and move to the end of the queue
|
||||
next.set_timetick(TIME_SLICE);
|
||||
}
|
||||
|
||||
// put to the end of the queue
|
||||
// todo: only move blocked and time expired threads to the end
|
||||
if next.timetick() == 0 || !next.schedulable() {
|
||||
next.link.detach();
|
||||
self.head.prepend(next);
|
||||
}
|
||||
}
|
||||
|
||||
error!("[Scheduler] No thread to schedule! Where is IDLE thread?");
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ pub trait PhysAddrPage: AddressOps {
|
||||
/// Almost the same as `paddr_extract_ppn`, but the PPN remains shifted in the address.
|
||||
fn extract_ppn_shifted(&self) -> usize;
|
||||
|
||||
/// Converts a Physical Page Number (PPN) into a Physical Address.
|
||||
fn from_ppn(ppn: usize) -> Self;
|
||||
|
||||
/// Converts a PTE into a Physical Address.
|
||||
fn from_pte(pte: usize) -> Self;
|
||||
|
||||
|
@ -6,6 +6,7 @@ pub trait TableOps<'a, T: TableLevel> {
|
||||
/// `location` must be a page-aligned virtual address and will not be dropped.
|
||||
unsafe fn new(location: VirtAddr) -> Self;
|
||||
fn vaddr(&self) -> VirtAddr;
|
||||
fn paddr(&self) -> PhysAddr;
|
||||
|
||||
// following methods only works at current level
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user