capsules_extra/
read_only_state.rs

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