xous-core/kernel/src/asm.S
Sean Cross dafec2bb89 kernel: make contexts first-class
Due to an initial misunderstanding of how the RISC-V is architected,
contexts were going to be an emergent property of the kernel.

However, after further consideration, contexts must now be baked into
the kernel.  Switching between contexts can be handled by the scheduler,
but the kernel must keep track of processor registers.

Keep track of context IDs and ensure they are sane.  With this patch,
interrupts work, as does basic context switching.

Signed-off-by: Sean Cross <sean@xobs.io>
2020-03-27 12:30:29 +08:00

219 lines
6.3 KiB
ArmAsm

#if __riscv_xlen == 64
# define STORE sd
# define LOAD ld
# define LOG_REGBYTES 3
#else
# define STORE sw
# define LOAD lw
# define LOG_REGBYTES 2
#endif
#define REGBYTES (1 << LOG_REGBYTES)
/*
Entry point of all programs (_start).
It initializes DWARF call frame information, the stack pointer, the
frame pointer (needed for closures to work in start_rust) and the global
pointer. Then it calls _start_rust.
*/
.section .text.init, "ax"
.global _start
_start:
// Set trap handler, which will be called
// on interrupts and cpu faults
la t0, _start_trap
csrw stvec, t0
// Jump to a program to initialize global variables.
// Note that at this point, the .data section should
// have already been set up by the loader.
jal x1, init
// Call the main function. This function diverges, so
// shouldn't ever return.
j main
/*
Trap entry point (_start_trap)
Saves all process context into a structure that is
mapped to each thread at 0xff801000. Return happens
elsewhere.
*/
.section .trap, "ax"
.global _start_trap
_start_trap:
csrw sscratch, sp
li sp, 0xff801000
STORE x1, 0*REGBYTES(sp) // Store x1 in the scratch field
LOAD x1, 1*REGBYTES(sp) // Load current context number
slli x1, x1, 7 // Multiply current context number by 32
add sp, sp, x1 // Set $sp to 0xff801000 + (current_context * 32)
STORE x1, 0*REGBYTES(sp)
// Skip SP for now
STORE x3, 2*REGBYTES(sp)
STORE x4, 3*REGBYTES(sp)
STORE x5, 4*REGBYTES(sp)
STORE x6, 5*REGBYTES(sp)
STORE x7, 6*REGBYTES(sp)
STORE x8, 7*REGBYTES(sp)
STORE x9, 8*REGBYTES(sp)
STORE x10, 9*REGBYTES(sp)
STORE x11, 10*REGBYTES(sp)
STORE x12, 11*REGBYTES(sp)
STORE x13, 12*REGBYTES(sp)
STORE x14, 13*REGBYTES(sp)
STORE x15, 14*REGBYTES(sp)
STORE x16, 15*REGBYTES(sp)
STORE x17, 16*REGBYTES(sp)
STORE x18, 17*REGBYTES(sp)
STORE x19, 18*REGBYTES(sp)
STORE x20, 19*REGBYTES(sp)
STORE x21, 20*REGBYTES(sp)
STORE x22, 21*REGBYTES(sp)
STORE x23, 22*REGBYTES(sp)
STORE x24, 23*REGBYTES(sp)
STORE x25, 24*REGBYTES(sp)
STORE x26, 25*REGBYTES(sp)
STORE x27, 26*REGBYTES(sp)
STORE x28, 27*REGBYTES(sp)
STORE x29, 28*REGBYTES(sp)
STORE x30, 29*REGBYTES(sp)
STORE x31, 30*REGBYTES(sp)
// Save SEPC
csrr t0, sepc
STORE t0, 31*REGBYTES(sp)
// Save x1, which was used to calculate the offset. Prior to
// calculating, it was stashed at 0xff801000.
li t0, 0xff801000
LOAD t1, 0*REGBYTES(t0)
STORE t1, 0*REGBYTES(sp)
// Finally, save SP
csrr t0, sscratch
STORE t0, 1*REGBYTES(sp)
// Restore a default stack pointer
li sp, 0xfffefffc
// Note that registers $a0-$a7 still contain the arguments
j _start_trap_rust
/*
Resume a context (_xous_resume_context)
Restores all registers from a ProcessContext passed in $a0.
Note that the SEPC and SSTATUS should be set already.
*/
.global _xous_resume_context
_xous_resume_context:
move sp, a0
LOAD x1, 0*REGBYTES(sp)
// Skip SP for now
LOAD x3, 2*REGBYTES(sp)
LOAD x4, 3*REGBYTES(sp)
LOAD x5, 4*REGBYTES(sp)
LOAD x6, 5*REGBYTES(sp)
LOAD x7, 6*REGBYTES(sp)
LOAD x8, 7*REGBYTES(sp)
LOAD x9, 8*REGBYTES(sp)
LOAD x10, 9*REGBYTES(sp)
LOAD x11, 10*REGBYTES(sp)
LOAD x12, 11*REGBYTES(sp)
LOAD x13, 12*REGBYTES(sp)
LOAD x14, 13*REGBYTES(sp)
LOAD x15, 14*REGBYTES(sp)
LOAD x16, 15*REGBYTES(sp)
LOAD x17, 16*REGBYTES(sp)
LOAD x18, 17*REGBYTES(sp)
LOAD x19, 18*REGBYTES(sp)
LOAD x20, 19*REGBYTES(sp)
LOAD x21, 20*REGBYTES(sp)
LOAD x22, 21*REGBYTES(sp)
LOAD x23, 22*REGBYTES(sp)
LOAD x24, 23*REGBYTES(sp)
LOAD x25, 24*REGBYTES(sp)
LOAD x26, 25*REGBYTES(sp)
LOAD x27, 26*REGBYTES(sp)
LOAD x28, 27*REGBYTES(sp)
LOAD x29, 28*REGBYTES(sp)
LOAD x30, 29*REGBYTES(sp)
LOAD x31, 30*REGBYTES(sp)
// Restore SP
LOAD x2, 1*REGBYTES(sp)
sret
/*
Return from a syscall with arguments (_xous_syscall_return_result)
Xous passes arguments in $a0-$a7, however the RISC-V calling convention
states that only $a0 and $a1 are used. To work around this, we pass
a pointer to a 32-byte structure in $a0, and this function moves that
into the registers. It then restores the context from other registers.
*/
.global _xous_syscall_return_result
_xous_syscall_return_result:
move sp, a1
LOAD t0, 31*REGBYTES(sp)
csrw sepc, t0
LOAD x1, 0*REGBYTES(sp)
// Skip SP for now
LOAD x3, 2*REGBYTES(sp)
LOAD x4, 3*REGBYTES(sp)
LOAD x5, 4*REGBYTES(sp)
LOAD x6, 5*REGBYTES(sp)
LOAD x7, 6*REGBYTES(sp)
LOAD x8, 7*REGBYTES(sp)
LOAD x9, 8*REGBYTES(sp)
// Load argument registers from the value pointed
// to by $a0
LOAD a7, 7*REGBYTES(a0)
LOAD a6, 6*REGBYTES(a0)
LOAD a5, 5*REGBYTES(a0)
LOAD a4, 4*REGBYTES(a0)
LOAD a3, 3*REGBYTES(a0)
LOAD a2, 2*REGBYTES(a0)
LOAD a1, 1*REGBYTES(a0)
LOAD a0, 0*REGBYTES(a0)
LOAD x18, 17*REGBYTES(sp)
LOAD x19, 18*REGBYTES(sp)
LOAD x20, 19*REGBYTES(sp)
LOAD x21, 20*REGBYTES(sp)
LOAD x22, 21*REGBYTES(sp)
LOAD x23, 22*REGBYTES(sp)
LOAD x24, 23*REGBYTES(sp)
LOAD x25, 24*REGBYTES(sp)
LOAD x26, 25*REGBYTES(sp)
LOAD x27, 26*REGBYTES(sp)
LOAD x28, 27*REGBYTES(sp)
LOAD x29, 28*REGBYTES(sp)
LOAD x30, 29*REGBYTES(sp)
LOAD x31, 30*REGBYTES(sp)
// Restore SP
//csrr t0, sscratch
LOAD x2, 1*REGBYTES(sp)
sret
.global flush_mmu
flush_mmu:
sfence.vma
ret