capsules_extra/
cycle_count.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//! Provides a cycle counter interface for userspace.
6//!
7//! Usage
8//! -----
9//!
10//! This capsule is intended for debug purposes. However, to ensure that use
11//! by multiple apps does not lead to innacurate results, basic virtualization
12//! is implemented: only the first app to start the cycle counter can start or
13//! stop or reset the counter. Other apps are restricted to reading the counter
14//! (which can be useful for debugging the time required by cross-process routines).
15
16/// Syscall driver number.
17use capsules_core::driver;
18pub const DRIVER_NUM: usize = driver::NUM::CycleCount as usize;
19
20use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
21use kernel::syscall::{CommandReturn, SyscallDriver};
22use kernel::utilities::cells::OptionalCell;
23use kernel::{hil, ErrorCode, ProcessId};
24
25#[derive(Default)]
26pub struct App;
27
28pub struct CycleCount<'a, P: hil::hw_debug::CycleCounter> {
29    counters: &'a P,
30    apps: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
31    controlling_app: OptionalCell<ProcessId>,
32}
33
34impl<'a, P: hil::hw_debug::CycleCounter> CycleCount<'a, P> {
35    pub fn new(
36        counters: &'a P,
37        grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
38    ) -> Self {
39        Self {
40            counters,
41            apps: grant,
42            controlling_app: OptionalCell::empty(),
43        }
44    }
45}
46
47impl<P: hil::hw_debug::CycleCounter> SyscallDriver for CycleCount<'_, P> {
48    /// Control the CycleCount system.
49    ///
50    /// ### `command_num`
51    ///
52    /// - `0`: Driver check.
53    /// - `1`: Start the cycle counter.
54    /// - `2`: Get current cycle count.
55    /// - `3`: Reset and stop the cycle counter.
56    /// - `4`: Stop the cycle counter.
57    fn command(
58        &self,
59        command_num: usize,
60        _data: usize,
61        _: usize,
62        processid: ProcessId,
63    ) -> CommandReturn {
64        let try_claim_driver = || {
65            let match_or_empty_or_nonexistant =
66                self.controlling_app.map_or(true, |controlling_app| {
67                    self.apps
68                        .enter(controlling_app, |_, _| controlling_app == processid)
69                        .unwrap_or(true)
70                });
71            if match_or_empty_or_nonexistant {
72                self.controlling_app.set(processid);
73                true
74            } else {
75                false
76            }
77        };
78        match command_num {
79            0 => CommandReturn::success(),
80
81            1 => {
82                if try_claim_driver() {
83                    self.counters.start();
84                    CommandReturn::success()
85                } else {
86                    CommandReturn::failure(ErrorCode::RESERVE)
87                }
88            }
89            2 => CommandReturn::success_u64(self.counters.count()),
90            3 => {
91                if try_claim_driver() {
92                    self.counters.reset();
93                    CommandReturn::success()
94                } else {
95                    CommandReturn::failure(ErrorCode::RESERVE)
96                }
97            }
98            4 => {
99                if try_claim_driver() {
100                    self.counters.stop();
101                    CommandReturn::success()
102                } else {
103                    CommandReturn::failure(ErrorCode::RESERVE)
104                }
105            }
106            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
107        }
108    }
109
110    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
111        self.apps.enter(processid, |_, _| {})
112    }
113}