refactor: unsafe only in adapter + libkos; forbid(unsafe_code) everywhere else; libkos Window (volatile, bounds-checked MMIO/DMA/shared mem), entry!, thread::spawn; MapRegion returns pages; kernel const-built (with_cores/install_platform); newtype addrs
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
// A kernel panic takes the whole system down, so kernel code may not
|
||||
// contain any way to panic. Tests and proofs are exempt.
|
||||
#![cfg_attr(
|
||||
@@ -85,6 +86,18 @@ pub struct Kernel<P: Platform> {
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub fn new(platform: P, pool_base: PhysicalAddress, pool_pages: usize) -> Self {
|
||||
let num_cores = platform.num_cores();
|
||||
Self::with_cores(platform, num_cores, pool_base, pool_pages)
|
||||
}
|
||||
|
||||
/// The kernel as a constant, so boot can place it in a static without
|
||||
/// building it on the stack: `platform` may be a placeholder until
|
||||
/// `install_platform`.
|
||||
pub const fn with_cores(
|
||||
platform: P,
|
||||
num_cores: u8,
|
||||
pool_base: PhysicalAddress,
|
||||
pool_pages: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
platform,
|
||||
processes: ProcessTable::new(),
|
||||
@@ -97,7 +110,7 @@ impl<P: Platform> Kernel<P> {
|
||||
regions: RegionTable::new(),
|
||||
irqs: IrqRoutingTable::new(),
|
||||
notifications: NotificationTable::new(),
|
||||
ipc: [syscall::IpcState::default(); MAX_THREADS],
|
||||
ipc: [syscall::IpcState::EMPTY; MAX_THREADS],
|
||||
pending_results: [const { None }; MAX_THREADS],
|
||||
homes: [types::id::CoreId::from_raw(0); MAX_THREADS],
|
||||
tree: domain::capability::tree::CapTree::new(),
|
||||
@@ -107,41 +120,11 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the kernel in place, one table at a time, to stay off the boot stack.
|
||||
///
|
||||
/// # Safety
|
||||
/// `slot` must be valid for writes and aligned; the old value is overwritten without drop.
|
||||
pub unsafe fn init_in_place(
|
||||
slot: *mut Self,
|
||||
platform: P,
|
||||
pool_base: PhysicalAddress,
|
||||
pool_pages: usize,
|
||||
) {
|
||||
use core::ptr::addr_of_mut;
|
||||
let num_cores = platform.num_cores();
|
||||
// SAFETY: the caller guarantees `slot` is valid and aligned; each
|
||||
// field is written exactly once.
|
||||
unsafe {
|
||||
addr_of_mut!((*slot).platform).write(platform);
|
||||
addr_of_mut!((*slot).processes).write(ProcessTable::new());
|
||||
addr_of_mut!((*slot).threads).write(ThreadTable::new());
|
||||
addr_of_mut!((*slot).endpoints).write(EndpointTable::new());
|
||||
addr_of_mut!((*slot).scheduler).write(MultiCoreScheduler::new(num_cores));
|
||||
addr_of_mut!((*slot).frames).write(FrameTable::new(pool_base, pool_pages));
|
||||
addr_of_mut!((*slot).budgets).write([Budget::EMPTY; MAX_PROCESSES]);
|
||||
addr_of_mut!((*slot).parents).write([None; MAX_PROCESSES]);
|
||||
addr_of_mut!((*slot).regions).write(RegionTable::new());
|
||||
addr_of_mut!((*slot).irqs).write(IrqRoutingTable::new());
|
||||
addr_of_mut!((*slot).notifications).write(NotificationTable::new());
|
||||
addr_of_mut!((*slot).ipc).write([syscall::IpcState::default(); MAX_THREADS]);
|
||||
addr_of_mut!((*slot).pending_results).write([const { None }; MAX_THREADS]);
|
||||
addr_of_mut!((*slot).homes).write([types::id::CoreId::from_raw(0); MAX_THREADS]);
|
||||
addr_of_mut!((*slot).tree).write(domain::capability::tree::CapTree::new());
|
||||
addr_of_mut!((*slot).heap_cursors).write([0; MAX_PROCESSES]);
|
||||
addr_of_mut!((*slot).death_watchers)
|
||||
.write([[None; domain::timer::MAX_WATCHERS]; MAX_PROCESSES]);
|
||||
addr_of_mut!((*slot).timers).write(domain::timer::Timers::new());
|
||||
}
|
||||
/// Hands over the real platform, which boot only has once the kernel
|
||||
/// is placed (see `with_cores`); before anything runs.
|
||||
pub fn install_platform(&mut self, platform: P) {
|
||||
self.scheduler.set_num_cores(platform.num_cores());
|
||||
self.platform = platform;
|
||||
}
|
||||
|
||||
pub fn boot(
|
||||
|
||||
@@ -47,6 +47,14 @@ pub struct IpcState {
|
||||
pub(crate) bound: Option<NotificationId>,
|
||||
}
|
||||
|
||||
impl IpcState {
|
||||
pub const EMPTY: Self = Self {
|
||||
outgoing: None,
|
||||
reply_to: None,
|
||||
bound: None,
|
||||
};
|
||||
}
|
||||
|
||||
impl<P: Platform> Kernel<P> {
|
||||
pub(crate) fn ipc(&self, tid: ThreadId) -> Option<&IpcState> {
|
||||
self.ipc.get(tid.raw() as usize)
|
||||
|
||||
@@ -104,7 +104,7 @@ impl<P: Platform> Kernel<P> {
|
||||
slot: CapSlot,
|
||||
va: VirtualAddress,
|
||||
perms: PagePerms,
|
||||
) -> Result<(), KernelError> {
|
||||
) -> Result<PageCount, KernelError> {
|
||||
let cap = self.check_cap(pid, slot, Permission::Read)?;
|
||||
let needed = match perms {
|
||||
PagePerms::ReadOnly => Permission::Read,
|
||||
@@ -118,7 +118,12 @@ impl<P: Platform> Kernel<P> {
|
||||
return Err(KernelError::WrongObject);
|
||||
};
|
||||
let via = self.cap_ref(pid, slot);
|
||||
self.map_region_via(id, pid, va, perms, via)
|
||||
self.map_region_via(id, pid, va, perms, via)?;
|
||||
// How much was mapped, so the caller never reaches past it.
|
||||
self.regions
|
||||
.get(id)
|
||||
.map(|r| r.pages())
|
||||
.ok_or(KernelError::NoSuchObject)
|
||||
}
|
||||
|
||||
/// Physical base of the region behind `slot` (needs Read); pool regions are contiguous.
|
||||
|
||||
@@ -96,7 +96,7 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
Syscall::MapRegion { region, va, perms } => complete(
|
||||
self.map_region_cap(pid, region, va, perms)
|
||||
.map(|()| SyscallResult::Ok),
|
||||
.map(|pages| SyscallResult::Value(pages.value() as usize)),
|
||||
),
|
||||
Syscall::RegionAddress { region } => complete(
|
||||
self.region_address_cap(pid, region)
|
||||
|
||||
@@ -171,9 +171,10 @@ fn init_can_map_a_device_through_its_cap() {
|
||||
va: VirtualAddress::from_raw(0x10_0000),
|
||||
perms: PagePerms::ReadWrite,
|
||||
});
|
||||
// It says how many pages it mapped: the device's one.
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
||||
SyscallOutcome::Complete(SyscallResult::Value(1))
|
||||
));
|
||||
let e = error_of(k.handle_syscall(Syscall::MapRegion {
|
||||
region,
|
||||
|
||||
Reference in New Issue
Block a user