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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Provides a cycle counter interface for userspace.
//!
//! Usage
//! -----
//!
//! This capsule is intended for debug purposes. However, to ensure that use
//! by multiple apps does not lead to innacurate results, basic virtualization
//! is implemented: only the first app to start the cycle counter can start or
//! stop or reset the counter. Other apps are restricted to reading the counter
//! (which can be useful for debugging the time required by cross-process routines).
/// Syscall driver number.
use capsules_core::driver;
pub const DRIVER_NUM: usize = driver::NUM::CycleCount as usize;
use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
use kernel::syscall::{CommandReturn, SyscallDriver};
use kernel::utilities::cells::OptionalCell;
use kernel::{hil, ErrorCode, ProcessId};
#[derive(Default)]
pub struct App;
pub struct CycleCount<'a, P: hil::hw_debug::CycleCounter> {
counters: &'a P,
apps: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
controlling_app: OptionalCell<ProcessId>,
}
impl<'a, P: hil::hw_debug::CycleCounter> CycleCount<'a, P> {
pub fn new(
counters: &'a P,
grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
) -> Self {
Self {
counters,
apps: grant,
controlling_app: OptionalCell::empty(),
}
}
}
impl<'a, P: hil::hw_debug::CycleCounter> SyscallDriver for CycleCount<'a, P> {
/// Control the CycleCount system.
///
/// ### `command_num`
///
/// - `0`: Driver check.
/// - `1`: Start the cycle counter.
/// - `2`: Get current cycle count.
/// - `3`: Reset and stop the cycle counter.
/// - `4`: Stop the cycle counter.
fn command(
&self,
command_num: usize,
_data: usize,
_: usize,
processid: ProcessId,
) -> CommandReturn {
let try_claim_driver = || {
let match_or_empty_or_nonexistant =
self.controlling_app.map_or(true, |controlling_app| {
self.apps
.enter(controlling_app, |_, _| controlling_app == processid)
.unwrap_or(true)
});
if match_or_empty_or_nonexistant {
self.controlling_app.set(processid);
true
} else {
false
}
};
match command_num {
0 => CommandReturn::success(),
1 => {
if try_claim_driver() {
self.counters.start();
CommandReturn::success()
} else {
CommandReturn::failure(ErrorCode::RESERVE)
}
}
2 => CommandReturn::success_u64(self.counters.count()),
3 => {
if try_claim_driver() {
self.counters.reset();
CommandReturn::success()
} else {
CommandReturn::failure(ErrorCode::RESERVE)
}
}
4 => {
if try_claim_driver() {
self.counters.stop();
CommandReturn::success()
} else {
CommandReturn::failure(ErrorCode::RESERVE)
}
}
_ => CommandReturn::failure(ErrorCode::NOSUPPORT),
}
}
fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
self.apps.enter(processid, |_, _| {})
}
}