Skip to content
Permalink
Browse files

syscall: documentation around process fn injection

Add an explicit note about the hazard of funciton injection, namely that
it will override any kernel return value (as the injected function will
run first, and its return value will overwrite any kernel return value
that has been set).

In practice, this means that process function injection is only safe to
call in response to syscalls without return values (i.e. only `yield`
in the current syscall interface).
  • Loading branch information...
ppannuto committed Jul 11, 2019
1 parent a6b10a0 commit 483faead8f5aa7e3c2142617f375a7ad6d8aa202
Showing with 26 additions and 14 deletions.
  1. +22 −14 arch/cortex-m/src/syscall.rs
  2. +4 −0 kernel/src/syscall.rs
@@ -108,28 +108,36 @@ impl kernel::syscall::UserspaceKernelBoundary for SysCall {
write_volatile(sp, return_value);
}

/// When the process calls `svc` to enter the kernel, the hardware
/// automatically pushes a stack frame that will be unstacked when the
/// kernel returns to the process. In the special case of process startup,
/// `initialize_new_process` sets up an empty stack frame and stored state
/// as if an `svc` had been called.
///
/// Here, we modify this stack frame such that the process resumes at the
/// beginning of the callback function that we want the process to run. We
/// place the originally intended return addess in the link register so
/// that when the function completes execution continues.
///
/// In effect, this converts `svc` into `bl callback`.
unsafe fn set_process_function(
&self,
stack_pointer: *const usize,
_remaining_stack_memory: usize,
state: &mut CortexMStoredState,
callback: kernel::procs::FunctionCall,
) -> Result<*mut usize, *mut usize> {
// Notes:
// - Instruction addresses require `|1` to indicated thumb code
// - Stack offset 4 is R12, which the syscall interface ignores
let stack_bottom = stack_pointer as *mut usize;

// Now either modify the existing stack frame or create one where we just
// made space. In either case the process is the same.
write_volatile(stack_bottom.offset(7), state.psr);
write_volatile(stack_bottom.offset(6), callback.pc | 1);

// Set the LR register to the saved PC so the callback returns to
// wherever wait was called. Set lowest bit to one because of THUMB
// instruction requirements.
write_volatile(stack_bottom.offset(5), state.yield_pc | 0x1);
write_volatile(stack_bottom, callback.argument0);
write_volatile(stack_bottom.offset(1), callback.argument1);
write_volatile(stack_bottom.offset(2), callback.argument2);
write_volatile(stack_bottom.offset(3), callback.argument3);
write_volatile(stack_bottom.offset(7), state.psr); //......... -> APSR
write_volatile(stack_bottom.offset(6), callback.pc | 1); //... -> PC
write_volatile(stack_bottom.offset(5), state.yield_pc | 1); // -> LR
write_volatile(stack_bottom.offset(3), callback.argument3); // -> R3
write_volatile(stack_bottom.offset(2), callback.argument2); // -> R2
write_volatile(stack_bottom.offset(1), callback.argument1); // -> R1
write_volatile(stack_bottom.offset(0), callback.argument0); // -> R0

Ok(stack_bottom)
}
@@ -101,6 +101,10 @@ pub trait UserspaceKernelBoundary {
/// `_start` when the process is started for the very first time; 2) tells
/// the process to execute a callback function after calling `yield()`.
///
/// **Note:** This method cannot be called in conjunction with
/// `set_syscall_return_value`, as the injected function will clobber the
/// return value.
///
/// ### Arguments
///
/// - `stack_pointer` is the address of the stack pointer for the current

0 comments on commit 483faea

Please sign in to comment.
You can’t perform that action at this time.