Struct kernel::kernel::Kernel

source ·
pub struct Kernel {
    processes: &'static [Option<&'static dyn Process>],
    process_identifier_max: Cell<usize>,
    grant_counter: Cell<usize>,
    grants_finalized: Cell<bool>,
}
Expand description

Main object for the kernel. Each board will need to create one.

Fields§

§processes: &'static [Option<&'static dyn Process>]

This holds a pointer to the static array of Process pointers.

§process_identifier_max: Cell<usize>

A counter which keeps track of how many process identifiers have been created. This is used to create new unique identifiers for processes.

§grant_counter: Cell<usize>

How many grant regions have been setup. This is incremented on every call to create_grant(). We need to explicitly track this so that when processes are created they can be allocated pointers for each grant.

§grants_finalized: Cell<bool>

Flag to mark that grants have been finalized. This means that the kernel cannot support creating new grants because processes have already been created and the data structures for grants have already been established.

Implementations§

source§

impl Kernel

source

pub fn new(processes: &'static [Option<&'static dyn Process>]) -> Kernel

Create the kernel object that knows about the list of processes.

Crucially, the processes included in the processes array MUST be valid to execute. Any credential checks or validation MUST happen before the Process object is included in this array.

source

pub(crate) fn get_process(&self, processid: ProcessId) -> Option<&dyn Process>

Helper function that moves all non-generic portions of process_map_or into a non-generic function to reduce code bloat from monomorphization.

source

pub(crate) fn process_map_or<F, R>( &self, default: R, processid: ProcessId, closure: F, ) -> R
where F: FnOnce(&dyn Process) -> R,

Run a closure on a specific process if it exists. If the process with a matching ProcessId does not exist at the index specified within the ProcessId, then default will be returned.

A match will not be found if the process was removed (and there is a None in the process array), if the process changed its identifier (likely after being restarted), or if the process was moved to a different index in the processes array. Note that a match will be found if the process still exists in the correct location in the array but is in any “stopped” state.

source

pub fn process_map_or_external<F, R>( &self, default: R, processid: ProcessId, closure: F, _capability: &dyn ProcessManagementCapability, ) -> R
where F: FnOnce(&dyn Process) -> R,

Run a closure on a specific process if it exists. If the process with a matching ProcessId does not exist at the index specified within the ProcessId, then default will be returned.

A match will not be found if the process was removed (and there is a None in the process array), if the process changed its identifier (likely after being restarted), or if the process was moved to a different index in the processes array. Note that a match will be found if the process still exists in the correct location in the array but is in any “stopped” state.

This is functionally the same as process_map_or(), but this method is available outside the kernel crate and requires a ProcessManagementCapability to use.

source

pub(crate) fn process_each<F>(&self, closure: F)
where F: FnMut(&dyn Process),

Run a closure on every valid process. This will iterate the array of processes and call the closure on every process that exists.

source

pub(crate) fn get_process_iter( &self, ) -> FilterMap<Iter<'_, Option<&dyn Process>>, fn(_: &Option<&'static dyn Process>) -> Option<&'static dyn Process>>

Returns an iterator over all processes loaded by the kernel

source

pub fn process_each_capability<F>( &'static self, _capability: &dyn ProcessManagementCapability, closure: F, )
where F: FnMut(&dyn Process),

Run a closure on every valid process. This will iterate the array of processes and call the closure on every process that exists.

This is functionally the same as process_each(), but this method is available outside the kernel crate and requires a ProcessManagementCapability to use.

source

pub(crate) fn process_until<T, F>(&self, closure: F) -> Option<T>
where F: Fn(&dyn Process) -> Option<T>,

Run a closure on every process, but only continue if the closure returns None. That is, if the closure returns any non-None value, iteration stops and the value is returned from this function to the called.

source

pub(crate) fn processid_is_valid(&self, processid: &ProcessId) -> bool

Checks if the provided ProcessId is still valid given the processes stored in the processes array. Returns true if the ProcessId still refers to a valid process, and false if not.

This is needed for ProcessId itself to implement the .index() command to verify that the referenced app is still at the correct index.

source

pub fn create_grant<T: Default, Upcalls: UpcallSize, AllowROs: AllowRoSize, AllowRWs: AllowRwSize>( &'static self, driver_num: usize, _capability: &dyn MemoryAllocationCapability, ) -> Grant<T, Upcalls, AllowROs, AllowRWs>

Create a new grant. This is used in board initialization to setup grants that capsules use to interact with processes.

Grants must only be created before processes are initialized. Processes use the number of grants that have been allocated to correctly initialize the process’s memory with a pointer for each grant. If a grant is created after processes are initialized this will panic.

Calling this function is restricted to only certain users, and to enforce this calling this function requires the MemoryAllocationCapability capability.

source

pub(crate) fn get_grant_count_and_finalize(&self) -> usize

Returns the number of grants that have been setup in the system and marks the grants as “finalized”. This means that no more grants can be created because data structures have been setup based on the number of grants when this function is called.

In practice, this is called when processes are created, and the process memory is setup based on the number of current grants.

source

pub fn get_grant_count_and_finalize_external( &self, _capability: &dyn ExternalProcessCapability, ) -> usize

Returns the number of grants that have been setup in the system and marks the grants as “finalized”. This means that no more grants can be created because data structures have been setup based on the number of grants when this function is called.

In practice, this is called when processes are created, and the process memory is setup based on the number of current grants.

This is exposed publicly, but restricted with a capability. The intent is that external implementations of Process need to be able to retrieve the final number of grants.

source

pub(crate) fn create_process_identifier(&self) -> usize

Create a new unique identifier for a process and return the identifier.

Typically we just choose a larger number than we have used for any process before which ensures that the identifier is unique.

source

pub fn hardfault_all_apps<C: ProcessManagementCapability>(&self, _c: &C)

Cause all apps to fault.

This will call set_fault_state() on each app, causing the app to enter the state as if it had crashed (for example with an MPU violation). If the process is configured to be restarted it will be.

Only callers with the ProcessManagementCapability can call this function. This restricts general capsules from being able to call this function, since capsules should not be able to arbitrarily restart all apps.

source

pub fn kernel_loop_operation<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>( &self, resources: &KR, chip: &C, ipc: Option<&IPC<NUM_PROCS>>, no_sleep: bool, _capability: &dyn MainLoopCapability, )

Perform one iteration of the core Tock kernel loop.

This function is responsible for three main operations:

  1. Check if the kernel itself has any work to be done and if the scheduler wants to complete that work now. If so, it allows the kernel to run.
  2. Check if any processes have any work to be done, and if so if the scheduler wants to allow any processes to run now, and if so which one.
  3. After ensuring the scheduler does not want to complete any kernel or process work (or there is no work to be done), are there are no outstanding interrupts to handle, put the chip to sleep.

This function has one configuration option: no_sleep. If that argument is set to true, the kernel will never attempt to put the chip to sleep, and this function can be called again immediately.

source

pub fn kernel_loop<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>( &self, resources: &KR, chip: &C, ipc: Option<&IPC<NUM_PROCS>>, capability: &dyn MainLoopCapability, ) -> !

Main loop of the OS.

Most of the behavior of this loop is controlled by the Scheduler implementation in use.

source

fn do_process<KR: KernelResources<C>, C: Chip, const NUM_PROCS: u8>( &self, resources: &KR, chip: &C, process: &dyn Process, ipc: Option<&IPC<NUM_PROCS>>, timeslice_us: Option<u32>, ) -> (StoppedExecutingReason, Option<u32>)

Transfer control from the kernel to a userspace process.

This function is called by the main kernel loop to run userspace code. Notably, system calls from processes are handled in the kernel, by the kernel thread in this function, and the syscall return value is set for the process immediately. Normally, a process is allowed to continue running after calling a syscall. However, the scheduler is given an out, as do_process() will check with the scheduler before re-executing the process to allow it to return from the syscall. If a process yields with no upcalls pending, exits, exceeds its timeslice, or is interrupted, then do_process() will return.

Depending on the particular scheduler in use, this function may act in a few different ways. scheduler.continue_process() allows the scheduler to tell the Kernel whether to continue executing the process, or to return control to the scheduler as soon as a kernel task becomes ready (either a bottom half interrupt handler or dynamic deferred call), or to continue executing the userspace process until it reaches one of the aforementioned stopping conditions. Some schedulers may not require a scheduler timer; passing None for the timeslice will use a null scheduler timer even if the chip provides a real scheduler timer. Schedulers can pass a timeslice (in us) of their choice, though if the passed timeslice is smaller than MIN_QUANTA_THRESHOLD_US the process will not execute, and this function will return immediately.

This function returns a tuple indicating the reason the reason this function has returned to the scheduler, and the amount of time the process spent executing (or None if the process was run cooperatively). Notably, time spent in this function by the kernel, executing system calls or merely setting up the switch to/from userspace, is charged to the process.

source

fn handle_syscall<KR: KernelResources<C>, C: Chip>( &self, resources: &KR, process: &dyn Process, syscall: Syscall, )

Method to invoke a system call on a particular process. Applies the kernel system call filtering policy (if any). Handles Yield and Exit, dispatches Memop to memop::memop, and dispatches peripheral driver system calls to peripheral driver capsules through the platforms with_driver method.

Auto Trait Implementations§

§

impl !Freeze for Kernel

§

impl !RefUnwindSafe for Kernel

§

impl !Send for Kernel

§

impl !Sync for Kernel

§

impl Unpin for Kernel

§

impl !UnwindSafe for Kernel

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.