-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmain.rs
252 lines (214 loc) · 9.17 KB
/
main.rs
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#![no_main]
#![no_std]
#[rtic::app(device = nrf52840_hal::pac, dispatchers = [SWI0_EGU0])]
mod app {
use core::sync::atomic::Ordering;
use cortex_m::{singleton, register::{psp, control}};
use defmt::unwrap;
use groundhog_nrf52::GlobalRollingTimer;
use nrf52840_hal::{
clocks::{ExternalOscillator, Internal, LfOscStopped},
pac::TIMER0,
usbd::{UsbPeripheral, Usbd},
Clocks,
};
use kernel::{
alloc::HEAP,
monotonic::{MonoTimer},
drivers::usb_serial::{UsbUartParts, setup_usb_uart, UsbUartIsr, enable_usb_interrupts},
syscall::{syscall_clear, try_syscall, try_recv_syscall, SysCallRequest, SysCallSuccess},
};
use usb_device::{
class_prelude::UsbBusAllocator,
device::{UsbDeviceBuilder, UsbVidPid},
};
use usbd_serial::{SerialPort, USB_CLASS_CDC};
use groundhog::RollingTimer;
#[monotonic(binds = TIMER0, default = true)]
type Monotonic = MonoTimer<TIMER0>;
#[shared]
struct Shared {}
#[local]
struct Local {
usb_isr: UsbUartIsr,
machine: kernel::traits::Machine,
}
#[init]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let device = cx.device;
// Setup clocks early in the process. We need this for USB later
let clocks = Clocks::new(device.CLOCK);
let clocks = clocks.enable_ext_hfosc();
let clocks =
unwrap!(singleton!(: Clocks<ExternalOscillator, Internal, LfOscStopped> = clocks));
// Configure the monotonic timer, currently using TIMER0, a 32-bit, 1MHz timer
let mono = Monotonic::new(device.TIMER0);
// I am annoying, and prefer my own libraries.
GlobalRollingTimer::init(device.TIMER1);
// Setup the heap
HEAP.init().ok();
// Reset the syscall contents
syscall_clear();
// Before we give away the USB peripheral, enable the relevant interrupts
enable_usb_interrupts(&device.USBD);
let (usb_dev, usb_serial) = {
let usb_bus = Usbd::new(UsbPeripheral::new(device.USBD, clocks));
let usb_bus = defmt::unwrap!(singleton!(:UsbBusAllocator<Usbd<UsbPeripheral>> = usb_bus));
let usb_serial = SerialPort::new(usb_bus);
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("OVAR Labs")
.product("Anachro Pellegrino")
// TODO: Use some kind of unique ID. This will probably require another singleton,
// as the storage must be static. Probably heapless::String -> singleton!()
.serial_number("ajm001")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) // (makes control transfers 8x faster)
.build();
(usb_dev, usb_serial)
};
let mut hg = defmt::unwrap!(HEAP.try_lock());
let UsbUartParts { isr, sys } = defmt::unwrap!(setup_usb_uart(usb_dev, usb_serial));
let box_uart = defmt::unwrap!(hg.alloc_box(sys));
let leak_uart = box_uart.leak();
let to_uart: &'static mut dyn kernel::traits::Serial = leak_uart;
let machine = kernel::traits::Machine {
serial: to_uart,
};
(
Shared {},
Local {
usb_isr: isr,
machine,
},
init::Monotonics(mono),
)
}
#[task(binds = SVCall, local = [machine], priority = 1)]
fn svc(cx: svc::Context) {
let machine = cx.local.machine;
let mut buf = [0u8; 64];
buf.copy_from_slice(&[0xACu8; 64]);
let bufaddr = buf.as_ptr() as u32;
defmt::println!("SVC: New variable lives at: 0x{=u32:08X}", bufaddr);
defmt::println!("SVC: Contents: {=[u8]}", buf);
if let Ok(()) = try_recv_syscall(|req| {
machine.handle_syscall(req)
}) {
// defmt::println!("Handled syscall!");
}
}
#[task(binds = USBD, local = [usb_isr], priority = 2)]
fn usb_tick(cx: usb_tick::Context) {
cx.local.usb_isr.poll();
}
// TODO: I am currently polling the syscall interfaces in the idle function,
// since I don't have syscalls yet. In the future, the `machine` will be given
// to the SWI handler, and idle will basically just launch a program. I think.
// Maybe idle will use SWIs too.
#[idle]
fn idle(_cx: idle::Context) -> ! {
defmt::println!("Hello, world!");
let timer = GlobalRollingTimer::default();
let start = timer.get_ticks();
// Wait, to allow RTT to attach
while timer.millis_since(start) < 100 { }
defmt::println!("⚠️ ENTERING USERSPACE ⚠️");
// Begin VERY CURSED userspace setup code
//
// TODO/UNSAFETY:
// This is likely all kinds of UB, and should be replaced with
// a divergent assembly function that:
//
// * Takes the new "userspace entry point" as an argument
// * Takes the new PSP start address as an argument
// * Sets the PSP
// * Enables npriv and psp in the control register
// * Calls the userspace entry point
core::sync::atomic::compiler_fence(Ordering::SeqCst);
// Try to set usermode! TODO! Should probably critical section!
// but also don't use any stack variables!
unsafe {
extern "C" {
static _app_stack_start: u32;
}
let stack_start = &_app_stack_start as *const u32 as u32;
defmt::println!("Setting PSP to: 0x{=u32:08X}", stack_start);
psp::write(stack_start);
// Note: This is where the really cursed stuff happens. We simultaneously:
// * Switch the stack pointer to use the newly-minted PSP instead of the
// default/exception mode MSP
// * Disables privilege mode for thread mode (e.g. `idle`)
//
// This makes the compiler sad. See note above for how to fix this the
// "right" way (spoiler: use ASM)
let mut cur_ctl = control::read();
cur_ctl.set_npriv(control::Npriv::Unprivileged);
cur_ctl.set_spsel(control::Spsel::Psp);
control::write(cur_ctl);
}
core::sync::atomic::compiler_fence(Ordering::SeqCst);
// So, moving the stack pointer is all kinds of cursed, and I should probably
// be using inline asm or something to do the above. Instead, immediately
// jump into another inline(never) function, and hope this wards off all of the
// UB goblins for now until I feel like learning ASM.
crate::userspace::entry();
// End VERY CURSED userspace setup code
}
}
mod userspace {
use groundhog::RollingTimer;
use groundhog_nrf52::GlobalRollingTimer;
use kernel::{self as _, syscall::{SysCallRequest, try_syscall, SysCallSuccess}, alloc::HEAP};
#[inline(never)]
pub fn entry() -> ! {
let timer = GlobalRollingTimer::default();
let mut last_mem = timer.get_ticks();
// First, open Port 1 (we will write to it)
let req = SysCallRequest::SerialOpenPort { port: 1 };
defmt::unwrap!(try_syscall(req));
let mut buf = [0u8; 128];
loop {
if timer.millis_since(last_mem) >= 1000 {
if let Some(hg) = HEAP.try_lock() {
last_mem = timer.get_ticks();
let used = hg.used_space();
let free = hg.free_space();
defmt::println!("used: {=usize}, free: {=usize}", used, free);
defmt::println!("Syscalling!");
}
let req = SysCallRequest::SerialReceive {
port: 0,
dest_buf: buf.as_mut().into(),
};
match try_syscall(req) {
Ok(succ) => {
if let SysCallSuccess::DataReceived { dest_buf } = succ {
let dest = unsafe { dest_buf.to_slice_mut() };
if dest.len() > 0 {
defmt::println!("Sending port 1!");
let req2 = SysCallRequest::SerialSend {
port: 1,
src_buf: (&*dest).into(),
};
match try_syscall(req2) {
Ok(SysCallSuccess::DataSent { remainder }) => {
defmt::assert!(remainder.is_none());
},
Ok(_) => defmt::panic!(),
Err(_) => {
defmt::println!("Port0 -> Port1 send failed!")
}
}
}
} else {
defmt::panic!("What?");
}
}
Err(()) => {
defmt::panic!("syscall failed!");
}
}
}
}
}
}