1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Read Only State
//!
//! This capsule provides read only state to userspace applications.
//! This is similar to the Linux vDSO syscalls.
//!
//! The benefit of using these is that applications can avoid the context
//! switch overhead of traditional syscalls by just reading the value from
//! memory.
//!
//! The value will only be as accurate as the last time the application was
//! switched to by the kernel.
//!
//! The layout of the read only state in the allow region depends on the
//! version. Userspace can use `command 0` to get the version information.
//!
//! Versions are backwards compatible, that is new versions will only add
//! fields, not remove existing ones or change the order.
//!
//! ```text
//! Version 1:
//! |-------------------------|
//! | Switch Count (u32) |
//! |-------------------------|
//! | Pending Tasks (u32) |
//! |-------------------------|
//! | |
//! | Time Ticks (u64) |
//! |-------------------------|
//! ```
use core::cell::Cell;
use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
use kernel::hil::time::{Ticks, Time};
use kernel::platform::ContextSwitchCallback;
use kernel::process::{self, ProcessId};
use kernel::processbuffer::{UserspaceReadableProcessBuffer, WriteableProcessBuffer};
use kernel::syscall::{CommandReturn, SyscallDriver};
use kernel::ErrorCode;
/// Syscall driver number.
pub const DRIVER_NUM: usize = capsules_core::driver::NUM::ReadOnlyState as usize;
const VERSION: u32 = 1;
pub struct ReadOnlyStateDriver<'a, T: Time> {
timer: &'a T,
apps: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
}
impl<'a, T: Time> ReadOnlyStateDriver<'a, T> {
pub fn new(
timer: &'a T,
grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
) -> ReadOnlyStateDriver<'a, T> {
ReadOnlyStateDriver { timer, apps: grant }
}
}
impl<'a, T: Time> ContextSwitchCallback for ReadOnlyStateDriver<'a, T> {
fn context_switch_hook(&self, process: &dyn process::Process) {
let processid = process.processid();
let pending_tasks = process.pending_tasks();
self.apps
.enter(processid, |app, _| {
let count = app.count.get();
let _ = app.mem_region.mut_enter(|buf| {
if buf.len() >= 4 {
buf[0..4].copy_from_slice(&count.to_le_bytes());
}
if buf.len() >= 8 {
buf[4..8].copy_from_slice(&(pending_tasks as u32).to_le_bytes());
}
if buf.len() >= 16 {
let now = self.timer.now().into_usize() as u64;
buf[8..16].copy_from_slice(&now.to_le_bytes());
}
});
app.count.set(count.wrapping_add(1));
})
.unwrap();
}
}
impl<'a, T: Time> SyscallDriver for ReadOnlyStateDriver<'a, T> {
/// Specify memory regions to be used.
///
/// ### `allow_num`
///
/// - `0`: Allow a buffer for the kernel to stored syscall values.
/// This should only be read by the app and written by the capsule.
fn allow_userspace_readable(
&self,
processid: ProcessId,
which: usize,
mut slice: UserspaceReadableProcessBuffer,
) -> Result<UserspaceReadableProcessBuffer, (UserspaceReadableProcessBuffer, ErrorCode)> {
if which == 0 {
let res = self.apps.enter(processid, |data, _| {
core::mem::swap(&mut data.mem_region, &mut slice);
});
match res {
Ok(()) => Ok(slice),
Err(e) => Err((slice, e.into())),
}
} else {
Err((slice, ErrorCode::NOSUPPORT))
}
}
/// Commands for ReadOnlyStateDriver.
///
/// ### `command_num`
///
/// - `0`: Driver existence check.
/// - `1`: Get version.
fn command(
&self,
command_number: usize,
_target_id: usize,
_: usize,
_processid: ProcessId,
) -> CommandReturn {
match command_number {
// Check existence
0 => CommandReturn::success(),
// Get version
1 => CommandReturn::success_u32(VERSION),
// default
_ => CommandReturn::failure(ErrorCode::NOSUPPORT),
}
}
fn allocate_grant(&self, processid: ProcessId) -> Result<(), process::Error> {
self.apps.enter(processid, |_, _| {})
}
}
#[derive(Default)]
pub struct App {
mem_region: UserspaceReadableProcessBuffer,
count: Cell<u32>,
}