Skip to content

Commit

Permalink
Merge pull request tock#4250 from tock/machine-register
Browse files Browse the repository at this point in the history
kernel: Introduce the `MachineRegister` type.
  • Loading branch information
ppannuto authored Feb 19, 2025
2 parents 9155594 + d366875 commit 1dbe75e
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 71 deletions.
4 changes: 2 additions & 2 deletions arch/cortex-m/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ impl<A: CortexMVariant> kernel::syscall::UserspaceKernelBoundary for SysCall<A>
// - Stack offset 4 is R12, which the syscall interface ignores
let stack_bottom = state.psp as *mut usize;
ptr::write(stack_bottom.offset(7), state.psr); //......... -> APSR
ptr::write(stack_bottom.offset(6), usize::from(callback.pc) | 1); //... -> PC
ptr::write(stack_bottom.offset(6), callback.pc.addr() | 1); //... -> PC
ptr::write(stack_bottom.offset(5), state.yield_pc | 1); // -> LR
ptr::write(stack_bottom.offset(3), callback.argument3.into()); // -> R3
ptr::write(stack_bottom.offset(3), callback.argument3.as_usize()); // -> R3
ptr::write(stack_bottom.offset(2), callback.argument2); // -> R2
ptr::write(stack_bottom.offset(1), callback.argument1); // -> R1
ptr::write(stack_bottom.offset(0), callback.argument0); // -> R0
Expand Down
4 changes: 2 additions & 2 deletions arch/rv32i/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl kernel::syscall::UserspaceKernelBoundary for SysCall {
state.regs[R_A0] = callback.argument0 as u32;
state.regs[R_A1] = callback.argument1 as u32;
state.regs[R_A2] = callback.argument2 as u32;
state.regs[R_A3] = callback.argument3.as_ptr::<()>() as usize as u32;
state.regs[R_A3] = callback.argument3.as_usize() as u32;

// We also need to set the return address (ra) register so that the new
// function that the process is running returns to the correct location.
Expand All @@ -209,7 +209,7 @@ impl kernel::syscall::UserspaceKernelBoundary for SysCall {
state.regs[R_RA] = state.pc;

// Save the PC we expect to execute.
state.pc = usize::from(callback.pc) as u32;
state.pc = callback.pc.addr() as u32;

Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion boards/hail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version.workspace = true
authors.workspace = true
build = "../build.rs"
edition.workspace = true
rust-version = "1.83"
rust-version = "1.84"

[dependencies]
components = { path = "../components" }
Expand Down
3 changes: 2 additions & 1 deletion kernel/src/grant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
use crate::processbuffer::{ReadOnlyProcessBufferRef, ReadWriteProcessBufferRef};
use crate::upcall::{Upcall, UpcallError, UpcallId};
use crate::utilities::capability_ptr::CapabilityPtr;
use crate::utilities::machine_register::MachineRegister;
use crate::ErrorCode;

/// Tracks how many upcalls a grant instance supports automatically.
Expand Down Expand Up @@ -731,7 +732,7 @@ impl<'a> GrantKernelData<'a> {
#[repr(C)]
#[derive(Default)]
struct SavedUpcall {
appdata: CapabilityPtr,
appdata: MachineRegister,
fn_ptr: CapabilityPtr,
}

Expand Down
3 changes: 2 additions & 1 deletion kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::storage_permissions;
use crate::syscall::{self, Syscall, SyscallReturn};
use crate::upcall::UpcallId;
use crate::utilities::capability_ptr::CapabilityPtr;
use crate::utilities::machine_register::MachineRegister;
use tock_tbf::types::CommandPermissions;

// Export all process related types via `kernel::process::`.
Expand Down Expand Up @@ -1086,7 +1087,7 @@ pub struct FunctionCall {
/// The third argument to the function.
pub argument2: usize,
/// The userdata provided by the process via `subscribe`
pub argument3: CapabilityPtr,
pub argument3: MachineRegister,
/// The PC of the function to execute.
pub pc: CapabilityPtr,
}
Expand Down
45 changes: 23 additions & 22 deletions kernel/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ use core::fmt::Write;
use crate::errorcode::ErrorCode;
use crate::process;
use crate::utilities::capability_ptr::CapabilityPtr;
use crate::utilities::machine_register::MachineRegister;

pub use crate::syscall_driver::{CommandReturn, SyscallDriver};

Expand Down Expand Up @@ -158,7 +159,7 @@ pub enum Syscall {
/// Upcall pointer to the upcall function.
upcall_ptr: CapabilityPtr,
/// Userspace application data.
appdata: CapabilityPtr,
appdata: MachineRegister,
},

/// Structure representing an invocation of the Command system call class.
Expand Down Expand Up @@ -241,53 +242,53 @@ impl Syscall {
pub fn from_register_arguments(
syscall_number: u8,
r0: usize,
r1: CapabilityPtr,
r2: CapabilityPtr,
r3: CapabilityPtr,
r1: MachineRegister,
r2: MachineRegister,
r3: MachineRegister,
) -> Option<Syscall> {
match SyscallClass::try_from(syscall_number) {
Ok(SyscallClass::Yield) => Some(Syscall::Yield {
which: r0,
param_a: r1.into(),
param_b: r2.into(),
param_a: r1.as_usize(),
param_b: r2.as_usize(),
}),
Ok(SyscallClass::Subscribe) => Some(Syscall::Subscribe {
driver_number: r0,
subdriver_number: r1.into(),
upcall_ptr: r2,
subdriver_number: r1.as_usize(),
upcall_ptr: r2.as_capability_ptr(),
appdata: r3,
}),
Ok(SyscallClass::Command) => Some(Syscall::Command {
driver_number: r0,
subdriver_number: r1.into(),
arg0: r2.into(),
arg1: r3.into(),
subdriver_number: r1.as_usize(),
arg0: r2.as_usize(),
arg1: r3.as_usize(),
}),
Ok(SyscallClass::ReadWriteAllow) => Some(Syscall::ReadWriteAllow {
driver_number: r0,
subdriver_number: r1.into(),
allow_address: r2.as_ptr::<u8>().cast_mut(),
allow_size: r3.into(),
subdriver_number: r1.as_usize(),
allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
allow_size: r3.as_usize(),
}),
Ok(SyscallClass::UserspaceReadableAllow) => Some(Syscall::UserspaceReadableAllow {
driver_number: r0,
subdriver_number: r1.into(),
allow_address: r2.as_ptr::<u8>().cast_mut(),
allow_size: r3.into(),
subdriver_number: r1.as_usize(),
allow_address: r2.as_capability_ptr().as_ptr::<u8>().cast_mut(),
allow_size: r3.as_usize(),
}),
Ok(SyscallClass::ReadOnlyAllow) => Some(Syscall::ReadOnlyAllow {
driver_number: r0,
subdriver_number: r1.into(),
allow_address: r2.as_ptr::<u8>().cast_mut(),
allow_size: r3.into(),
subdriver_number: r1.as_usize(),
allow_address: r2.as_capability_ptr().as_ptr(),
allow_size: r3.as_usize(),
}),
Ok(SyscallClass::Memop) => Some(Syscall::Memop {
operand: r0,
arg0: r1.into(),
arg0: r1.as_usize(),
}),
Ok(SyscallClass::Exit) => Some(Syscall::Exit {
which: r0,
completion_code: r1.into(),
completion_code: r1.as_usize(),
}),
Err(_) => None,
}
Expand Down
13 changes: 7 additions & 6 deletions kernel/src/upcall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::process;
use crate::process::ProcessId;
use crate::syscall::SyscallReturn;
use crate::utilities::capability_ptr::CapabilityPtr;
use crate::utilities::machine_register::MachineRegister;
use crate::ErrorCode;

/// Type to uniquely identify an upcall subscription across all drivers.
Expand Down Expand Up @@ -82,7 +83,7 @@ pub(crate) struct Upcall {
pub(crate) upcall_id: UpcallId,

/// The application data passed by the app when `subscribe()` was called.
pub(crate) appdata: CapabilityPtr,
pub(crate) appdata: MachineRegister,

/// A pointer to the first instruction of the function in the app that
/// corresponds to this upcall.
Expand All @@ -97,7 +98,7 @@ impl Upcall {
pub(crate) fn new(
process_id: ProcessId,
upcall_id: UpcallId,
appdata: CapabilityPtr,
appdata: MachineRegister,
fn_ptr: CapabilityPtr,
) -> Upcall {
Upcall {
Expand Down Expand Up @@ -199,8 +200,8 @@ impl Upcall {
/// include the function pointer of the upcall.
pub(crate) fn into_subscribe_success(self) -> SyscallReturn {
self.fn_ptr.map_or(
SyscallReturn::SubscribeSuccess(core::ptr::null::<()>(), self.appdata.into()),
|fp| SyscallReturn::SubscribeSuccess(fp.as_ptr(), self.appdata.into()),
SyscallReturn::SubscribeSuccess(core::ptr::null::<()>(), self.appdata.as_usize()),
|fp| SyscallReturn::SubscribeSuccess(fp.as_ptr(), self.appdata.as_usize()),
)
}

Expand All @@ -215,8 +216,8 @@ impl Upcall {
/// include the function pointer of the upcall.
pub(crate) fn into_subscribe_failure(self, err: ErrorCode) -> SyscallReturn {
self.fn_ptr.map_or(
SyscallReturn::SubscribeFailure(err, core::ptr::null::<()>(), self.appdata.into()),
|fp| SyscallReturn::SubscribeFailure(err, fp.as_ptr(), self.appdata.into()),
SyscallReturn::SubscribeFailure(err, core::ptr::null::<()>(), self.appdata.as_usize()),
|fp| SyscallReturn::SubscribeFailure(err, fp.as_ptr(), self.appdata.as_usize()),
)
}
}
76 changes: 40 additions & 36 deletions kernel/src/utilities/capability_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@
use core::fmt::{Formatter, LowerHex, UpperHex};
use core::ops::AddAssign;
use core::ptr::null;

use super::machine_register::MachineRegister;

/// A pointer to userspace memory with implied authority.
///
/// A [`CapabilityPtr`] points to memory a userspace process may be
/// permitted to read, write, or execute. It is sized exactly to a
/// CPU register that can pass values between userspace and the kernel.
/// Because it is register sized, [`CapabilityPtr`] is guaranteed to be
/// at least the size of a word ([usize]) [^note1]. Operations on the
/// pointer may affect permissions, e.g. offsetting the pointer beyond
/// the bounds of the memory object invalidates it. Like a `*const
/// ()`, a [`CapabilityPtr`] may also "hide" information by storing a
/// word of data with no memory access permissions.
/// A [`CapabilityPtr`] points to memory a userspace process may be permitted to
/// read, write, or execute. It is sized exactly to a CPU register that can pass
/// values between userspace and the kernel [^note1]. Operations on the pointer
/// may affect permissions, e.g. offsetting the pointer beyond the bounds of the
/// memory object may invalidate it.
///
/// [`CapabilityPtr`] should be used to store or pass a value between the
/// kernel and userspace that may represent a valid userspace reference,
Expand All @@ -32,14 +31,6 @@ pub struct CapabilityPtr {
ptr: *const (),
}

impl Default for CapabilityPtr {
fn default() -> Self {
Self {
ptr: core::ptr::null(),
}
}
}

/// Permission sets a [`CapabilityPtr`] may grant.
/// These may not be enforced or exist on a given platform.
#[derive(Copy, Clone, PartialEq)]
Expand All @@ -51,57 +42,70 @@ pub enum CapabilityPtrPermissions {
Execute,
}

impl From<CapabilityPtr> for usize {
/// Returns the address of the [`CapabilityPtr`].
/// Provenance note: may not expose provenance.
#[inline]
fn from(from: CapabilityPtr) -> Self {
from.ptr as usize
impl Default for CapabilityPtr {
/// Returns a null CapabilityPtr.
fn default() -> Self {
Self { ptr: null() }
}
}

impl From<usize> for CapabilityPtr {
/// Constructs a [`CapabilityPtr`] with a given address and no authority
///
/// Provenance note: may have null provenance.
/// Constructs a [`CapabilityPtr`] with a given address but no authority or
/// provenance.
#[inline]
fn from(from: usize) -> Self {
Self {
ptr: from as *const (),
// Ideally this would be core::ptr::without_provenance(from), but
// the CHERI toolchain is too old for without_provenance. This is
// equivalent.
ptr: null::<()>().with_addr(from),
}
}
}

// In addition to its publicly-documented capabilities, CapabilityPtr's
// implementation can also store integers. MachineRegister uses that ability to
// simplify its implementation. No other user of CapabilityPtr should rely on
// that ability.

impl From<usize> for MachineRegister {
fn from(from: usize) -> Self {
Self::from(CapabilityPtr::from(from))
}
}

impl UpperHex for CapabilityPtr {
/// Format the capability as an uppercase hex string.
/// Will print at least the address, and any platform specific metadata if it exists.
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
UpperHex::fmt(&(self.ptr as usize), f)
UpperHex::fmt(&self.ptr.addr(), f)
}
}

impl LowerHex for CapabilityPtr {
/// Format the capability as a lowercase hex string.
/// Will print at least the address, and any platform specific metadata if it exists.
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
LowerHex::fmt(&(self.ptr as usize), f)
LowerHex::fmt(&self.ptr.addr(), f)
}
}

impl AddAssign<usize> for CapabilityPtr {
/// Increments the address of a [`CapabilityPtr`]
/// Increments the address of a [`CapabilityPtr`]. If the pointer is offset
/// past its bounds, its authority may be invalidated.
#[inline]
fn add_assign(&mut self, rhs: usize) {
self.ptr = (self.ptr as *const u8).wrapping_add(rhs) as *const ();
self.ptr = self.ptr.wrapping_byte_add(rhs);
}
}

impl CapabilityPtr {
/// Returns the address of this pointer. Does not expose provenance.
pub fn addr(self) -> usize {
self.ptr.addr()
}

/// Returns the pointer component of a [`CapabilityPtr`] but without any of the authority.
pub fn as_ptr<T>(&self) -> *const T {
self.ptr as *const T
self.ptr.cast()
}

/// Construct a [`CapabilityPtr`] from a raw pointer, with authority ranging over
Expand Down
Loading

0 comments on commit 1dbe75e

Please sign in to comment.