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>,
}