rt/arch/arm/r/syscall.S

168 lines
3.9 KiB
ArmAsm

@ vim:ft=arm
/*
* The interrupt management code for the syscall handler depends on both the
* type of interrupt controller and the source of the syscall interrupt, so we
* provide a way to extend this code with assembly macros.
* vic_syscall_start needs to mask and clear the syscall interrupt such that
* another syscall cannot occur when IRQs are re-enabled. vic_syscall_finish
* must unmask the syscall interrupt. Both are run with IRQs disabled.
*/
#include <rt/mpu.h>
#include <vic.S>
#include "mode.h"
.macro mpuconfigure
#if RT_MPU_ENABLE
ldr r1, =rt_mpu_config
ldr r1, [r1]
movs r2, RT_MPU_TASK_REGION_START_ID
adds r5, r2, RT_MPU_NUM_TASK_REGIONS
.Lmpusetloop\@:
// Set the region number register.
mcr p15, 0, r2, c6, c2, 0
// Load the region configuration and advance to the next one.
ldmia r1!, {r3, r4}
// Set the base address register.
mcr p15, 0, r3, c6, c1, 0
// Set the size and enable register.
uxtb r3, r4
mcr p15, 0, r3, c6, c1, 2
// Set the access control register.
lsrs r3, r4, 16
mcr p15, 0, r3, c6, c1, 4
add r2, 1
cmp r2, r5
blt .Lmpusetloop\@
dsb
#endif
.endm
#ifdef __thumb__
.thumb
#else
.arm
// arm mode doesn't have cbz, so make our own.
.macro cbz reg, label
cmp \reg, 0
beq \label
.endm
#endif
.syntax unified
.section .text.rt_syscall_handler,"ax",%progbits
.global rt_syscall_handler
.type rt_syscall_handler, %function
#ifdef RT_VIC_IRQ_ALIGN
.balign RT_VIC_IRQ_ALIGN
#endif
rt_syscall_handler:
sub lr, 4
// Some VIC implementations use a separate SVC handler.
.ifndef rt_syscall_handler_svc
.global rt_syscall_handler_svc
.type rt_syscall_handler_svc, %function
rt_syscall_handler_svc:
.endif
// Push lr (task pc) and spsr (task cpsr) to the system/user stack.
srsdb sp!, MODE_SYS
cps MODE_SYS
// Save the task's volatile registers.
push {r0-r3, r12, lr}
vic_syscall_start
cpsie i, MODE_SVC
bl rt_syscall_run
cps MODE_SYS
// If there's no new context to switch to, return early.
cbz r0, .Lreturn
/* Ordinarily a clrex is necessary here, but rt_syscall_run uses strex,
* which also clears the exclusive monitor. */
#ifdef __ARM_FP
// Check if the task has a floating-point context.
mrc p15, 0, r1, cr1, cr0, 2
tst r1, 0xf00000 // CPACR_10_FULL_ACCESS | CPACR_11_FULL_ACCESS
beq .Lskip_fp_save
vpush {d0-d15}
vmrs r2, fpexc
vmrs r3, fpscr
push {r2, r3}
.Lskip_fp_save:
push {r1, r4-r11} // cpacr, callee-saved registers
#else // !defined(__ARM_FP)
push {r4-r11}
#endif // __ARM_FP
// Store the stack pointer with the saved context.
ldr r2, =rt_context_prev
ldr r2, [r2]
str sp, [r2]
mpuconfigure
// Switch to the new task stack returned by rt_syscall_run.
mov sp, r0
#ifdef __ARM_FP
pop {r1, r4-r11}
mcr p15, 0, r1, cr1, cr0, 2
tst r1, 0xf00000
beq .Lskip_fp_restore
pop {r2, r3}
vmsr fpexc, r2
vmsr fpscr, r3
vpop {d0-d15}
.Lskip_fp_restore:
#else // !defined(__ARM_FP)
pop {r4-r11}
#endif // __ARM_FP
.Lreturn:
/* Disable interrupts again before unmasking the syscall interrupt at the
* VIC; otherwise, repeated syscalls could grow the task stack
* indefinitely. */
cpsid i
vic_syscall_finish
// Restore the task's volatile registers.
pop {r0-r3, r12, lr}
// Restore the task's pc and cpsr (this re-enables interrupts).
rfeia sp!
.size rt_syscall_handler, .-rt_syscall_handler
.section .text.rt_start,"ax",%progbits
.global rt_start
.type rt_start, %function
rt_start:
bl rt_start_context
cps MODE_SYS
mpuconfigure
mov sp, r0
#ifdef __ARM_FP
pop {r1, r4-r11}
mcr p15, 0, r1, cr1, cr0, 2
#else // !defined(__ARM_FP)
pop {r4-r11}
#endif // __ARM_FP
pop {r0-r3, r12, lr}
rfeia sp!
.size rt_start, .-rt_start