Skip to content

implemented yield_wait_for #575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apis/interface/buttons/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ impl<S: Syscalls> Buttons<S> {
pub fn unregister_listener() {
S::unsubscribe(DRIVER_NUM, 0)
}

// Wrapper for yield_wait_for
pub fn wait_for_button(driver_number: u32, subscribe_number: u32) {
S::yield_wait_for(driver_number, subscribe_number);
}
}

/// A wrapper around a closure to be registered and called when
Expand Down
3 changes: 3 additions & 0 deletions apis/net/ieee802154/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ unsafe impl RawSyscalls for FakeSyscalls {
libtock_unittest::fake::Syscalls::yield2([r0, r1])
}

unsafe fn yield3([r0, r1, r2]: [Register; 3]) {
libtock_unittest::fake::Syscalls::yield3([r0, r1, r2])
}
unsafe fn syscall1<const CLASS: usize>([r0]: [Register; 1]) -> [Register; 2] {
libtock_unittest::fake::Syscalls::syscall1::<CLASS>([r0])
}
Expand Down
44 changes: 44 additions & 0 deletions examples/buttons_wait_for.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! An extremely simple libtock-rs example. Register button events.
#![no_main]
#![no_std]

use core::fmt::Write;
use libtock::buttons::{ButtonListener, Buttons};
use libtock::console::Console;
use libtock::leds::Leds;
use libtock::runtime::{set_main, stack_size};
use libtock_platform::share;
// use libtock_runtime::TockSyscalls;

set_main! {main}
stack_size! {0x1000}

fn main() {
writeln!(Console::writer(), "main!").unwrap();
let listener = ButtonListener(|button, _state| {
let _ = Leds::toggle(button);
// writeln!(Console::writer(), "button {:?}: {:?}", button, state).unwrap();
});
if let Ok(buttons_count) = Buttons::count() {
writeln!(Console::writer(), "button count: {}", buttons_count).unwrap();

share::scope(|subscribe| {
// Subscribe to the button callback.
Buttons::register_listener(&listener, subscribe).unwrap();
// Enable interrupts for each button press.
for i in 0..buttons_count {
Buttons::enable_interrupts(i).unwrap();
}

// Wait for buttons to be pressed.
loop {
for i in 0..buttons_count {
let driver_number: u32 = 0x3;
let subscribe_number: u32 = i as u32;
Buttons::wait_for_button(driver_number, subscribe_number);
writeln!(Console::writer(), "button pressed (yield_wait_for)").unwrap();
}
}
});
}
}
1 change: 1 addition & 0 deletions platform/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ pub mod syscall_class {
pub mod yield_id {
pub const NO_WAIT: u32 = 0;
pub const WAIT: u32 = 1;
pub const WAIT_FOR: u32 = 2;
}
3 changes: 3 additions & 0 deletions platform/src/raw_syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ pub unsafe trait RawSyscalls: Sized {
/// yield2 may only be used for yield operations that do not return a value.
/// It has the same safety invariants as the underlying system call.
unsafe fn yield2(_: [Register; 2]);

// yield3 can only be used to call `yield-wait-for`
unsafe fn yield3(_: [Register; 3]);

// syscall1 is only used to invoke Memop operations. Because there are no
// Memop commands that set r2 or r3, raw_syscall1 only needs to return r0
Expand Down
2 changes: 2 additions & 0 deletions platform/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub trait Syscalls: RawSyscalls + Sized {
/// callback, then returns.
fn yield_wait();

fn yield_wait_for(driver_number: u32, subscribe_number: u32);

// -------------------------------------------------------------------------
// Subscribe
// -------------------------------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions platform/src/syscalls_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ impl<S: RawSyscalls> Syscalls for S {
}
}

fn yield_wait_for(driver_number: u32, subscribe_number: u32) {
// Safety: yield-wait does not return a value, which satisfies yield1's
// requirement. The yield-wait system call cannot trigger undefined
// behavior on its own in any other way.
unsafe {
Self::yield3([
yield_id::WAIT_FOR.into(),
driver_number.into(),
subscribe_number.into(),
]);
}
}

// -------------------------------------------------------------------------
// Subscribe
// -------------------------------------------------------------------------
Expand Down
22 changes: 22 additions & 0 deletions runtime/src/syscalls_impl_arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ unsafe impl RawSyscalls for crate::TockSyscalls {
}
}

unsafe fn yield3([Register(r0), Register(r1), Register(r2)]: [Register; 3]) {
// Safety: This matches the invariants required by the documentation on
// RawSyscalls::yield2
// the use of `clobber_abi` allows us this to run on both Thumb-1 and Thumb-2
unsafe {
asm!("svc 0",
inlateout("r0") r0 => _, // a1
inlateout("r1") r1 => _, // a2
inlateout("r2") r2 => _,
// r4-r8 are callee-saved.
// r9 is platform-specific. We don't use it in libtock_runtime,
// so it is either unused or used as a callee-saved register.
// r10 and r11 are callee-saved.

// r13 is the stack pointer and must be restored by the callee.
// r15 is the program counter.

clobber_abi("C"), // a3, a4, ip (r12), lr (r14)
);
}
}

unsafe fn syscall1<const SYSCALL_CLASS_NUMBER: usize>(
[Register(mut r0)]: [Register; 1],
) -> [Register; 2] {
Expand Down
31 changes: 31 additions & 0 deletions runtime/src/syscalls_impl_riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,37 @@ unsafe impl RawSyscalls for crate::TockSyscalls {
}
}

#[cfg(not(any(target_feature = "d", target_feature = "f")))]
unsafe fn yield3([Register(r0), Register(r1), Register(_r2)]: [Register; 3]) {
// Safety: This matches the invariants required by the documentation on
// RawSyscalls::yield2
unsafe {
asm!("ecall",
// x0 is the zero register.
lateout("x1") _, // Return address
// x2-x4 are stack, global, and thread pointers. sp is
// callee-saved.
lateout("x5") _, // t0
lateout("x6") _, // t1
lateout("x7") _, // t2
// x8 and x9 are s0 and s1 and are callee-saved.
inlateout("x10") r0 => _, // a0
inlateout("x11") r1 => _, // a1
lateout("x12") _, // a2
lateout("x13") _, // a3
inlateout("x14") 0 => _, // a4
lateout("x15") _, // a5
lateout("x16") _, // a6
lateout("x17") _, // a7
// x18-27 are s2-s11 and are callee-saved
lateout("x28") _, // t3
lateout("x29") _, // t4
lateout("x30") _, // t5
lateout("x31") _, // t6
);
}
}

unsafe fn syscall1<const CLASS: usize>([Register(mut r0)]: [Register; 1]) -> [Register; 2] {
let r1;
// Safety: This matches the invariants required by the documentation on
Expand Down
7 changes: 7 additions & 0 deletions unittest/src/expected_syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub enum ExpectedSyscall {
skip_upcall: bool,
},

YieldWaitFor {
driver: u32,
subscribe: u32,
},



// -------------------------------------------------------------------------
// Subscribe
// -------------------------------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions unittest/src/fake/syscalls/raw_syscalls_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ unsafe impl RawSyscalls for crate::fake::Syscalls {
match r0.try_into().expect("too-large Yield ID passed") {
yield_id::NO_WAIT => panic!("yield-no-wait called without an argument"),
yield_id::WAIT => super::yield_impl::yield_wait(),
yield_id::WAIT_FOR => panic!("yield-wait-for called without arguments"),
id => panic!("unknown yield ID {}", id),
}
}
Expand All @@ -21,6 +22,18 @@ unsafe impl RawSyscalls for crate::fake::Syscalls {
// we fail the test case regardless.
panic!("yield-wait called with an argument");
}
yield_id::WAIT_FOR => panic!("yield wait for called with just one argument"),
id => panic!("unknown yield ID {}", id),
}
}
unsafe fn yield3([r0, r1, r2]: [Register; 3]) {
crate::fake::syscalls::assert_valid((r0, r1, r2));
match r0.try_into().expect("too-large Yield ID passed") {
yield_id::NO_WAIT => panic!("yield-no-wait called with 2 arguments"),
yield_id::WAIT => panic!("yield-wait called with 2 arguments"),
yield_id::WAIT_FOR => unsafe {
super::yield_impl::yield_wait_for(r1.into(), r2.into());
},
id => panic!("unknown yield ID {}", id),
}
}
Expand Down
38 changes: 38 additions & 0 deletions unittest/src/fake/syscalls/yield_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,44 @@ pub(super) fn yield_wait() {
);
}

pub(super) unsafe fn yield_wait_for(
driver_number: libtock_platform::Register,
subscribe_number: libtock_platform::Register,
) {
let upcall_found = KERNEL_DATA.with(|refcell| {
let mut refmut = refcell.borrow_mut();
let kernel_data = refmut
.as_mut()
.expect("yield-wait-for called but no fake::Kernel exists");

kernel_data.syscall_log.push(SyscallLogEntry::YieldWaitFor);

match kernel_data.expected_syscalls.pop_front() {
None => false,
Some(ExpectedSyscall::YieldWaitFor { driver, subscribe })
if <libtock_platform::Register as From<u32>>::from(driver) == driver_number
&& <libtock_platform::Register as From<u32>>::from(subscribe)
== subscribe_number =>
{
true
}
Some(expected_syscall) => {
kernel_data.expected_syscalls.push_front(expected_syscall);
false
}
}
});

if upcall_found {
return;
}

assert!(
invoke_next_upcall(),
"yield-wait-for called with no queueued upcall"
);
}

// Pops the next upcall off the kernel data's upcall queue and invokes it, or
// does nothing if the upcall queue was entry. The return value indicates
// whether an upcall was run. Panics if no kernel data is present.
Expand Down
1 change: 1 addition & 0 deletions unittest/src/syscall_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum SyscallLogEntry {

YieldWait,

YieldWaitFor,
// -------------------------------------------------------------------------
// Subscribe
// -------------------------------------------------------------------------
Expand Down