blob: c0f998faa34920644c991509d55ff1180a77f0b3 [file] [log] [blame]
/*
* 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)
*/
#include <autoconf.h>
#ifdef CONFIG_LIB_SEL4_VSPACE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sel4/sel4.h>
#include <vka/vka.h>
#include <vka/object.h>
#include <vspace/vspace.h>
#include <sel4utils/thread.h>
#include <sel4utils/util.h>
#include <sel4utils/arch/util.h>
#include "helpers.h"
int
sel4utils_configure_thread(vka_t *vka, vspace_t *alloc, seL4_CPtr fault_endpoint,
uint8_t priority, seL4_CNode cspace, seL4_CapData_t cspace_root_data,
sel4utils_thread_t *res)
{
memset(res, 0, sizeof(sel4utils_thread_t));
int error = vka_alloc_tcb(vka, &res->tcb);
if (error == -1) {
LOG_ERROR("vka_alloc tcb failed");
sel4utils_clean_up_thread(vka, alloc, res);
return -1;
}
res->ipc_buffer_addr = (seL4_Word) vspace_new_ipc_buffer(alloc, &res->ipc_buffer);
if (res->ipc_buffer_addr == 0) {
LOG_ERROR("ipc buffer allocation failed");
return -1;
}
seL4_CapData_t null_cap_data = {{0}};
error = seL4_TCB_Configure(res->tcb.cptr, fault_endpoint, priority, cspace, cspace_root_data,
vspace_get_root(alloc), null_cap_data,
res->ipc_buffer_addr, res->ipc_buffer);
if (error != seL4_NoError) {
LOG_ERROR("TCB configure failed with seL4 error code %d", error);
sel4utils_clean_up_thread(vka, alloc, res);
return -1;
}
res->stack_top = vspace_new_stack(alloc);
if (res->stack_top == NULL) {
LOG_ERROR("Stack allocation failed!");
sel4utils_clean_up_thread(vka, alloc, res);
return -1;
}
return 0;
}
int
sel4utils_internal_start_thread(sel4utils_thread_t *thread, void *entry_point, void *arg0,
void *arg1, int resume, void *stack_top)
{
seL4_UserContext context = {0};
size_t context_size = sizeof(seL4_UserContext) / sizeof(seL4_Word);
int error;
#ifdef ARCH_IA32
#ifdef CONFIG_X86_64
context.rdi = (seL4_Word)arg0;
context.rsi = (seL4_Word)arg1;
context.rdx = (seL4_Word)thread->ipc_buffer_addr;
context.rsp = (seL4_Word) thread->stack_top;
context.gs = IPCBUF_GDT_SELECTOR;
context.rip = (seL4_Word)entry_point;
#else
seL4_Word *stack_ptr = (seL4_Word *) stack_top;
stack_ptr[-5] = (seL4_Word) arg0;
stack_ptr[-4] = (seL4_Word) arg1;
stack_ptr[-3] = (seL4_Word) thread->ipc_buffer_addr;
context.esp = (seL4_Word) (thread->stack_top - 24);
context.gs = IPCBUF_GDT_SELECTOR;
context.eip = (seL4_Word)entry_point;
#endif
/* check that stack is aligned correctly */
assert((seL4_Word) thread->stack_top % (sizeof(seL4_Word) * 2) == 0);
error = seL4_TCB_WriteRegisters(thread->tcb.cptr, resume, 0, context_size, &context);
#elif ARCH_ARM /* ARCH_IA32 */
context.pc = (seL4_Word) entry_point;
context.sp = (seL4_Word) thread->stack_top;
context.r0 = (seL4_Word) arg0;
context.r1 = (seL4_Word) arg1;
context.r2 = (seL4_Word) thread->ipc_buffer_addr;
/* check that stack is aligned correctly */
assert((uint32_t) thread->stack_top % (sizeof(seL4_Word) * 2) == 0);
error = seL4_TCB_WriteRegisters(thread->tcb.cptr, resume, 0, context_size, &context);
#endif /* ARCH_ARM */
if (error) {
LOG_ERROR("seL4_TCB_WriteRegisters failed with error %d", error);
return -1;
}
return 0;
}
int
sel4utils_start_thread(sel4utils_thread_t *thread, void *entry_point, void *arg0, void *arg1,
int resume)
{
return sel4utils_internal_start_thread(thread, entry_point, arg0, arg1, resume, thread->stack_top);
}
void
sel4utils_clean_up_thread(vka_t *vka, vspace_t *alloc, sel4utils_thread_t *thread)
{
if (thread->tcb.cptr != 0) {
vka_free_object(vka, &thread->tcb);
}
if (thread->ipc_buffer_addr != 0) {
vspace_free_ipc_buffer(alloc, (seL4_Word *) thread->ipc_buffer_addr);
}
if (thread->stack_top != 0) {
vspace_free_stack(alloc, thread->stack_top);
}
memset(thread, 0, sizeof(sel4utils_thread_t));
}
void
sel4utils_print_fault_message(seL4_MessageInfo_t tag, char *thread_name)
{
switch (seL4_MessageInfo_get_label(tag)) {
case SEL4_PFIPC_LABEL:
assert(seL4_MessageInfo_get_length(tag) == SEL4_PFIPC_LENGTH);
printf("%sPagefault from [%s]: %s %s at PC: 0x"XFMT" vaddr: 0x"XFMT"%s\n",
COLOR_ERROR,
thread_name,
sel4utils_is_read_fault() ? "read" : "write",
seL4_GetMR(SEL4_PFIPC_PREFETCH_FAULT) ? "prefetch fault" : "fault",
seL4_GetMR(SEL4_PFIPC_FAULT_IP),
seL4_GetMR(SEL4_PFIPC_FAULT_ADDR),
COLOR_NORMAL);
break;
case SEL4_EXCEPT_IPC_LABEL:
assert(seL4_MessageInfo_get_length(tag) == SEL4_EXCEPT_IPC_LENGTH);
printf("%sBad syscall from [%s]: scno "DFMT" at PC: 0x"XFMT"%s\n",
COLOR_ERROR,
thread_name,
seL4_GetMR(EXCEPT_IPC_SYS_MR_SYSCALL),
seL4_GetMR(EXCEPT_IPC_SYS_MR_IP),
COLOR_NORMAL
);
break;
case SEL4_USER_EXCEPTION_LABEL:
assert(seL4_MessageInfo_get_length(tag) == SEL4_USER_EXCEPTION_LENGTH);
printf("%sInvalid instruction from [%s] at PC: 0x"XFMT"%s\n",
COLOR_ERROR,
thread_name,
seL4_GetMR(0),
COLOR_NORMAL);
break;
default:
/* What? Why are we here? What just happened? */
printf("Unknown fault from [%s]: "DFMT" (length = "DFMT")\n", thread_name, seL4_MessageInfo_get_label(tag), seL4_MessageInfo_get_length(tag));
break;
}
}
static int
fault_handler(char *name, seL4_CPtr endpoint) {
seL4_Word badge;
seL4_MessageInfo_t info = seL4_Wait(endpoint, &badge);
sel4utils_print_fault_message(info, name);
/* go back to sleep so other things can run */
seL4_Wait(endpoint, &badge);
return 0;
}
int
sel4utils_start_fault_handler(seL4_CPtr fault_endpoint, vka_t *vka, vspace_t *vspace,
uint8_t prio, seL4_CPtr cspace, seL4_CapData_t cap_data, char *name,
sel4utils_thread_t *res)
{
int error = sel4utils_configure_thread(vka, vspace, 0, prio, cspace,
cap_data, res);
if (error) {
LOG_ERROR("Failed to configure fault handling thread\n");
return -1;
}
return sel4utils_start_thread(res, fault_handler, (void *) name,
(void *) fault_endpoint, 1);
}
#endif /* CONFIG_LIB_SEL4_VSPACE */