blob: 544ef5d9fa097c1b649becb6a5cb803a95046f2d [file]
/*
* Copyright 2014, NICTA
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(NICTA_BSD)
*/
#ifndef _SEL4UTILS_ARCH_UTIL_H
#define _SEL4UTILS_ARCH_UTIL_H
#include <stdint.h>
#include <sel4/sel4.h>
#include <sel4/arch/pfIPC.h>
#include <sel4/arch/exIPC.h>
#define EXCEPT_IPC_SYS_MR_IP EXCEPT_IPC_SYS_MR_EIP
static inline int
sel4utils_is_read_fault(void)
{
seL4_Word fsr = seL4_GetMR(SEL4_PFIPC_FSR);
return (fsr & (1 << 1)) == 0;
}
static inline void
sel4utils_set_instruction_pointer(seL4_UserContext *regs, seL4_Word value)
{
regs->eip = value;
}
static inline seL4_Word
sel4utils_get_instruction_pointer(seL4_UserContext regs)
{
return regs.eip;
}
static inline seL4_Word
sel4utils_get_sp(seL4_UserContext regs)
{
return regs.esp;
}
static inline void
sel4utils_set_stack_pointer(seL4_UserContext *regs, seL4_Word value)
{
regs->esp = value;
}
/* Helper function for writing your OWN TLS_BASE register on x86.
* This function assumes that the tcb capability your are modifying
* is the currently executing thread and will not work otherwise.
* This function is also hand written and changes to the IPC buffer layout,
* number of message registers, or WriteRegisters invocation will need
* to be manually reflected here. This is all neccessary as we need
* to tread very carefully over our registers and stack to make this work */
static inline int sel4utils_ia32_tcb_set_tls_base(seL4_CPtr tcb_cap, seL4_Word tls_base)
{
/* Declared as variables since input operands cannot overlap with clobbers */
uint32_t edi = 1; /* first message argument. 1 means 'resume_target' everything else is 0 */
uint32_t ecx = sizeof(seL4_UserContext) / sizeof(seL4_Word); /* second message argument. number of registers of thread to modify */
seL4_MessageInfo_t tag = seL4_MessageInfo_new(TCBWriteRegisters, 0, 0, ecx + 2); /* message length is user context + 2 arguments */
asm volatile(
/* eax is what we want for tls_base, so write this first so we can reuse eax */
"movl %%eax, %%gs:24 \n"
"movl %%eax, %%gs:52 \n"
/* Get a copy of the stack pointer */
"movl %%esp, %%eax \n"
/* The value we want to be set is the current value - 4, as we will push to it in the invocation stub */
"subl $4, %%eax \n"
"movl %%eax, %%gs:16 \n"
/* The kernel should not let us overwrite our own EIP, but set
* something sensible anyway */
"leal 2f, %%eax \n"
"movl %%eax, %%gs:12 \n"
/* Set fs and gs. We set FS to the correct tls selector, and leave gs unchanged */
"movl %[tls], %%eax \n"
"movl %%eax, %%gs:56 \n"
"movl %%gs, %%eax \n"
"movl %%eax, %%gs:60 \n"
/* No point saving any other registers as they are going to get reset
* as a result of the syscall anyway */
/* Now the syscall invocation */
"movl %[syscall], %%eax \n" /* we the syscall value */
"pushl %%ebp \n" /* we added 4 bytes to esp earlier so after the write registers our stack will point to here */
"movl %%ecx, %%ebp \n" /* stash ecx for after invocation */
"movl %%esp, %%ecx \n" /* esp goes into ecx, per sysenter convention */
"leal 1f, %%edx \n" /* calculate return address */
"1: \n"
"sysenter \n"
"2: \n"
"movl %%ebp, %%ecx \n"
"popl %%ebp \n"
: "=S" (tag),
"=D" (edi),
"=c" (ecx),
"=b" (tcb_cap)
: "a" (tls_base),
[syscall] "i" (seL4_SysCall),
[tls] "i" (TLS_GDT_SELECTOR),
"b" (tcb_cap),
"S" (tag.words[0]),
"D" (edi),
"c" (ecx)
: "cc", /* we possibly clobber the eflags register */
"%edx", /* edx gets clobbered due to sysexit convention */
"memory"
);
/* seL4 will not actually reload our tls_base until it switches to us.
* we can force this to happen with a yield */
seL4_Yield();
return seL4_MessageInfo_get_label(tag);
}
#endif /* _SEL4UTILS_ARCH_UTIL_H */