capsules_core/test/double_grant_entry.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//! Test that tries to enter a grant twice.
6//!
7//! This must fail or Tock allows multiple mutable references to the same memory
8//! which is undefined behavior.
9//!
10//! To use, setup this capsule and connect the syscall `Driver` implementation
11//! to userspace. Then call the commands to test various double grant entries.
12//!
13//! # Usage
14//!
15//! Here is my example usage for hail:
16//!
17//! ```diff
18//! diff --git a/boards/hail/src/main.rs b/boards/hail/src/main.rs
19//! index 110d45fa7..e8f4728c2 100644
20//! --- a/boards/hail/src/main.rs
21//! +++ b/boards/hail/src/main.rs
22//! @@ -73,6 +73,7 @@ struct Hail {
23//! ipc: kernel::ipc::IPC<NUM_PROCS>,
24//! crc: &'static capsules::crc::Crc<'static, sam4l::crccu::Crccu<'static>>,
25//! dac: &'static capsules::dac::Dac<'static>,
26//! + dge: &'static capsules::test::double_grant_entry::TestGrantDoubleEntry,
27//! }
28//!
29//! /// Mapping of integer syscalls to objects that implement syscalls.
30//! @@ -102,6 +103,8 @@ impl Platform for Hail {
31//!
32//! capsules::dac::DRIVER_NUM => f(Some(self.dac)),
33//!
34//! + capsules::test::double_grant_entry::DRIVER_NUM => f(Some(self.dge)),
35//! +
36//! kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
37//! _ => f(None),
38//! }
39//! @@ -396,6 +399,14 @@ pub unsafe fn reset_handler() {
40//! capsules::dac::Dac::new(&peripherals.dac)
41//! );
42//!
43//! + // Test double grant entry
44//! + let dge = static_init!(
45//! + capsules::test::double_grant_entry::TestGrantDoubleEntry,
46//! + capsules::test::double_grant_entry::TestGrantDoubleEntry::new(
47//! + board_kernel.create_grant(&memory_allocation_capability)
48//! + )
49//! + );
50//! +
51//! // // DEBUG Restart All Apps
52//! // //
53//! // // Uncomment to enable a button press to restart all apps.
54//! @@ -440,6 +451,7 @@ pub unsafe fn reset_handler() {
55//! ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
56//! crc,
57//! dac,
58//! + dge,
59//! };
60//!
61//! // Setup the UART bus for nRF51 serialization..
62//! ```
63
64use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
65use kernel::syscall::{CommandReturn, SyscallDriver};
66use kernel::{ErrorCode, ProcessId};
67
68/// Syscall driver number.
69pub const DRIVER_NUM: usize = 0xF001;
70
71/// Need a grant for the process.
72#[derive(Default)]
73pub struct App {
74 pending: bool,
75}
76
77pub struct TestGrantDoubleEntry {
78 grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
79}
80
81impl TestGrantDoubleEntry {
82 pub fn new(
83 grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
84 ) -> TestGrantDoubleEntry {
85 TestGrantDoubleEntry { grant }
86 }
87}
88
89impl SyscallDriver for TestGrantDoubleEntry {
90 fn command(
91 &self,
92 command_num: usize,
93 _: usize,
94 _: usize,
95 processid: ProcessId,
96 ) -> CommandReturn {
97 match command_num {
98 0 => CommandReturn::success(),
99
100 1 => {
101 // Try Grant.enter() then Grant.iter().enter()
102
103 // Check if we saw a grant with pending as true. If so, we
104 // entered the same grant twice.
105 let mut found_pending = false;
106
107 // Enter the grant for the app.
108 let err = self
109 .grant
110 .enter(processid, |appgrant, _| {
111 // We can now change the state of the app's grant
112 // region.
113 appgrant.pending = true;
114
115 // Now, try to iterate all grant regions.
116 for grant in self.grant.iter() {
117 // And, try to enter each grant! This should fail.
118 grant.enter(|appgrant2, _| {
119 if appgrant2.pending {
120 found_pending = true;
121 }
122 });
123 }
124 CommandReturn::success()
125 })
126 .unwrap_or_else(|err| err.into());
127
128 // If found pending is true, things are broken.
129 if found_pending {
130 kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
131 }
132
133 err
134 }
135
136 2 => {
137 // Try Grant.iter() then Grant.enter() then Grant.iter().enter()
138
139 // Check if we saw a grant with pending as true. If so, we
140 // entered the same grant twice.
141 let mut found_pending = false;
142
143 // Make sure the grant is allocated.
144 let _ = self.grant.enter(processid, |appgrant, _| {
145 appgrant.pending = false;
146 });
147
148 for app in self.grant.iter() {
149 let _ = self.grant.enter(processid, |appgrant, _| {
150 // Mark the field.
151 appgrant.pending = true;
152
153 // Check if we can access this grant twice.
154 app.enter(|appgrant2, _| {
155 if appgrant2.pending {
156 found_pending = true;
157 }
158 });
159 });
160 }
161
162 // If found pending is true, things are broken.
163 if found_pending {
164 kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
165 }
166
167 // Since we expect a panic these return values don't matter.
168 CommandReturn::success()
169 }
170
171 3 => {
172 // Try Grant.enter() then Grant.enter()
173
174 // Check if we saw a grant with pending as true. If so, we
175 // entered the same grant twice.
176 let mut found_pending = false;
177
178 let _ = self.grant.enter(processid, |appgrant, _| {
179 appgrant.pending = true;
180
181 let _ = self.grant.enter(processid, |appgrant2, _| {
182 if appgrant2.pending {
183 found_pending = true;
184 }
185 });
186 });
187
188 // If found pending is true, things are broken.
189 if found_pending {
190 kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
191 }
192
193 // Since we expect a panic these return values don't matter.
194 CommandReturn::success()
195 }
196
197 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
198 }
199 }
200
201 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
202 self.grant.enter(processid, |_, _| {})
203 }
204}