blob: 049d1020a676cf8c7ab661d6417051935287afe7 [file] [log] [blame]
use crate::callback::CallbackSubscription;
use crate::callback::SubscribableCallback;
use crate::shared_memory::SharedMemory;
#[cfg(target_arch = "arm")]
pub fn yieldk() {
// Note: A process stops yielding when there is a callback ready to run,
// which the kernel executes by modifying the stack frame pushed by the
// hardware. The kernel copies the PC value from the stack frame to the LR
// field, and sets the PC value to callback to run. When this frame is
// unstacked during the interrupt return, the effectively clobbers the LR
// register.
//
// At this point, the callback function is now executing, which may itself
// clobber any of the other caller-saved registers. Thus we mark this
// inline assembly as conservatively clobbering all caller-saved registers,
// forcing yield to save any live registers.
//
// Upon direct observation of this function, the LR is the only register
// that is live across the SVC invocation, however, if the yield call is
// inlined, it is possible that the LR won't be live at all (commonly seen
// for the `loop { yieldk(); }` idiom) or that other registers are live,
// thus it is important to let the compiler do the work here.
//
// According to the AAPCS: A subroutine must preserve the contents of the
// registers r4-r8, r10, r11 and SP (and r9 in PCS variants that designate
// r9 as v6) As our compilation flags mark r9 as the PIC base register, it
// does not need to be saved. Thus we must clobber r0-3, r12, and LR
unsafe {
asm!(
"svc 0"
:
:
: "memory", "r0", "r1", "r2", "r3", "r12", "lr"
: "volatile");
}
}
#[cfg(target_arch = "riscv32")]
pub fn yieldk() {
/* TODO: Stop yielding */
unsafe {
asm! (
"li a0, 0
ecall"
:
:
: "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "ra"
: "volatile");
}
}
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
while !cond() {
yieldk();
}
}
pub fn subscribe<CB: SubscribableCallback>(
driver_number: usize,
subscribe_number: usize,
callback: &mut CB,
) -> Result<CallbackSubscription, isize> {
let return_code = unsafe {
subscribe_ptr(
driver_number,
subscribe_number,
c_callback::<CB> as *const _,
callback as *mut CB as usize,
)
};
if return_code == 0 {
Ok(CallbackSubscription::new(driver_number, subscribe_number))
} else {
Err(return_code)
}
}
extern "C" fn c_callback<CB: SubscribableCallback>(
arg0: usize,
arg1: usize,
arg2: usize,
userdata: usize,
) {
let callback = unsafe { &mut *(userdata as *mut CB) };
callback.call_rust(arg0, arg1, arg2);
}
#[cfg(target_arch = "arm")]
pub unsafe fn subscribe_ptr(
major: usize,
minor: usize,
cb: *const unsafe extern "C" fn(usize, usize, usize, usize),
ud: usize,
) -> isize {
let res;
asm!("svc 1" : "={r0}"(res)
: "{r0}"(major) "{r1}"(minor) "{r2}"(cb) "{r3}"(ud)
: "memory"
: "volatile");
res
}
#[cfg(target_arch = "riscv32")]
pub unsafe fn subscribe_ptr(
major: usize,
minor: usize,
cb: *const unsafe extern "C" fn(usize, usize, usize, usize),
ud: usize,
) -> isize {
let res;
asm!("li a0, 1
ecall"
: "=r" (res)
: "r" (major), "r" (minor), "r" (cb), "r" (ud)
: "memory"
: "volatile" );
res
}
#[cfg(target_arch = "arm")]
pub unsafe fn command(major: usize, minor: usize, arg1: usize, arg2: usize) -> isize {
let res;
asm!("svc 2" : "={r0}"(res)
: "{r0}"(major) "{r1}"(minor) "{r2}"(arg1) "{r3}"(arg2)
: "memory"
: "volatile");
res
}
#[cfg(target_arch = "riscv32")]
pub unsafe fn command(major: usize, minor: usize, arg1: usize, arg2: usize) -> isize {
let res;
asm!("li a0, 2
ecall"
: "=r" (res)
: "r" (major), "r" (minor), "r" (arg1), "r" (arg2)
: "memory"
: "volatile");
res
}
pub fn allow(
driver_number: usize,
allow_number: usize,
buffer_to_share: &mut [u8],
) -> Result<SharedMemory, isize> {
let len = buffer_to_share.len();
let return_code = unsafe {
allow_ptr(
driver_number,
allow_number,
buffer_to_share.as_mut_ptr(),
len,
)
};
if return_code == 0 {
Ok(SharedMemory::new(
driver_number,
allow_number,
buffer_to_share,
))
} else {
Err(return_code)
}
}
#[cfg(target_arch = "arm")]
pub unsafe fn allow_ptr(major: usize, minor: usize, slice: *mut u8, len: usize) -> isize {
let res;
asm!("svc 3" : "={r0}"(res)
: "{r0}"(major) "{r1}"(minor) "{r2}"(slice) "{r3}"(len)
: "memory"
: "volatile");
res
}
#[cfg(target_arch = "riscv32")]
pub unsafe fn allow_ptr(major: usize, minor: usize, slice: *mut u8, len: usize) -> isize {
let res;
asm!("li a0, 3
ecall"
: "=r" (res)
: "r" (major), "r" (minor), "r" (slice), "r" (len)
: "memory"
: "volatile");
res
}
#[cfg(target_arch = "arm")]
pub unsafe fn memop(major: u32, arg1: usize) -> isize {
let res;
asm!("svc 4" : "={r0}"(res)
: "{r0}"(major) "{r1}"(arg1)
: "memory"
: "volatile");
res
}
#[cfg(target_arch = "riscv32")]
pub unsafe fn memop(major: u32, arg1: usize) -> isize {
let res;
asm!("li a0, 4
ecall"
: "=r" (res)
: "r" (major), "r" (arg1)
: "memory"
: "volatile");
res
}