Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn wait_for_button(driver_number: u32, subscribe_number: u32) {
pub fn wait_for_button(driver_number: u32, subscribe_number: u32) -> (u32, ButtonState) {

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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yield-wait-for does not trigger the callback, it returns the arguments for the callback when yield-for-returns.

// 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;
}
18 changes: 18 additions & 0 deletions platform/src/raw_syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ pub unsafe trait RawSyscalls: Sized {
/// 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`
// yield3 should:
// 1. Call syscall class 0
// 2. Pass in r0 and r1 as inlateout registers.
// 3. Mark all caller-saved registers as lateout clobbers.
// 4. NOT provide any of the following options:
// pure (yield has side effects)
// nomem (a callback can read + write globals)
// readonly (a callback can write globals)
// preserves_flags (a callback can change flags)
// noreturn (yield is expected to return)
// nostack (a callback needs the stack)
/// `yield3` should only be called by `libtock_platform`.
/// # Safety
/// yield3 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 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
// and r1.
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn yield_wait_for(driver_number: u32, subscribe_number: u32) {
fn yield_wait_for(driver_number: u32, subscribe_number: u32) -> (u32, u32, u32) {

yield-wait-for returns values instead of calling an upcall, all registers return. This is similar to yield-no-wait, but it only returns r1.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first u32 is always 3, and the rest are driver_number and subscribe_number?

// 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
5 changes: 5 additions & 0 deletions unittest/src/expected_syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ 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"
);
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this unittest, this function basically returns the same thing everytime, is this okay? What should happen if we don't have the expected 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
27 changes: 25 additions & 2 deletions unittest/src/fake/syscalls/yield_impl_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,16 @@ fn yield1() {
}
assert_eq!(kernel.take_syscall_log(), [SyscallLogEntry::YieldWait]);

// Call yield1 with a yield ID that is unknown but which fits in a u32.
// Call yield-no-wait through yield1, which is not valid.
let result = catch_unwind(|| unsafe { fake::Syscalls::yield1([2u32.into()]) });
assert!(result
.expect_err("failed to catch yield-no-wait without arg")
.downcast_ref::<&'static str>()
.expect("wrong panic payload type")
.contains("yield-wait-for called without arguments"));

// Call yield1 with a yield ID that is unknown but which fits in a u32.
let result = catch_unwind(|| unsafe { fake::Syscalls::yield1([3u32.into()]) });
assert!(result
.expect_err("failed to catch incorrect yield ID -- new ID added?")
.downcast_ref::<String>()
Expand Down Expand Up @@ -262,6 +270,14 @@ fn yield2() {
assert_eq!(kernel.take_syscall_log(), [SyscallLogEntry::YieldNoWait]);
assert_eq!(return_value, YieldNoWaitReturn::Upcall);

// // Call yield-no-wait through yield2, which should be rejected.
// let result = catch_unwind(|| unsafe { fake::Syscalls::yield2([0u32.into(), 0u32.into()]) });
// assert!(result
// .expect_err("failed to catch yield-wait with arg")
// .downcast_ref::<&'static str>()
// .expect("wrong panic payload type")
// .contains("yield-no-wait called with an argument"));

// Call yield-wait through yield2, which should be rejected.
let result = catch_unwind(|| unsafe { fake::Syscalls::yield2([1u32.into(), 0u32.into()]) });
assert!(result
Expand All @@ -270,8 +286,15 @@ fn yield2() {
.expect("wrong panic payload type")
.contains("yield-wait called with an argument"));

// Call yield2 with a yield ID that is unknown but which fits in a u32.
// Call yield-wait-for through yield2, which should be rejected.
let result = catch_unwind(|| unsafe { fake::Syscalls::yield2([2u32.into(), 0u32.into()]) });
assert!(result
.expect_err("failed to catch yield-wait-for with arg")
.downcast_ref::<&'static str>()
.expect("wrong panic payload type")
.contains("yield-wait-for called with just one argument"));
// Call yield2 with a yield ID that is unknown but which fits in a u32.
let result = catch_unwind(|| unsafe { fake::Syscalls::yield2([3u32.into(), 0u32.into()]) });
assert!(result
.expect_err("failed to catch incorrect yield ID -- new ID added?")
.downcast_ref::<String>()
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
Loading