Compare commits

...

13 Commits

24 changed files with 530 additions and 67 deletions

20
.vscode/settings.json vendored Normal file
View 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,
}
}

View File

@ -1,4 +1,4 @@
#[derive(Clone, Copy, Eq, PartialEq, FromPrimitive, ToPrimitive)] #[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum ObjectType { pub enum ObjectType {
Null = 0, Null = 0,
CNode = 1, CNode = 1,
@ -18,3 +18,26 @@ impl Default for ObjectType {
Self::Null 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
View 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>;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![feature(custom_inner_attributes)]
#[macro_use] #[macro_use]
extern crate num_derive; extern crate num_derive;
pub mod cap; pub mod cap;
pub mod error;

View File

@ -5,9 +5,9 @@ use crate::arch::vspace::is_kernel_pagetable_installed;
unsafe fn get_qemu_test_device() -> *mut u32 { unsafe fn get_qemu_test_device() -> *mut u32 {
if is_kernel_pagetable_installed() { if is_kernel_pagetable_installed() {
return mmap_phys_to_virt(TEST_DEVICE.into()).into(); mmap_phys_to_virt(TEST_DEVICE.into()).into()
} else { } else {
return TEST_DEVICE; TEST_DEVICE
} }
} }

View File

@ -1,16 +1,16 @@
use crate::arch::init_early_console; 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::arch::vspace::{setup_kernel_paging, setup_memory};
use crate::entry::{rust_main, HART_ID}; use crate::entry::{rust_main, HART_ID};
use crate::plat::console::mute_console; use crate::plat::console::mute_console;
use fdt::Fdt; use utils::atomic::AtomicConstPtr;
use vspace::addr::AddressOps; use vspace::addr::AddressOps;
#[naked] #[naked]
#[no_mangle] #[no_mangle]
#[allow(named_asm_labels)] #[allow(named_asm_labels)]
#[link_section = ".text.entry"] #[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 // NOTE: no stack here
// we should be launched by OpenSBI and running is S-mode // 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) { unsafe fn pre_main(hart_id: usize, fdt_addr: usize) {
zero_bss(); 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) // after kernel paging, board level early console is broken (no address mapping)
mute_console(); mute_console();
let fdt = unsafe { Fdt::from_ptr(mmap_phys_to_virt(fdt_addr.into()).as_const_ptr()).unwrap() }; let fdt_addr: *const u8 = mmap_phys_to_virt(fdt_addr.into()).as_const_ptr();
rust_main(fdt); FDT.store(fdt_addr as *mut u8, core::sync::atomic::Ordering::Release);
rust_main();
} }

View File

@ -11,10 +11,6 @@ cfg_if! {
} }
} }
// Console: plat/console.rs
pub use board::console::init_early_console;
pub use board::console::EarlyConsole;
mod entry; mod entry;
pub mod layout; pub mod layout;
mod lowlevel; mod lowlevel;
@ -22,3 +18,10 @@ mod timer;
mod tls; mod tls;
pub mod trap; pub mod trap;
pub mod vspace; 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;

View File

@ -1,12 +1,11 @@
use crate::entry::HART_ID; use crate::entry::HART_ID;
use crate::plat::console::CONSOLE; 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::timer::{Timer, TimerOps};
use crate::plat::trap::{Trap, TrapContextOps, TrapOps}; 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/trap64.S"));
core::arch::global_asm!(include_str!("./asm/trap_common.S")); core::arch::global_asm!(include_str!("./asm/trap_common.S"));
@ -18,29 +17,52 @@ extern "C" {
#[no_mangle] #[no_mangle]
extern "C" fn kernel_trap_handler(tf: &mut TrapContext) { 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() } unsafe { CONSOLE.force_unlock() }
trap_handler(tf, true) 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(); 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() { match scause.cause() {
T::Interrupt(I::SupervisorTimer) => { T::Interrupt(I::SupervisorTimer) => {
// TODO: refactor this if !from_kernel {
trace!("[Interrupt] Tick: {}", riscv::register::time::read64()); SCHEDULER.schedule()
Timer::tick(); }
}, },
_ => { T::Exception(E::UserEnvCall) => {
panic!( // TODO: handle syscall
"[Interrupt] Unhandled Trap, scause: {:?}, bits: {:#x}", tf.sepc += 4;
scause.cause(),
scause.bits()
);
}, },
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 { impl TrapOps for Trap {

View File

@ -1,5 +1,6 @@
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
use utils::size::{GIB, KIB, MIB, TIB}; use utils::size::{GIB, KIB, MIB, TIB};
use utils::MASK;
use vspace::addr::{AddressOps, PhysAddr, VirtAddr}; use vspace::addr::{AddressOps, PhysAddr, VirtAddr};
use vspace::paging::TableLevel; use vspace::paging::TableLevel;
@ -8,8 +9,8 @@ pub trait PhysAddrPaging {
const PPN_BITS: usize; const PPN_BITS: usize;
const PPN_OFFSET: usize; const PPN_OFFSET: usize;
const PA_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PG_OFFSET; const PA_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PG_OFFSET;
const PTE_PPN_MASK: usize = ((1 << Self::PPN_BITS) - 1) << Self::PPN_OFFSET; const PTE_PPN_MASK: usize = MASK!(Self::PPN_BITS) << Self::PPN_OFFSET;
fn to_ppn(&self) -> usize fn to_ppn(&self) -> usize
where Self: AddressOps { where Self: AddressOps {
@ -61,7 +62,7 @@ pub trait VirtAddrPaging {
fn lower_bits(&self, level: usize) -> usize fn lower_bits(&self, level: usize) -> usize
where Self: AddressOps { 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))
} }
} }

View File

@ -9,7 +9,10 @@ use log::{debug, error, info, warn};
#[thread_local] #[thread_local]
pub static HART_ID: Cell<usize> = Cell::new(0); 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); setup_console(&fdt);
info!("Kernel Started"); info!("Kernel Started");

View File

@ -16,7 +16,8 @@
#![feature(custom_test_frameworks)] #![feature(custom_test_frameworks)]
#![test_runner(test_runner::runner)] #![test_runner(test_runner::runner)]
#![reexport_test_harness_main = "test_main"] #![reexport_test_harness_main = "test_main"]
#![cfg_attr(test, allow(dead_code))] // no noisy dead_code warnings
#![allow(dead_code)]
#[macro_use] #[macro_use]
extern crate static_assertions; extern crate static_assertions;
@ -28,6 +29,7 @@ mod lang;
mod logging; mod logging;
mod objects; mod objects;
mod plat; mod plat;
mod scheduler;
// test infrastructure // test infrastructure
#[cfg(test)] #[cfg(test)]

View File

@ -1,23 +1,27 @@
use api::cap::ObjectType; use api::cap::ObjectType;
use vspace::addr::PhysAddr;
/// RawCap is the specific implementation of capability which stores in CNode /// RawCap is the specific implementation of capability which stores in CNode
#[derive(Clone, Copy, Default)]
pub struct RawCap { 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, /// 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 /// 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: every capability links to one specific object
cap_type: ObjectType, pub cap_type: ObjectType,
} }
impl RawCap { 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 { Self {
args: [arg0, arg1], args: [arg0, arg1],
ptr,
cap_type, cap_type,
} }
} }
pub fn cap_type(&self) -> ObjectType {
self.cap_type
}
} }

127
kernel/src/objects/cnode.rs Normal file
View 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)
}
}

View File

@ -14,22 +14,41 @@
*/ */
use api::cap::ObjectType; use api::{
cap::ObjectType,
error::{SysError, SysResult},
};
use cap::RawCap; use cap::RawCap;
use core::cell::Cell; use core::{cell::Cell, marker::PhantomData};
use core::marker::PhantomData;
pub mod cap; pub mod cap;
pub mod cnode;
pub mod null; 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) /// 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` /// 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) /// And for those objects, at least implement `mint` method and `decodeInvocation` (not enforcing in `KernelObject` for complexity)
pub struct Cap<'a, T: KernelObject> { pub struct Cap<'a, T: KernelObject + ?Sized> {
cap: &'a Cell<RawCap>, cap: &'a Cell<RawCap>, // use Cell to avoid lifetime issues
cap_type: PhantomData<T>, 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 /// KernelObject is the base trait for all kernel objects
pub trait KernelObject { pub trait KernelObject {
// this should be optimized by compiler? // this should be optimized by compiler?

View File

@ -1,27 +1,17 @@
use super::cap::RawCap; use super::cap::RawCap;
use super::{Cap, KernelObject}; use super::{Cap, KernelObject};
use api::cap::ObjectType; use api::cap::ObjectType;
use core::marker::PhantomData; use vspace::addr::PhysAddr;
/// NullObject is used as empty (capability) slot /// NullObject is used as empty (capability) slot
pub struct NullObject {} pub struct NullObject {}
pub type NullCap<'a> = Cap<'a, NullObject>;
impl KernelObject for NullObject { impl KernelObject for NullObject {
const OBJ_TYPE: ObjectType = ObjectType::Null; const OBJ_TYPE: ObjectType = ObjectType::Null;
} }
pub type NullCap<'a> = Cap<'a, NullObject>;
impl<'a> NullCap<'a> { impl<'a> NullCap<'a> {
pub fn mint() -> RawCap { pub fn mint() -> RawCap {
let cap = RawCap::new(0, 0, ObjectType::Null); RawCap::new(0, 0, PhysAddr(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,
}
} }
} }

View 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(())
}
}

View 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");
}
}

View File

@ -119,7 +119,7 @@ impl FreeList {
pub fn reserve(&mut self, start: PhysAddr, size: usize) { pub fn reserve(&mut self, start: PhysAddr, size: usize) {
// NOTE: only support inclusive range // NOTE: only support inclusive range
if let Some((region, _)) = 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_start = region.start_addr();
let region_end = region.end_addr(); let region_end = region.end_addr();

7
lib/utils/src/assert.rs Normal file
View 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
View 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
View File

@ -0,0 +1,6 @@
#[macro_export]
macro_rules! MASK {
($bits:expr) => {
((1 << $bits) - 1)
};
}

View 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
};
}

View File

@ -1,6 +1,10 @@
#![no_std] #![no_std]
#![feature(extern_types)] #![feature(extern_types)]
pub mod assert;
pub mod atomic;
pub mod bin;
pub mod container_of;
pub mod extern_addr; pub mod extern_addr;
pub mod function_name; pub mod function_name;
pub mod size; pub mod size;

View File

@ -1,22 +1,42 @@
pub trait Then { pub trait Then {
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>;
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>;
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 { impl Then for bool {
#[inline] #[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 { if self {
f() Ok(ok)
} else { } else {
Err(err) Err(err)
} }
} }
#[inline] #[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 { if self {
Ok(f()) Ok(ok)
} else { } else {
Err(err) Err(err)
} }