Skip to content

Commit 29d713b

Browse files
authored
added initial embedded track (#20)
* added initial tasks * added branch change tutorial * changed banner to welcome message * clarified grant section
1 parent 768864b commit 29d713b

File tree

2 files changed

+155
-1
lines changed

2 files changed

+155
-1
lines changed

docs/embedded/assets/grant.png

16.2 KB
Loading

docs/embedded/index.md

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,157 @@
1-
# Intorduction
1+
# Introduction
22

3+
## Prerequisites
34

5+
If you did not attend the **Tock Workshop**, please follow the [Setup Tutorial](../tock_workshop/index.md).
6+
7+
## Getting Started
8+
9+
For this track we will be using the **Nucleo-F429ZI** boards. You will need to **change the branch** you are working on, but first make sure you commit your changes.
10+
11+
```shell
12+
git add .
13+
git commit -m "tock workshop progress"
14+
```
15+
16+
Then, to fetch the branch and work on it, run:
17+
18+
```shell
19+
git fetch
20+
git checkout track/embedded
21+
```
22+
23+
The board's main can be found in the `boards/nucleo_f429zi` subfolder. Try to flash the kernel to the board, using the board's `Makefile`. After you are done flashing, connect to the board using `tockloader listen`
24+
25+
```shell
26+
[INFO ] No device name specified. Using default name "tock".
27+
[INFO ] No serial port with device name "tock" found.
28+
[INFO ] Found 2 serial ports.
29+
Multiple serial port options found. Which would you like to use?
30+
[0] /dev/cu.debug-console - n/a
31+
[1] /dev/cu.usbmodem1303 - STM32 STLink
32+
33+
Which option? [0] 1
34+
[INFO ] Using "/dev/cu.usbmodem1303 - STM32 STLink".
35+
[INFO ] Listening for serial output.
36+
37+
tock$
38+
```
39+
40+
## Customize your kernel
41+
42+
After connecting to Tock's terminal, you can run `help` to see the supported commands. One of them is `reset` and by running it, you can see the default *"welcome"* message.
43+
44+
```shell
45+
tock$ reset
46+
Initialization complete. Entering main loop
47+
tock$
48+
```
49+
50+
Personalize your kernel, by changing the hostname and the welcome message.
51+
52+
## Print Counter Capsule
53+
54+
For this task, you will need to build a capsule that prints a custom message each time it receives a print command from an application, along with a message counter representing the number of commands received. Remember that you will need to implement the `SyscallDriver` trait.
55+
56+
### The simple way
57+
58+
Simplest method to do this is to add a `counter` field in the capsule's structure. One issue you will most likely encounter is that the `command` method required by the `SyscallDriver` trait has a immutable reference to `&self`, so you may need to wrap the counter in a wrapper that allows for inner mutability, such as `Cell`.
59+
60+
### The Tock way
61+
62+
One issue with the previous approach is that the counter would be shared between the applications. This could be an issue for mutually distrustful application. Fortunately, Tock has a mechanism in place for such situations, called `Grant`s, which are per-process memory regions allocated by the kernel in a process memory region for a capsule to store that process’s state.
63+
64+
![Grant](assets/grant.png)
65+
66+
To access this region, you can simply add a new `grant` field in the capsule structure.
67+
68+
```rust title="capsules/extra/src/print_counter.rs"
69+
use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
70+
71+
// TODO: Define `App` structure. Make sure to satisfy trait constraints.
72+
struct App;
73+
74+
struct PrintCounter {
75+
grant: Grant<
76+
App,
77+
UpcallCount<0>, // Number of upcalls supported by the capsule
78+
AllowRoCount<0>, // Number of Read-Only buffers supported
79+
AllowRwCount<0>, // Number of Read-Write buffers supported
80+
>,
81+
}
82+
```
83+
84+
As before, we will need to define a component for this capsule, to initialize it.
85+
86+
```rust title="boards/components/src/print_counter.rs"
87+
#[macro_export]
88+
macro_rules! print_counter_component_static {
89+
($(,)?) => {{
90+
kernel::static_buf!(capsules_extra::print_counter::PrintCounter)
91+
};};
92+
}
93+
94+
pub struct PrintCounterComponent;
95+
96+
impl Component for PrintCounterComponent {
97+
type StaticInput = &'static mut MaybeUninit<capsules_extra::print_counter::PrintCounter>;
98+
99+
type Output = &'static capsules_extra::print_counter::PrintCounter;
100+
101+
fn finalize(self, static_memory: Self::StaticInput) -> Self::Output {
102+
todo!()
103+
}
104+
}
105+
```
106+
107+
Grants are a sensitive component of the operating system, so the creation and management operations are considered unsafe, and require
108+
privileges to perform. Tock restricts these privileged operations through the use of capabilities, which are tokens implementing `unsafe` traits. Because capsules are forbidden from using unsafe code, these tokens cannot be forged.
109+
110+
Creating a grant is requires a reference to the board's kernel, and a driver number, so we will need to add these parts in the components.
111+
112+
```rust title="boards/components/src/print_counter.rs"
113+
pub struct PrintCounterComponent {
114+
driver_num: usize,
115+
board_kernel: &'static kernel::Kernel,
116+
}
117+
118+
impl PrintCounterComponent {
119+
pub fn new(driver_num: usize, board_kernel: &'static kernel::Kernel) -> Self {
120+
Self {
121+
driver_num,
122+
board_kernel,
123+
}
124+
}
125+
}
126+
```
127+
128+
The capability needed for grant creating is called `MemoryAllocationCapability`, and it can be found in the `kernel::capabilities` module. The `kernel` also exposes the `crate_capability!` macro for ease of use.
129+
130+
```rust title="boards/components/src/print_counter.rs"
131+
impl Component for PrintCounterComponent {
132+
// ...
133+
134+
fn finalize(self, static_memory: Self::StaticInput) -> Self::Output {
135+
let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
136+
let grant = self.board_kernel.create_grant(self.driver_num, &grant_cap);
137+
138+
static_memory.write(capsules_extra::print_counter::PrintCounter::new(grant))
139+
}
140+
}
141+
```
142+
143+
:::note `new` constructor
144+
You will also need to implement the `new` constructor for the `PrintCounter` capsule.
145+
:::
146+
147+
Next, you must implement the `SyscallDriver` trait, where the command logic will be. For the `allocate_grant` method implementation, it is enough to use the `enter` method of the Grant which takes a closure with two parameters.
148+
149+
```rust
150+
fn allocate_grant(&self, process_id: kernel::ProcessId) -> Result<(), kernel::process::Error> {
151+
self.grant.enter(process_id, |_, _| {})
152+
}
153+
```
154+
155+
For the command logic, you must also use the `enter` API. The first parameter of the closure will be a mutable reference to a `GrantData` wrapper over the previously defined `App`. The wrapper is transparent, meaning it permits accessing fields of the generic type.
156+
157+
The next step is configuring the capsule in the board's main file. Remember you need to add the capsule in the `NucleoF429ZI` structure, the `SyscallDriverLookup` and initialize the printer counter capsule.

0 commit comments

Comments
 (0)