mirror of
https://github.com/panpaul/tiny_os
synced 2024-09-20 09:45:19 +08:00
Compare commits
13 Commits
319c9db662
...
7d34dfc2cd
Author | SHA1 | Date | |
---|---|---|---|
7d34dfc2cd | |||
ff02847885 | |||
615dac5f5c | |||
59514440e0 | |||
25982b5a33 | |||
5c1e73a4ee | |||
c371fd543f | |||
ea80bd3e8d | |||
c67a771500 | |||
8218f87765 | |||
e360db4972 | |||
5a9a11ce1c | |||
c53dd8b930 |
20
.vscode/settings.json
vendored
Normal file
20
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"rust-analyzer.cargo.allTargets": false,
|
||||
"rust-analyzer.cargo.target": "riscv64imac-unknown-none-elf",
|
||||
"rust-analyzer.cargo.extraArgs": [
|
||||
"--bin",
|
||||
"kernel"
|
||||
],
|
||||
"rust-analyzer.cargo.extraEnv": {
|
||||
"RUSTFLAGS": "-Crelocation-model=static -Ccode-model=medium -Ctarget-feature=+relax -Clink-arg=-Tkernel/src/arch/riscv/linker64.ld"
|
||||
},
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"./kernel/Cargo.toml"
|
||||
],
|
||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||
"files.readonlyInclude": {
|
||||
"**/.rustup/**": true,
|
||||
"**/.cargo/registry/**": true,
|
||||
"**/.cargo/git/**": true,
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#[derive(Clone, Copy, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
pub enum ObjectType {
|
||||
Null = 0,
|
||||
CNode = 1,
|
||||
@ -18,3 +18,26 @@ impl Default for ObjectType {
|
||||
Self::Null
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectType {
|
||||
pub const fn size(&self, user_obj_bits: usize) -> usize {
|
||||
1 << self.bits(user_obj_bits)
|
||||
}
|
||||
|
||||
pub const fn bits(&self, user_obj_bits: usize) -> usize {
|
||||
#![rustfmt::skip]
|
||||
match self {
|
||||
ObjectType::Null => 0, // TODO: fill it!
|
||||
ObjectType::CNode => 0, // TODO: fill it!
|
||||
ObjectType::TCB => 0, // TODO: fill it!
|
||||
ObjectType::SchedCtx => 0, // TODO: fill it!
|
||||
ObjectType::Endpoint => 0, // TODO: fill it!
|
||||
ObjectType::Reply => 0, // TODO: fill it!
|
||||
ObjectType::Notification => 0, // TODO: fill it!
|
||||
ObjectType::Frame => 0, // TODO: fill it!
|
||||
ObjectType::PageTable => 0, // TODO: fill it!
|
||||
ObjectType::Interrupt => 0, // TODO: fill it!
|
||||
ObjectType::Untyped => user_obj_bits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
api/src/error.rs
Normal file
27
api/src/error.rs
Normal file
@ -0,0 +1,27 @@
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CapFault {
|
||||
InvalidRoot,
|
||||
MissingCapability {
|
||||
bits_left: usize,
|
||||
},
|
||||
DepthMismatch {
|
||||
bits_found: usize,
|
||||
bits_left: usize,
|
||||
},
|
||||
GuardMismatch {
|
||||
bits_left: usize,
|
||||
guard_found: usize,
|
||||
guard_size: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
pub enum SysError {
|
||||
Ok,
|
||||
CapTypeMismatch, // InvalidCapability
|
||||
SlotNotEmpty, // DeleteFirst
|
||||
InvalidArgument,
|
||||
RangeError,
|
||||
}
|
||||
|
||||
pub type SysResult<T> = Result<T, SysError>;
|
@ -1,6 +1,8 @@
|
||||
#![no_std]
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate num_derive;
|
||||
|
||||
pub mod cap;
|
||||
pub mod error;
|
||||
|
@ -5,9 +5,9 @@ use crate::arch::vspace::is_kernel_pagetable_installed;
|
||||
|
||||
unsafe fn get_qemu_test_device() -> *mut u32 {
|
||||
if is_kernel_pagetable_installed() {
|
||||
return mmap_phys_to_virt(TEST_DEVICE.into()).into();
|
||||
mmap_phys_to_virt(TEST_DEVICE.into()).into()
|
||||
} else {
|
||||
return TEST_DEVICE;
|
||||
TEST_DEVICE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
use crate::arch::init_early_console;
|
||||
use crate::arch::layout::{mmap_phys_to_virt, zero_bss, KERNEL_OFFSET};
|
||||
use crate::arch::layout::{mmap_phys_to_virt, zero_bss};
|
||||
use crate::arch::vspace::{setup_kernel_paging, setup_memory};
|
||||
use crate::entry::{rust_main, HART_ID};
|
||||
use crate::plat::console::mute_console;
|
||||
use fdt::Fdt;
|
||||
use utils::atomic::AtomicConstPtr;
|
||||
use vspace::addr::AddressOps;
|
||||
|
||||
#[naked]
|
||||
#[no_mangle]
|
||||
#[allow(named_asm_labels)]
|
||||
#[link_section = ".text.entry"]
|
||||
unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! {
|
||||
unsafe extern "C" fn _start(hart_id: usize, fdt_addr: usize) -> ! {
|
||||
// NOTE: no stack here
|
||||
// we should be launched by OpenSBI and running is S-mode
|
||||
|
||||
@ -60,6 +60,8 @@ unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! {
|
||||
)
|
||||
}
|
||||
|
||||
pub static FDT: AtomicConstPtr<u8> = AtomicConstPtr::new(core::ptr::null());
|
||||
|
||||
unsafe fn pre_main(hart_id: usize, fdt_addr: usize) {
|
||||
zero_bss();
|
||||
|
||||
@ -73,6 +75,8 @@ unsafe fn pre_main(hart_id: usize, fdt_addr: usize) {
|
||||
// after kernel paging, board level early console is broken (no address mapping)
|
||||
mute_console();
|
||||
|
||||
let fdt = unsafe { Fdt::from_ptr(mmap_phys_to_virt(fdt_addr.into()).as_const_ptr()).unwrap() };
|
||||
rust_main(fdt);
|
||||
let fdt_addr: *const u8 = mmap_phys_to_virt(fdt_addr.into()).as_const_ptr();
|
||||
FDT.store(fdt_addr as *mut u8, core::sync::atomic::Ordering::Release);
|
||||
|
||||
rust_main();
|
||||
}
|
||||
|
@ -11,10 +11,6 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
// Console: plat/console.rs
|
||||
pub use board::console::init_early_console;
|
||||
pub use board::console::EarlyConsole;
|
||||
|
||||
mod entry;
|
||||
pub mod layout;
|
||||
mod lowlevel;
|
||||
@ -22,3 +18,10 @@ mod timer;
|
||||
mod tls;
|
||||
pub mod trap;
|
||||
pub mod vspace;
|
||||
|
||||
// Console: plat/console.rs
|
||||
pub use board::console::init_early_console;
|
||||
pub use board::console::EarlyConsole;
|
||||
|
||||
// FDT: entry.rs
|
||||
pub use entry::FDT;
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::entry::HART_ID;
|
||||
use crate::plat::console::CONSOLE;
|
||||
use cfg_if::cfg_if;
|
||||
use log::trace;
|
||||
use riscv::register::scause::{Interrupt as I, Trap as T};
|
||||
use riscv::register::stvec::TrapMode;
|
||||
|
||||
use crate::plat::timer::{Timer, TimerOps};
|
||||
use crate::plat::trap::{Trap, TrapContextOps, TrapOps};
|
||||
use crate::scheduler::SCHEDULER;
|
||||
use log::trace;
|
||||
use riscv::register::scause::{Exception as E, Interrupt as I, Trap as T};
|
||||
use riscv::register::stvec::TrapMode;
|
||||
|
||||
core::arch::global_asm!(include_str!("./asm/trap64.S"));
|
||||
core::arch::global_asm!(include_str!("./asm/trap_common.S"));
|
||||
@ -18,29 +17,52 @@ extern "C" {
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn kernel_trap_handler(tf: &mut TrapContext) {
|
||||
// TODO: consider redesigning this, should we allow trap in kernel?
|
||||
// TODO: Replace CONSOLE Mutex to ReentrantLock
|
||||
unsafe { CONSOLE.force_unlock() }
|
||||
trap_handler(tf, true)
|
||||
}
|
||||
|
||||
fn trap_handler(_tf: &mut TrapContext, _from_kernel: bool) {
|
||||
fn trap_handler(tf: &mut TrapContext, from_kernel: bool) {
|
||||
let scause = riscv::register::scause::read();
|
||||
trace!("[Interrupt] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
|
||||
trace!("[Trap] cpu@{} scause: {:?}", HART_ID.get(), scause.cause());
|
||||
|
||||
macro_rules! panic_fatal {
|
||||
($msg:expr) => {
|
||||
panic!(
|
||||
"[Trap] {}, scause: {:?}, bits: {:#x}, sepc: {:#x}, stval: {:#x}",
|
||||
$msg,
|
||||
scause.cause(),
|
||||
scause.bits(),
|
||||
tf.sepc,
|
||||
riscv::register::stval::read()
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
match scause.cause() {
|
||||
T::Interrupt(I::SupervisorTimer) => {
|
||||
// TODO: refactor this
|
||||
trace!("[Interrupt] Tick: {}", riscv::register::time::read64());
|
||||
Timer::tick();
|
||||
if !from_kernel {
|
||||
SCHEDULER.schedule()
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!(
|
||||
"[Interrupt] Unhandled Trap, scause: {:?}, bits: {:#x}",
|
||||
scause.cause(),
|
||||
scause.bits()
|
||||
);
|
||||
T::Exception(E::UserEnvCall) => {
|
||||
// TODO: handle syscall
|
||||
tf.sepc += 4;
|
||||
},
|
||||
T::Interrupt(I::SupervisorExternal) => {
|
||||
// TODO: handle external interrupt, e.g. plic
|
||||
},
|
||||
T::Exception(E::InstructionPageFault) | T::Exception(E::LoadPageFault) | T::Exception(E::StorePageFault) => {
|
||||
if from_kernel {
|
||||
panic_fatal!("Page Fault in Kernel");
|
||||
}
|
||||
// TODO: handle page fault
|
||||
},
|
||||
_ => panic_fatal!("Unhandled Trap"),
|
||||
}
|
||||
trace!("[Interrupt] exiting...");
|
||||
|
||||
Timer::tick();
|
||||
trace!("[Trap] exiting...");
|
||||
}
|
||||
|
||||
impl TrapOps for Trap {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use num_traits::ToPrimitive;
|
||||
use utils::size::{GIB, KIB, MIB, TIB};
|
||||
use utils::MASK;
|
||||
use vspace::addr::{AddressOps, PhysAddr, VirtAddr};
|
||||
use vspace::paging::TableLevel;
|
||||
|
||||
@ -8,8 +9,8 @@ pub trait PhysAddrPaging {
|
||||
const PPN_BITS: usize;
|
||||
const PPN_OFFSET: usize;
|
||||
|
||||
const PA_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PG_OFFSET;
|
||||
const PTE_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PPN_OFFSET;
|
||||
const PA_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PG_OFFSET;
|
||||
const PTE_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PPN_OFFSET;
|
||||
|
||||
fn to_ppn(&self) -> usize
|
||||
where Self: AddressOps {
|
||||
@ -61,7 +62,7 @@ pub trait VirtAddrPaging {
|
||||
|
||||
fn lower_bits(&self, level: usize) -> usize
|
||||
where Self: AddressOps {
|
||||
self.as_usize() & ((1 << (Self::PG_OFFSET + Self::VPN_BITS * (level + 1))) - 1)
|
||||
self.as_usize() & MASK!(Self::PG_OFFSET + Self::VPN_BITS * (level + 1))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,10 @@ use log::{debug, error, info, warn};
|
||||
#[thread_local]
|
||||
pub static HART_ID: Cell<usize> = Cell::new(0);
|
||||
|
||||
pub fn rust_main(fdt: Fdt) -> ! {
|
||||
pub fn rust_main() -> ! {
|
||||
let fdt_addr = crate::arch::FDT.load(core::sync::atomic::Ordering::Acquire);
|
||||
let fdt = unsafe { Fdt::from_ptr(fdt_addr).unwrap() };
|
||||
|
||||
setup_console(&fdt);
|
||||
info!("Kernel Started");
|
||||
|
||||
|
@ -16,7 +16,8 @@
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(test_runner::runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![cfg_attr(test, allow(dead_code))]
|
||||
// no noisy dead_code warnings
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
@ -28,6 +29,7 @@ mod lang;
|
||||
mod logging;
|
||||
mod objects;
|
||||
mod plat;
|
||||
mod scheduler;
|
||||
|
||||
// test infrastructure
|
||||
#[cfg(test)]
|
||||
|
@ -1,23 +1,27 @@
|
||||
use api::cap::ObjectType;
|
||||
use vspace::addr::PhysAddr;
|
||||
|
||||
/// RawCap is the specific implementation of capability which stores in CNode
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct RawCap {
|
||||
// TODO: this could be an enum, figure out a way to do this
|
||||
/// args: in vanilla seL4 implementation, a cap use two 64-bit words to store all information,
|
||||
/// but we'll waste some space rather than using bitfield to simplify the implementation
|
||||
args: [usize; 2],
|
||||
pub args: [usize; 2],
|
||||
/// ptr: vanilla seL4 stores the pointer with either higher bits or lower bits cut off,
|
||||
/// which limits the address space. Use an independent field to store the pointer.
|
||||
/// Kernel will not manage memory, userspace should pass PhysAddr to kernel
|
||||
pub ptr: PhysAddr,
|
||||
/// cap_type: every capability links to one specific object
|
||||
cap_type: ObjectType,
|
||||
pub cap_type: ObjectType,
|
||||
}
|
||||
|
||||
impl RawCap {
|
||||
pub fn new(arg0: usize, arg1: usize, cap_type: ObjectType) -> Self {
|
||||
pub fn new(arg0: usize, arg1: usize, ptr: PhysAddr, cap_type: ObjectType) -> Self {
|
||||
Self {
|
||||
args: [arg0, arg1],
|
||||
ptr,
|
||||
cap_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cap_type(&self) -> ObjectType {
|
||||
self.cap_type
|
||||
}
|
||||
}
|
||||
|
127
kernel/src/objects/cnode.rs
Normal file
127
kernel/src/objects/cnode.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use core::cell::Cell;
|
||||
|
||||
use super::{cap::RawCap, Cap, KernelObject};
|
||||
use crate::arch::layout::mmap_phys_to_virt;
|
||||
use api::{cap::ObjectType, error::CapFault};
|
||||
use utils::MASK;
|
||||
use vspace::addr::{AddressOps, PhysAddr};
|
||||
|
||||
/// CNodeObject is a array of Capabilities (`RawCap`)
|
||||
/// The size of the array is stored in CNodeCap
|
||||
pub type CNodeObject = [Cell<RawCap>];
|
||||
|
||||
impl KernelObject for CNodeObject {
|
||||
const OBJ_TYPE: ObjectType = ObjectType::CNode;
|
||||
}
|
||||
|
||||
/*
|
||||
* in vanilla seL4, CNodeCap layout:
|
||||
* > args[0]: | cap_tag | guard_size | radix | cnode_ptr |
|
||||
* > | [63:59] | [58:53] | [52:47] | [46:0] |
|
||||
* > args[1]: guard
|
||||
*
|
||||
* in our implementation, CNodeCap layout:
|
||||
* > args[0]: [guard_size, radix]
|
||||
* > args[1]: guard
|
||||
* > ptr: cnode_ptr
|
||||
* > cap_type: cap_tag
|
||||
*/
|
||||
|
||||
pub type CNodeCap<'a> = Cap<'a, CNodeObject>;
|
||||
|
||||
impl<'a> CNodeCap<'a> {
|
||||
const GUARD_SIZE_BITS: usize = 6;
|
||||
const GUARD_SIZE_MASK: usize = MASK!(Self::GUARD_SIZE_BITS);
|
||||
const GUARD_SIZE_OFFSET: usize = Self::RADIX_BITS + Self::RADIX_OFFSET;
|
||||
const RADIX_BITS: usize = 6;
|
||||
const RADIX_MASK: usize = MASK!(Self::RADIX_BITS);
|
||||
const RADIX_OFFSET: usize = 0;
|
||||
|
||||
pub fn mint(radix: usize, guard_size: usize, guard: usize, ptr: PhysAddr) -> RawCap {
|
||||
let arg0 = ((radix & Self::RADIX_MASK) << Self::RADIX_OFFSET)
|
||||
| ((guard_size & Self::GUARD_SIZE_MASK) << Self::GUARD_SIZE_OFFSET);
|
||||
let arg1 = guard;
|
||||
|
||||
let cap = RawCap::new(arg0, arg1, ptr, ObjectType::CNode);
|
||||
CNodeCap::try_from(&Cell::new(cap))
|
||||
.unwrap()
|
||||
.as_object()
|
||||
.iter()
|
||||
.for_each(|cell| {
|
||||
cell.set(RawCap::default());
|
||||
});
|
||||
|
||||
cap
|
||||
}
|
||||
|
||||
fn radix(&self) -> usize {
|
||||
(self.cap.get().args[0] >> Self::RADIX_OFFSET) & Self::RADIX_MASK
|
||||
}
|
||||
|
||||
fn guard_size(&self) -> usize {
|
||||
(self.cap.get().args[0] >> Self::GUARD_SIZE_OFFSET) & Self::GUARD_SIZE_MASK
|
||||
}
|
||||
|
||||
fn guard(&self) -> usize {
|
||||
self.cap.get().args[1]
|
||||
}
|
||||
|
||||
/// CNodeObject length
|
||||
fn length(&self) -> usize {
|
||||
// Return the limited size of the CNode, Rust will help us to check the boundary :)
|
||||
// > the kernel then uses the next most-significant `radix` bits of the
|
||||
// > capability address as an `index` into the CNode to which the CNode capability refers
|
||||
1 << self.radix()
|
||||
}
|
||||
|
||||
fn as_object(&self) -> &CNodeObject {
|
||||
unsafe {
|
||||
let virt = mmap_phys_to_virt(self.cap.get().ptr);
|
||||
core::slice::from_raw_parts(virt.as_const_ptr(), self.length())
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_address_bits(&self, cap_ptr: usize, n_bits: usize) -> Result<&Cell<RawCap>, CapFault> {
|
||||
let mut bits_remaining = n_bits;
|
||||
let mut slot = self.cap;
|
||||
|
||||
while let Ok(cnode) = CNodeCap::try_from(slot) {
|
||||
let radix_bits = cnode.radix();
|
||||
let guard_bits = cnode.guard_size();
|
||||
let level_bits = radix_bits + guard_bits;
|
||||
let cap_guard = cnode.guard();
|
||||
|
||||
assert_ne!(level_bits, 0);
|
||||
|
||||
if level_bits > bits_remaining {
|
||||
return Err(CapFault::DepthMismatch {
|
||||
bits_found: level_bits,
|
||||
bits_left: bits_remaining,
|
||||
});
|
||||
}
|
||||
|
||||
let get_guard = || (cap_ptr >> (bits_remaining - guard_bits)) & MASK!(guard_bits);
|
||||
if guard_bits > bits_remaining || get_guard() != cap_guard {
|
||||
return Err(CapFault::GuardMismatch {
|
||||
bits_left: bits_remaining,
|
||||
guard_found: cap_guard,
|
||||
guard_size: guard_bits,
|
||||
});
|
||||
}
|
||||
|
||||
let offset = (cap_ptr >> (bits_remaining - level_bits)) & MASK!(radix_bits);
|
||||
slot = unsafe { &*(&cnode.as_object()[offset] as *const _) };
|
||||
|
||||
if bits_remaining == level_bits {
|
||||
return Ok(slot);
|
||||
}
|
||||
|
||||
bits_remaining -= level_bits;
|
||||
}
|
||||
|
||||
if CNodeCap::try_from(slot).is_err() {
|
||||
return Err(CapFault::InvalidRoot);
|
||||
}
|
||||
Ok(slot)
|
||||
}
|
||||
}
|
@ -14,22 +14,41 @@
|
||||
|
||||
*/
|
||||
|
||||
use api::cap::ObjectType;
|
||||
use api::{
|
||||
cap::ObjectType,
|
||||
error::{SysError, SysResult},
|
||||
};
|
||||
use cap::RawCap;
|
||||
use core::cell::Cell;
|
||||
use core::marker::PhantomData;
|
||||
use core::{cell::Cell, marker::PhantomData};
|
||||
|
||||
pub mod cap;
|
||||
pub mod cnode;
|
||||
pub mod null;
|
||||
pub mod untyped;
|
||||
|
||||
/// 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`
|
||||
/// And for those objects, at least implement `mint` method and `decodeInvocation` (not enforcing in `KernelObject` for complexity)
|
||||
pub struct Cap<'a, T: KernelObject> {
|
||||
cap: &'a Cell<RawCap>,
|
||||
pub struct Cap<'a, T: KernelObject + ?Sized> {
|
||||
cap: &'a Cell<RawCap>, // use Cell to avoid lifetime issues
|
||||
cap_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: KernelObject + ?Sized> TryFrom<&'a Cell<RawCap>> for Cap<'a, T> {
|
||||
type Error = SysError;
|
||||
|
||||
fn try_from(new: &'a Cell<RawCap>) -> SysResult<Self> {
|
||||
if new.get().cap_type != T::OBJ_TYPE {
|
||||
Err(SysError::CapTypeMismatch)
|
||||
} else {
|
||||
Ok(Self {
|
||||
cap: new,
|
||||
cap_type: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// KernelObject is the base trait for all kernel objects
|
||||
pub trait KernelObject {
|
||||
// this should be optimized by compiler?
|
||||
|
@ -1,27 +1,17 @@
|
||||
use super::cap::RawCap;
|
||||
use super::{Cap, KernelObject};
|
||||
use api::cap::ObjectType;
|
||||
use core::marker::PhantomData;
|
||||
use vspace::addr::PhysAddr;
|
||||
|
||||
/// NullObject is used as empty (capability) slot
|
||||
pub struct NullObject {}
|
||||
pub type NullCap<'a> = Cap<'a, NullObject>;
|
||||
impl KernelObject for NullObject {
|
||||
const OBJ_TYPE: ObjectType = ObjectType::Null;
|
||||
}
|
||||
|
||||
pub type NullCap<'a> = Cap<'a, NullObject>;
|
||||
impl<'a> NullCap<'a> {
|
||||
pub fn mint() -> RawCap {
|
||||
let cap = RawCap::new(0, 0, ObjectType::Null);
|
||||
cap
|
||||
}
|
||||
|
||||
pub fn retype<T: KernelObject>(self, new: RawCap) -> Cap<'a, T> {
|
||||
assert!(T::OBJ_TYPE == new.cap_type());
|
||||
self.cap.set(new);
|
||||
Cap {
|
||||
cap: self.cap,
|
||||
cap_type: PhantomData,
|
||||
}
|
||||
RawCap::new(0, 0, PhysAddr(0), ObjectType::Null)
|
||||
}
|
||||
}
|
||||
|
129
kernel/src/objects/untyped.rs
Normal file
129
kernel/src/objects/untyped.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use super::cap::RawCap;
|
||||
use super::cnode::{CNodeCap, CNodeObject};
|
||||
use super::null::NullCap;
|
||||
use super::{Cap, KernelObject};
|
||||
use api::cap::ObjectType;
|
||||
use api::error::{SysError, SysResult};
|
||||
use utils::then::Then;
|
||||
use utils::MASK;
|
||||
use vspace::addr::{align_up, PhysAddr};
|
||||
|
||||
/// UntypedObject is used as raw memory (associated with PhysAddr)
|
||||
/// It can be further retyped to other objects (TCB, CNode ...)
|
||||
pub struct UntypedObject {}
|
||||
|
||||
impl KernelObject for UntypedObject {
|
||||
const OBJ_TYPE: ObjectType = ObjectType::Untyped;
|
||||
}
|
||||
|
||||
/*
|
||||
* in vanilla seL4, UntypedCap layout:
|
||||
* > args[0]: | cap_tag | unused | cap_ptr |
|
||||
* > | [63:59] | unused | [38:0] |
|
||||
* > args[1]: | free_index | unused | is_device | block_bits |
|
||||
* > | [63:25] | unused | [6:6] | [5:0] |
|
||||
*
|
||||
* in our implementation, UntypedCap layout:
|
||||
* > NOTE: we are not storing bits, but bytes. free_offset is in bytes.
|
||||
* > args[0]: free_offset
|
||||
* > args[1]: [is_device, block_bits]
|
||||
* > ptr: cap_ptr
|
||||
* > cap_type: cap_tag
|
||||
*/
|
||||
|
||||
pub type UntypedCap<'a> = Cap<'a, UntypedObject>;
|
||||
impl UntypedCap<'_> {
|
||||
pub fn mint(free_offset: usize, block_bits: usize, is_device: bool, ptr: PhysAddr) -> RawCap {
|
||||
RawCap::new(
|
||||
free_offset,
|
||||
((is_device as usize) << 6) | (block_bits & MASK!(6)),
|
||||
ptr,
|
||||
ObjectType::Untyped,
|
||||
)
|
||||
}
|
||||
|
||||
fn free_offset(&self) -> usize {
|
||||
self.cap.get().args[0]
|
||||
}
|
||||
|
||||
fn set_free_offset(&self, free_offset: usize) {
|
||||
self.cap.get().args[0] = free_offset;
|
||||
}
|
||||
|
||||
fn is_device(&self) -> bool {
|
||||
(self.cap.get().args[1] >> 6) & 1 == 1
|
||||
}
|
||||
|
||||
fn block_bits(&self) -> usize {
|
||||
self.cap.get().args[1] & MASK!(6)
|
||||
}
|
||||
|
||||
fn block_size(&self) -> usize {
|
||||
1 << self.block_bits()
|
||||
}
|
||||
|
||||
pub fn retype(&self, obj_type: ObjectType, user_obj_bits: usize, slots: &CNodeObject) -> SysResult<()> {
|
||||
/*
|
||||
* vallina seL4: `decode_untiped_invocation`
|
||||
* - _service CPTR to an untyped object.
|
||||
* - type The seL4 object type that we are retyping to.
|
||||
* - size_bits Used to determine the size of variable-sized objects.
|
||||
* - root CPTR to the CNode at the root of the destination CSpace.
|
||||
* - node_index CPTR to the destination CNode. Resolved relative to the root parameter.
|
||||
* - node_depth Number of bits of node_index to translate when addressing the destination CNode.
|
||||
* - node_offset Number of slots into the node at which capabilities start being placed.
|
||||
* - num_objects Number of capabilities to create.
|
||||
*
|
||||
* for our `retype`
|
||||
* - _service is just self
|
||||
* - type is passed by obj_type
|
||||
* - size_bits passed by user_obj_bits
|
||||
* - root + node_index + node_depth are used by `resolve_address_bits`
|
||||
* - cnode[node_offset +: num_objects] being cut-off from previous, passing as slots
|
||||
*/
|
||||
|
||||
// Make sure all slots are empty
|
||||
slots
|
||||
.iter()
|
||||
.any(|cap: &core::cell::Cell<RawCap>| NullCap::try_from(cap).is_err())
|
||||
.then_ok((), SysError::CapTypeMismatch)?;
|
||||
|
||||
// Start allocating from free_offset
|
||||
// Notice: in vallina seL4, it will check whether there are child nodes,
|
||||
// if not, will ignore free_index and start from 0.
|
||||
// We won't do this check, so we need to make sure free_offset is valid anytime.
|
||||
let obj_size = obj_type.size(user_obj_bits);
|
||||
let start_offset = align_up(self.free_offset(), obj_size);
|
||||
|
||||
// Check whether the size is valid
|
||||
let objs_size = obj_size * slots.len();
|
||||
if start_offset + objs_size > self.block_size() {
|
||||
return Err(SysError::RangeError);
|
||||
}
|
||||
|
||||
// Check whether we are handling device memory
|
||||
if self.is_device() && obj_type != ObjectType::Frame && obj_type != ObjectType::Untyped {
|
||||
// TODO: we might split frame into tera, giga, mega, kilo...
|
||||
return Err(SysError::InvalidArgument);
|
||||
}
|
||||
|
||||
// Create new capabilities in slot
|
||||
for (i, slot) in slots.iter().enumerate() {
|
||||
let addr = self.cap.get().ptr + start_offset + i * obj_size;
|
||||
let new_cap = match obj_type {
|
||||
ObjectType::Untyped => UntypedCap::mint(0, obj_type.bits(user_obj_bits), self.is_device(), addr),
|
||||
ObjectType::CNode => CNodeCap::mint(user_obj_bits, 0, 0, addr),
|
||||
// TODO: other object types
|
||||
_ => return Err(SysError::InvalidArgument),
|
||||
};
|
||||
|
||||
slot.set(new_cap);
|
||||
// TODO: insert into linked list
|
||||
}
|
||||
|
||||
// Update free_offset
|
||||
self.set_free_offset(start_offset + objs_size);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
15
kernel/src/scheduler/mod.rs
Normal file
15
kernel/src/scheduler/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
pub static SCHEDULER: Scheduler = Scheduler::new();
|
||||
|
||||
pub struct Scheduler {}
|
||||
|
||||
impl Scheduler {
|
||||
pub const fn new() -> Self {
|
||||
// TODO: implement Scheduler::new
|
||||
Self {}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn schedule(&self) {
|
||||
todo!("Scheduler::schedule");
|
||||
}
|
||||
}
|
@ -119,7 +119,7 @@ impl FreeList {
|
||||
pub fn reserve(&mut self, start: PhysAddr, size: usize) {
|
||||
// NOTE: only support inclusive range
|
||||
if let Some((region, _)) =
|
||||
self.alloc_node(|region| (region.start_addr() <= start && start < region.end_addr()).some(|| (), ()))
|
||||
self.alloc_node(|region| (region.start_addr() <= start && start < region.end_addr()).then_ok((), ()))
|
||||
{
|
||||
let region_start = region.start_addr();
|
||||
let region_end = region.end_addr();
|
||||
|
7
lib/utils/src/assert.rs
Normal file
7
lib/utils/src/assert.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub struct Assert<const CONDITION: bool> {}
|
||||
|
||||
pub trait AssertTrue {}
|
||||
impl AssertTrue for Assert<true> {}
|
||||
|
||||
pub trait AssertFalse {}
|
||||
impl AssertFalse for Assert<false> {}
|
27
lib/utils/src/atomic.rs
Normal file
27
lib/utils/src/atomic.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
||||
pub struct AtomicConstPtr<T>(AtomicPtr<T>);
|
||||
|
||||
impl<T> Default for AtomicConstPtr<T> {
|
||||
fn default() -> Self {
|
||||
Self(AtomicPtr::default())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for AtomicConstPtr<T> {}
|
||||
|
||||
unsafe impl<T> Sync for AtomicConstPtr<T> {}
|
||||
|
||||
impl<T> AtomicConstPtr<T> {
|
||||
pub const fn new(p: *const T) -> Self {
|
||||
Self(AtomicPtr::new(p as *mut T))
|
||||
}
|
||||
|
||||
pub fn load(&self, order: Ordering) -> *const T {
|
||||
self.0.load(order) as *const T
|
||||
}
|
||||
|
||||
pub fn store(&self, ptr: *mut T, order: Ordering) {
|
||||
self.0.store(ptr as *mut T, order)
|
||||
}
|
||||
}
|
6
lib/utils/src/bin.rs
Normal file
6
lib/utils/src/bin.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#[macro_export]
|
||||
macro_rules! MASK {
|
||||
($bits:expr) => {
|
||||
((1 << $bits) - 1)
|
||||
};
|
||||
}
|
8
lib/utils/src/container_of.rs
Normal file
8
lib/utils/src/container_of.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// yet another kernel's container_of macro for Rust
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! container_of {
|
||||
($ptr:expr, $type:ty, $field:ident) => {
|
||||
($ptr as *const u8).sub(core::mem::offset_of!($type, $field) as usize) as *const $type
|
||||
};
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
#![no_std]
|
||||
#![feature(extern_types)]
|
||||
|
||||
pub mod assert;
|
||||
pub mod atomic;
|
||||
pub mod bin;
|
||||
pub mod container_of;
|
||||
pub mod extern_addr;
|
||||
pub mod function_name;
|
||||
pub mod size;
|
||||
|
@ -1,22 +1,42 @@
|
||||
pub trait Then {
|
||||
fn and<T, E, F: FnOnce() -> Result<T, E>>(self, f: F, err: E) -> Result<T, E>;
|
||||
fn some<T, E, F: FnOnce() -> T>(self, f: F, err: E) -> Result<T, E>;
|
||||
fn then_ok<T, E>(self, ok: T, err: E) -> Result<T, E>;
|
||||
fn then_err<T, E>(self, ok: T, err: E) -> Result<T, E>;
|
||||
fn else_ok<T, E>(self, ok: T, err: E) -> Result<T, E>;
|
||||
fn else_err<T, E>(self, ok: T, err: E) -> Result<T, E>;
|
||||
}
|
||||
|
||||
impl Then for bool {
|
||||
#[inline]
|
||||
fn and<T, E, F: FnOnce() -> Result<T, E>>(self, f: F, err: E) -> Result<T, E> {
|
||||
fn then_ok<T, E>(self, ok: T, err: E) -> Result<T, E> {
|
||||
if self {
|
||||
f()
|
||||
Ok(ok)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn some<T, E, F: FnOnce() -> T>(self, f: F, err: E) -> Result<T, E> {
|
||||
fn then_err<T, E>(self, ok: T, err: E) -> Result<T, E> {
|
||||
if !self {
|
||||
Ok(ok)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn else_ok<T, E>(self, ok: T, err: E) -> Result<T, E> {
|
||||
if !self {
|
||||
Ok(ok)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn else_err<T, E>(self, ok: T, err: E) -> Result<T, E> {
|
||||
if self {
|
||||
Ok(f())
|
||||
Ok(ok)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user