158 lines
4.6 KiB
ArmAsm
158 lines
4.6 KiB
ArmAsm
|
#include <machine/asm.h>
|
||
|
#include <ucontextoffsets.h>
|
||
|
|
||
|
|
||
|
IMPORT(getuctx)
|
||
|
IMPORT(setuctx)
|
||
|
IMPORT(resumecontext)
|
||
|
|
||
|
|
||
|
/* MCF_MAGIC value from <mcontext.h> */
|
||
|
#define MCF_MAGIC 0xc0ffee
|
||
|
|
||
|
/* Values from <sys/ucontext.h> */
|
||
|
#define UCF_IGNFPU 0x002
|
||
|
#define UCF_IGNSIGM 0x004
|
||
|
|
||
|
|
||
|
/* EINVAL from errno.h */
|
||
|
#define EFAULT 14
|
||
|
#define EINVAL 22
|
||
|
|
||
|
/* int getcontext(ucontext_t *ucp)
|
||
|
* Initialise the structure pointed to by ucp to the current user context
|
||
|
* of the calling thread. */
|
||
|
ENTRY(getcontext)
|
||
|
/* In case a process does not use the FPU and is neither interested in
|
||
|
* saving its signal mask, then we can skip the context switch to
|
||
|
* PM and kernel altogether and only save general-purpose registers. */
|
||
|
|
||
|
mov r3, lr /* Save return address:
|
||
|
* When setcontext or swapcontext is called,
|
||
|
* we jump to this address and continue
|
||
|
* running. */
|
||
|
|
||
|
/* r0 = ucp */
|
||
|
|
||
|
/* Check null pointer */
|
||
|
cmp r0, #0 /* ucp == NULL? */
|
||
|
bne 3f /* Not null, continue */
|
||
|
mov r1, #EFAULT
|
||
|
ldr r2, =_C_LABEL(errno)
|
||
|
str r1, [r2] /* errno = EFAULT */
|
||
|
mov r0, #-1 /* return -1 */
|
||
|
bx lr
|
||
|
|
||
|
3: /* Check flags */
|
||
|
ldr r1, [r0, #UC_FLAGS] /* r1 = ucp->uc_flags */
|
||
|
mov r2, #(UCF_IGNFPU | UCF_IGNSIGM)
|
||
|
cmp r1, r2 /* is UCF_IGNFPU or UCF_IGNSIGM set? */
|
||
|
beq 1f /* Both are set, skip getuctx */
|
||
|
|
||
|
0:
|
||
|
push {r0, r3}
|
||
|
bl _C_LABEL(getuctx) /* getuctx(ucp) */
|
||
|
pop {r0, r3}
|
||
|
|
||
|
1:
|
||
|
/* Save the context */
|
||
|
mov lr, r3 /* Restore lr */
|
||
|
str lr, [r0, #LRREG] /* Save lr */
|
||
|
str lr, [r0, #PCREG] /* Save real RTA in mcp struct */
|
||
|
str sp, [r0, #SPREG] /* Save stack pointer */
|
||
|
str fp, [r0, #FPREG] /* Save fp */
|
||
|
str r4, [r0, #REG4] /* Save r4 */
|
||
|
str r5, [r0, #REG5] /* Save r5 */
|
||
|
str r6, [r0, #REG6] /* Save r6 */
|
||
|
str r7, [r0, #REG7] /* Save r7 */
|
||
|
str r8, [r0, #REG8] /* Save r8 */
|
||
|
str r9, [r0, #REG9] /* Save r9 */
|
||
|
str r10, [r0, #REG10] /* Save r10 */
|
||
|
|
||
|
ldr r1, =MCF_MAGIC
|
||
|
str r1, [r0, #MAGIC] /* Set magic value */
|
||
|
|
||
|
mov r1, #0
|
||
|
str r1, [r0, #REG0] /* Return 0 */
|
||
|
mov r0, #0 /* Return 0 */
|
||
|
|
||
|
2:
|
||
|
bx lr /* Restore return address */
|
||
|
|
||
|
|
||
|
/* int setcontext(const ucontext_t *ucp)
|
||
|
* Restore the user context pointed to by ucp. A successful call to
|
||
|
* setcontext does not return; program execution resumes at the point
|
||
|
* specified by the ucp argument. If ucp was created with getcontext(),
|
||
|
* program execution continues as if the corresponding call of getcontext()
|
||
|
* had just returned. If ucp was created with makecontext(), program
|
||
|
* execution continues with the function passed to makecontext(). */
|
||
|
ENTRY(setcontext)
|
||
|
/* In case a process does not use the FPU and is neither interested in
|
||
|
* restoring its signal mask, then we can skip the context switch to
|
||
|
* PM and kernel altogether and restore state here. */
|
||
|
|
||
|
/* r0 = ucp */
|
||
|
|
||
|
/* Check null pointer */
|
||
|
cmp r0, #0 /* ucp == NULL? */
|
||
|
bne 3f /* Not null, continue */
|
||
|
mov r1, #EFAULT
|
||
|
ldr r2, =_C_LABEL(errno)
|
||
|
str r1, [r2] /* errno = EFAULT */
|
||
|
mov r0, #-1 /* return -1 */
|
||
|
bx lr
|
||
|
|
||
|
3: /* Check flags */
|
||
|
ldr r1, [r0, #MAGIC] /* r1 = ucp->mc_context.mc_magic */
|
||
|
ldr r2, =MCF_MAGIC
|
||
|
cmp r1, r2 /* is the magic value set (is context valid)?*/
|
||
|
beq 4f /* is set, proceed */
|
||
|
mov r1, #EINVAL /* not set, return error code */
|
||
|
ldr r2, =_C_LABEL(errno)
|
||
|
str r1, [r2] /* errno = EINVAL */
|
||
|
mov r0, #-1 /* return -1 */
|
||
|
bx lr
|
||
|
|
||
|
|
||
|
4: ldr r1, [r0, #UC_FLAGS] /* r1 = ucp->uc_flags */
|
||
|
mov r2, #(UCF_IGNFPU | UCF_IGNSIGM)
|
||
|
cmp r1, r2 /* Are UCF_IGNFPU and UCF_IGNSIGM flags set? */
|
||
|
beq 1f /* Both are set, so don't bother restoring FPU
|
||
|
* state and signal mask */
|
||
|
|
||
|
push {r0, r3}
|
||
|
0: bl _C_LABEL(setuctx) /* setuctx(ucp) */
|
||
|
pop {r0, r3}
|
||
|
|
||
|
1: /* Restore the registers */
|
||
|
ldr r1, [r0, #REG1] /* Restore r1 */
|
||
|
ldr r2, [r0, #REG2] /* Restore r2 */
|
||
|
ldr r3, [r0, #REG3] /* Restore r3 */
|
||
|
ldr r4, [r0, #REG4] /* Restore r4 */
|
||
|
ldr r5, [r0, #REG5] /* Restore r5 */
|
||
|
ldr r6, [r0, #REG6] /* Restore r6 */
|
||
|
ldr r7, [r0, #REG7] /* Restore r7 */
|
||
|
ldr r8, [r0, #REG8] /* Restore r8 */
|
||
|
ldr r9, [r0, #REG9] /* Restore r9 */
|
||
|
ldr r10, [r0, #REG10] /* Restore r10 */
|
||
|
ldr r12, [r0, #REG12] /* Restore r12 */
|
||
|
ldr fp, [r0, #FPREG] /* Restore fp */
|
||
|
ldr sp, [r0, #SPREG] /* Restore sp */
|
||
|
ldr lr, [r0, #LRREG] /* Restore lr */
|
||
|
mov r4, r0
|
||
|
ldr r0, [r4, #REG0] /* Restore r0 */
|
||
|
2:
|
||
|
ldr pc, [r4, #PCREG] /* Restore pc */
|
||
|
|
||
|
|
||
|
/* void ctx_start()
|
||
|
* A wrapper to call resumecontext. Makecontext puts the ucp in r4.
|
||
|
* This function moves the ucp into r0 so that the ucp is the first
|
||
|
* parameter for resumecontext. The call to resumecontext will start
|
||
|
* the next context in the linked list (or exit the program if there
|
||
|
* is no context). */
|
||
|
ENTRY(ctx_start)
|
||
|
mov r0, r4
|
||
|
b _C_LABEL(resumecontext)
|