mirror of
https://github.com/clearlinux/graphene.git
synced 2026-10-03 15:39:36 +00:00
[Linux PAL] Rewrite signal handling
This commit is contained in:
committed by
Chia-Che Tsai
parent
e8f334b220
commit
8aaeafc0a3
@@ -8,7 +8,6 @@ AR = ar rcs
|
||||
LD = ld
|
||||
|
||||
CFLAGS = -Wall -fPIC -O2 -std=gnu99 -fgnu89-inline -U_FORTIFY_SOURCE \
|
||||
-fno-omit-frame-pointer \
|
||||
-fno-stack-protector -fno-builtin
|
||||
ASFLAGS = -DPIC -DSHARED -fPIC -DASSEMBLER -Wa,--noexecstack \
|
||||
-x assembler-with-cpp
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "pal.h"
|
||||
#include "pal_internal.h"
|
||||
#include "pal_linux.h"
|
||||
#include "pal_debug.h"
|
||||
#include "pal_error.h"
|
||||
#include "pal_security.h"
|
||||
#include "api.h"
|
||||
@@ -114,14 +115,8 @@ typedef struct {
|
||||
PAL_IDX event_num;
|
||||
PAL_CONTEXT context;
|
||||
ucontext_t * uc;
|
||||
PAL_PTR eframe;
|
||||
} PAL_EVENT;
|
||||
|
||||
#define save_return_point(ptr) \
|
||||
asm volatile ("leaq 0(%%rip), %%rax\r\n" \
|
||||
"movq %%rax, %0\r\n" \
|
||||
: "=b"(ptr) :: "memory", "rax")
|
||||
|
||||
static int get_event_num (int signum)
|
||||
{
|
||||
switch(signum) {
|
||||
@@ -136,8 +131,7 @@ static int get_event_num (int signum)
|
||||
}
|
||||
|
||||
void _DkGenericEventTrigger (PAL_IDX event_num, PAL_EVENT_HANDLER upcall,
|
||||
PAL_NUM arg, struct pal_frame * frame,
|
||||
ucontext_t * uc, void * eframe)
|
||||
PAL_NUM arg, ucontext_t * uc)
|
||||
{
|
||||
PAL_EVENT event;
|
||||
event.event_num = event_num;
|
||||
@@ -145,31 +139,13 @@ void _DkGenericEventTrigger (PAL_IDX event_num, PAL_EVENT_HANDLER upcall,
|
||||
if (uc)
|
||||
memcpy(&event.context, uc->uc_mcontext.gregs, sizeof(PAL_CONTEXT));
|
||||
|
||||
if (frame) {
|
||||
event.context.r15 = frame->arch.r15;
|
||||
event.context.r14 = frame->arch.r14;
|
||||
event.context.r13 = frame->arch.r13;
|
||||
event.context.r12 = frame->arch.r12;
|
||||
event.context.rdi = frame->arch.rdi;
|
||||
event.context.rsi = frame->arch.rsi;
|
||||
event.context.rbx = frame->arch.rbx;
|
||||
/* find last frame */
|
||||
event.context.rsp = frame->arch.rbp + sizeof(unsigned long) * 2;
|
||||
event.context.rbp = ((unsigned long *) frame->arch.rbp)[0];
|
||||
event.context.rip = ((unsigned long *) frame->arch.rbp)[1];
|
||||
/* making rax = 0 to tell the caller that this PAL call failed */
|
||||
event.context.rax = 0;
|
||||
}
|
||||
|
||||
event.uc = uc;
|
||||
event.eframe = eframe;
|
||||
|
||||
(*upcall) ((PAL_PTR) &event, arg, &event.context);
|
||||
}
|
||||
|
||||
static bool _DkGenericSignalHandle (int event_num, siginfo_t * info,
|
||||
struct pal_frame * frame,
|
||||
ucontext_t * uc, void * eframe)
|
||||
ucontext_t * uc)
|
||||
{
|
||||
PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event_num);
|
||||
|
||||
@@ -181,133 +157,113 @@ static bool _DkGenericSignalHandle (int event_num, siginfo_t * info,
|
||||
event_num == PAL_EVENT_ILLEGAL)
|
||||
arg = (PAL_NUM) (info ? info->si_addr : 0);
|
||||
|
||||
_DkGenericEventTrigger(event_num, upcall, arg, frame, uc, eframe);
|
||||
_DkGenericEventTrigger(event_num, upcall, arg, uc);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ADDR_IN_PAL(addr) \
|
||||
((void *) (addr) > TEXT_START && (void *) (addr) < TEXT_END)
|
||||
|
||||
/* This function walks the stack to find the PAL_FRAME
|
||||
* that was saved upon entry to the PAL, if an exception/interrupt
|
||||
* comes in during a PAL call. This is needed to support the behavior that an
|
||||
* exception in the PAL has Unix-style, EAGAIN semantics.
|
||||
*
|
||||
* The PAL_FRAME is supposed to be in the first PAL frame, and we look for
|
||||
* it by matching a special magic number, that should only appear on the stack
|
||||
* once.
|
||||
*
|
||||
* If an exception comes in while we are not in the PAL, this PAL_FRAME won't
|
||||
* exist, and it is ok to return NULL.
|
||||
*/
|
||||
static struct pal_frame * get_frame (ucontext_t * uc)
|
||||
{
|
||||
unsigned long rip = uc->uc_mcontext.gregs[REG_RIP];
|
||||
unsigned long rbp = uc->uc_mcontext.gregs[REG_RBP];
|
||||
unsigned long last_rbp = rbp - 64;
|
||||
|
||||
if (!ADDR_IN_PAL(rip))
|
||||
return NULL;
|
||||
|
||||
while (ADDR_IN_PAL(((unsigned long *) rbp)[1])) {
|
||||
last_rbp = rbp;
|
||||
rbp = *(unsigned long *) rbp;
|
||||
}
|
||||
|
||||
/* search frame record in the top frame of PAL */
|
||||
for (unsigned long ptr = rbp - sizeof(unsigned long) ;
|
||||
ptr > last_rbp ; ptr -= 8) {
|
||||
struct pal_frame * frame = (struct pal_frame *) ptr;
|
||||
if (frame->identifier == PAL_FRAME_IDENTIFIER)
|
||||
return frame;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void return_frame (struct pal_frame * frame, int err)
|
||||
{
|
||||
if (err)
|
||||
_DkRaiseFailure(err);
|
||||
|
||||
__clear_frame(frame);
|
||||
arch_restore_frame(&frame->arch);
|
||||
|
||||
asm volatile ("xor %rax, %rax\r\n"
|
||||
"leaveq\r\n"
|
||||
"retq\r\n");
|
||||
}
|
||||
|
||||
#if BLOCK_SIGFAULT == 1
|
||||
static char exception_msg[24] = "--- SIGSEGV --- [ ]\n";
|
||||
static volatile bool cont_exec = false;
|
||||
#endif
|
||||
|
||||
static void _DkGenericSighandler (int signum, siginfo_t * info,
|
||||
struct ucontext * uc)
|
||||
{
|
||||
#if BLOCK_SIGFUALT == 1
|
||||
/* reseurrect this code if signal handler if giving segmentation fault */
|
||||
if (signum == SIGSEGV) {
|
||||
int pid = INLINE_SYSCALL(getpid, 0);
|
||||
exception_msg[17] = '0' + pid / 10000;
|
||||
exception_msg[18] = '0' + (pid / 1000) % 10;
|
||||
exception_msg[19] = '0' + (pid / 100) % 10;
|
||||
exception_msg[20] = '0' + (pid / 10) % 10;
|
||||
exception_msg[21] = '0' + pid % 10;
|
||||
INLINE_SYSCALL(write, 3, 1, exception_msg, 24);
|
||||
while(!cont_exec);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct pal_frame * frame = get_frame(uc);
|
||||
void * eframe;
|
||||
|
||||
if (signum == SIGCONT && frame && frame->func == DkObjectsWaitAny)
|
||||
return;
|
||||
|
||||
asm volatile ("movq %%rbp, %0" : "=r"(eframe));
|
||||
|
||||
if (frame && frame->func != &_DkGenericSighandler &&
|
||||
signum != SIGCONT &&
|
||||
signum != SIGINT &&
|
||||
signum != SIGTERM) {
|
||||
return_frame(frame, PAL_ERROR_BADADDR);
|
||||
return;
|
||||
}
|
||||
|
||||
int event_num = get_event_num(signum);
|
||||
if (event_num == -1)
|
||||
return;
|
||||
|
||||
_DkGenericSignalHandle(event_num, info, frame, uc, eframe);
|
||||
uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
|
||||
if (ADDR_IN_PAL(rip)) {
|
||||
// We expect none of the memory faults, illegal instructions, or arithmetic exceptions
|
||||
// will happen in PAL. If these exceptions happen in PAL, exit the thread with loud warning.
|
||||
int pid = INLINE_SYSCALL(getpid, 0);
|
||||
int tid = INLINE_SYSCALL(gettid, 0);
|
||||
const char * name = "exception";
|
||||
switch(event_num) {
|
||||
case PAL_EVENT_DIVZERO: name = "div-by-zero exception"; break;
|
||||
case PAL_EVENT_MEMFAULT: name = "memory fault"; break;
|
||||
case PAL_EVENT_ILLEGAL: name = "illegal instruction"; break;
|
||||
}
|
||||
|
||||
printf("*** An unexpected %s occurred inside PAL. Exiting the thread. "
|
||||
"(PID = %d, TID = %d, RIP = +%p) ***\n",
|
||||
name, pid, tid, rip - (uintptr_t) TEXT_START);
|
||||
_DkThreadExit();
|
||||
return;
|
||||
}
|
||||
|
||||
_DkGenericSignalHandle(event_num, info, uc);
|
||||
}
|
||||
|
||||
static void _DkTerminateSighandler (int signum, siginfo_t * info,
|
||||
struct ucontext * uc)
|
||||
{
|
||||
struct pal_frame * frame = get_frame(uc);
|
||||
void * eframe;
|
||||
|
||||
asm volatile ("movq %%rbp, %0" : "=r"(eframe));
|
||||
|
||||
int event_num = get_event_num(signum);
|
||||
if (event_num == -1)
|
||||
return;
|
||||
|
||||
if (!_DkGenericSignalHandle(event_num, NULL, frame, uc, eframe))
|
||||
uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
|
||||
|
||||
// If the signal arrives in the middle of a PAL call, add the event
|
||||
// to pending in the current TCB.
|
||||
if (ADDR_IN_PAL(rip)) {
|
||||
PAL_TCB * tcb = get_tcb();
|
||||
assert(tcb);
|
||||
if (!tcb->pending_event) {
|
||||
// Use the preserved pending event slot
|
||||
tcb->pending_event = event_num;
|
||||
} else {
|
||||
// If there is already a pending event, add the new event to the queue.
|
||||
// (a relatively rare case.)
|
||||
struct event_queue * ev = malloc(sizeof(*ev));
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
INIT_LIST_HEAD(ev, list);
|
||||
ev->event_num = event_num;
|
||||
listp_add_tail(ev, &tcb->pending_queue, list);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the event handler. If there is no handler, terminate the thread
|
||||
// unless it is a resuming event (then ignore the event).
|
||||
if (!_DkGenericSignalHandle(event_num, NULL, uc) && event_num != PAL_EVENT_RESUME)
|
||||
_DkThreadExit();
|
||||
}
|
||||
|
||||
static void _DkPipeSighandler (int signum, siginfo_t * info,
|
||||
struct ucontext * uc)
|
||||
{
|
||||
uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
|
||||
assert(ADDR_IN_PAL(rip)); // This signal can only happens inside PAL
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* __check_pending_event(): checks the existence of a pending event in the TCB
|
||||
* and handles the event consequently.
|
||||
*/
|
||||
void __check_pending_event (void)
|
||||
{
|
||||
PAL_TCB * tcb = get_tcb();
|
||||
assert(tcb);
|
||||
if (tcb->pending_event) {
|
||||
int event = tcb->pending_event;
|
||||
tcb->pending_event = 0;
|
||||
_DkGenericSignalHandle(event, NULL, NULL);
|
||||
|
||||
if (!listp_empty(&tcb->pending_queue)) {
|
||||
// If there are more than one pending events, process them from the queue
|
||||
struct event_queue * ev, * n;
|
||||
listp_for_each_entry_safe(ev, n, &tcb->pending_queue, list) {
|
||||
listp_del(ev, &tcb->pending_queue, list);
|
||||
_DkGenericSignalHandle(ev->event_num, NULL, NULL);
|
||||
free(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _DkRaiseFailure (int error)
|
||||
{
|
||||
PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(PAL_EVENT_FAILURE);
|
||||
@@ -318,7 +274,6 @@ void _DkRaiseFailure (int error)
|
||||
PAL_EVENT event;
|
||||
event.event_num = PAL_EVENT_FAILURE;
|
||||
event.uc = NULL;
|
||||
event.eframe = NULL;
|
||||
|
||||
(*upcall) ((PAL_PTR) &event, error, NULL);
|
||||
}
|
||||
@@ -340,7 +295,7 @@ struct signal_ops on_signals[] = {
|
||||
[PAL_EVENT_SUSPEND] = { .signum = { SIGINT, 0 },
|
||||
.handler = _DkTerminateSighandler },
|
||||
[PAL_EVENT_RESUME] = { .signum = { SIGCONT, 0 },
|
||||
.handler = _DkGenericSighandler },
|
||||
.handler = _DkTerminateSighandler },
|
||||
};
|
||||
|
||||
static int _DkPersistentSighandlerSetup (int event_num)
|
||||
@@ -389,28 +344,6 @@ err:
|
||||
void _DkExceptionReturn (void * event)
|
||||
{
|
||||
PAL_EVENT * e = event;
|
||||
|
||||
if (e->eframe) {
|
||||
struct pal_frame * frame = (struct pal_frame *) e->eframe;
|
||||
int err = 0;
|
||||
|
||||
switch (e->event_num) {
|
||||
case PAL_EVENT_MEMFAULT:
|
||||
err = PAL_ERROR_BADADDR;
|
||||
break;
|
||||
case PAL_EVENT_QUIT:
|
||||
case PAL_EVENT_SUSPEND:
|
||||
case PAL_EVENT_RESUME:
|
||||
err = PAL_ERROR_INTERRUPTED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
_DkRaiseFailure(err);
|
||||
|
||||
__clear_frame(frame);
|
||||
}
|
||||
|
||||
if (e->uc) {
|
||||
/* copy the context back to ucontext */
|
||||
memcpy(e->uc->uc_mcontext.gregs, &e->context, sizeof(PAL_CONTEXT));
|
||||
|
||||
@@ -229,6 +229,26 @@ void pal_linux_main (void * args)
|
||||
|
||||
init_slab_mgr(pagesz);
|
||||
|
||||
first_thread = malloc(HANDLE_SIZE(thread));
|
||||
if (!first_thread)
|
||||
init_fail(PAL_ERROR_NOMEM, "Out of memory");
|
||||
SET_HANDLE_TYPE(first_thread, thread);
|
||||
first_thread->thread.tid = INLINE_SYSCALL(gettid, 0);
|
||||
|
||||
void * alt_stack = NULL;
|
||||
_DkVirtualMemoryAlloc(&alt_stack, ALT_STACK_SIZE, 0, PAL_PROT_READ|PAL_PROT_WRITE);
|
||||
if (!alt_stack)
|
||||
init_fail(PAL_ERROR_NOMEM, "Out of memory");
|
||||
|
||||
// Initialize TCB at the top of the alternative stack.
|
||||
PAL_TCB * tcb = alt_stack + ALT_STACK_SIZE - sizeof(PAL_TCB);
|
||||
tcb->self = tcb;
|
||||
tcb->handle = first_thread;
|
||||
tcb->alt_stack = alt_stack; // Stack bottom
|
||||
tcb->callback = NULL;
|
||||
tcb->param = NULL;
|
||||
pal_thread_init(tcb);
|
||||
|
||||
setup_pal_map(&pal_map);
|
||||
|
||||
#if USE_VDSO_GETTIME == 1
|
||||
@@ -287,10 +307,6 @@ done_init:
|
||||
return;
|
||||
}
|
||||
|
||||
first_thread = malloc(HANDLE_SIZE(thread));
|
||||
SET_HANDLE_TYPE(first_thread, thread);
|
||||
first_thread->thread.tid = linux_state.pid;
|
||||
|
||||
signal_setup();
|
||||
|
||||
/* call to main function */
|
||||
|
||||
@@ -186,7 +186,7 @@ int _DkSegmentRegisterSet (int reg, const void * addr)
|
||||
if (reg == PAL_SEGMENT_FS) {
|
||||
ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_FS, addr);
|
||||
} else if (reg == PAL_SEGMENT_GS) {
|
||||
ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_GS, addr);
|
||||
return -PAL_ERROR_DENIED;
|
||||
} else {
|
||||
return -PAL_ERROR_INVAL;
|
||||
}
|
||||
@@ -216,7 +216,8 @@ int _DkSegmentRegisterGet (int reg, void ** addr)
|
||||
if (reg == PAL_SEGMENT_FS) {
|
||||
ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_GET_FS, &ret_addr);
|
||||
} else if (reg == PAL_SEGMENT_GS) {
|
||||
ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_GET_GS, &ret_addr);
|
||||
// The GS segment is used for the internal TCB of PAL
|
||||
return -PAL_ERROR_DENIED;
|
||||
} else {
|
||||
return -PAL_ERROR_INVAL;
|
||||
}
|
||||
|
||||
@@ -197,6 +197,16 @@ void _DkMutexRelease (PAL_HANDLE handle)
|
||||
return;
|
||||
}
|
||||
|
||||
int _DkInternalLock (PAL_LOCK* lock) {
|
||||
while (_DkMutexLock(lock) < 0); // Retry the lock if being interrupted by signals
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _DkInternalUnlock (PAL_LOCK* lock) {
|
||||
_DkMutexUnlock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mutex_wait (PAL_HANDLE handle, uint64_t timeout)
|
||||
{
|
||||
return _DkMutexAcquireTimeout(handle, timeout);
|
||||
|
||||
@@ -39,12 +39,46 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
/* default size of a new thread stack */
|
||||
/* DEP 2/4/17: There is enough stuff allocated on the PAL stack now
|
||||
* that we need two pages in Linux host mode. In SGX mode,
|
||||
* the enclave/non-clave split makes 1 page in/1 out sufficient.
|
||||
#if defined(__i386__)
|
||||
#include <asm/ldt.h>
|
||||
#else
|
||||
#include <asm/prctl.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* pal_thread_init(): An initialization wrapper of a newly-created thread (including
|
||||
* the first thread). This function accepts a TCB pointer to be set to the GS register
|
||||
* of the thread. The rest of the TCB is used as the alternative stack for signal
|
||||
* handling.
|
||||
*/
|
||||
#define THREAD_STACK_SIZE (pal_state.alloc_align * 2)
|
||||
int pal_thread_init (void * tcbptr)
|
||||
{
|
||||
PAL_TCB * tcb = tcbptr;
|
||||
int ret;
|
||||
|
||||
ret = INLINE_SYSCALL(arch_prctl, 2, ARCH_SET_GS, tcb);
|
||||
if (IS_ERR(ret))
|
||||
return -ERRNO(ret);
|
||||
|
||||
if (tcb->alt_stack) {
|
||||
// Align stack to 16 bytes
|
||||
void * alt_stack_top = (void *) ((uint64_t) tcb & ~16);
|
||||
assert(alt_stack_top > tcb->alt_stack);
|
||||
stack_t ss;
|
||||
ss.ss_sp = alt_stack_top;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = alt_stack_top - tcb->alt_stack;
|
||||
|
||||
ret = INLINE_SYSCALL(sigaltstack, 2, &ss, NULL);
|
||||
if (IS_ERR(ret))
|
||||
return -ERRNO(ret);
|
||||
}
|
||||
|
||||
if (tcb->callback)
|
||||
return (*tcb->callback) (tcb->param);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* _DkThreadCreate for internal use. Create an internal thread
|
||||
inside the current process. The arguments callback and param
|
||||
@@ -52,35 +86,51 @@
|
||||
int _DkThreadCreate (PAL_HANDLE * handle, int (*callback) (void *),
|
||||
const void * param, int flags)
|
||||
{
|
||||
void * child_stack = NULL;
|
||||
void * stack = NULL;
|
||||
int ret = _DkVirtualMemoryAlloc(&stack, THREAD_STACK_SIZE + ALT_STACK_SIZE,
|
||||
0, PAL_PROT_READ|PAL_PROT_WRITE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (_DkVirtualMemoryAlloc(&child_stack, THREAD_STACK_SIZE, 0,
|
||||
PAL_PROT_READ|PAL_PROT_WRITE) < 0)
|
||||
return -PAL_ERROR_NOMEM;
|
||||
void * child_stack = stack + THREAD_STACK_SIZE;
|
||||
|
||||
/* move child_stack to the top of stack. */
|
||||
child_stack += THREAD_STACK_SIZE;
|
||||
PAL_HANDLE hdl = malloc(HANDLE_SIZE(thread));
|
||||
if (!hdl) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
SET_HANDLE_TYPE(hdl, thread);
|
||||
|
||||
// Initialize TCB at the top of the alternative stack.
|
||||
PAL_TCB * tcb = child_stack + ALT_STACK_SIZE - sizeof(PAL_TCB);
|
||||
tcb->self = tcb;
|
||||
tcb->handle = hdl;
|
||||
tcb->alt_stack = child_stack; // Stack bottom
|
||||
tcb->callback = callback;
|
||||
tcb->param = (void *) param;
|
||||
|
||||
/* align child_stack to 16 */
|
||||
child_stack = (void *) ((uintptr_t) child_stack & ~16);
|
||||
|
||||
int tid = 0;
|
||||
int ret = clone(callback, child_stack,
|
||||
ret = clone(pal_thread_init, child_stack,
|
||||
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SYSVSEM|
|
||||
CLONE_THREAD|CLONE_SIGHAND|CLONE_PTRACE|
|
||||
CLONE_PARENT_SETTID,
|
||||
param, &tid, NULL);
|
||||
if (IS_ERR(ret))
|
||||
return -PAL_ERROR_DENIED;
|
||||
(void *) tcb, &hdl->thread.tid, NULL);
|
||||
|
||||
if (IS_ERR(ret)) {
|
||||
ret = -PAL_ERROR_DENIED;
|
||||
goto err;
|
||||
}
|
||||
|
||||
PAL_HANDLE hdl = malloc(HANDLE_SIZE(thread));
|
||||
SET_HANDLE_TYPE(hdl, thread);
|
||||
hdl->thread.tid = tid;
|
||||
*handle = hdl;
|
||||
|
||||
/* _DkThreadAdd(tid); */
|
||||
|
||||
return 0;
|
||||
err:
|
||||
if (stack)
|
||||
_DkVirtualMemoryFree(stack, THREAD_STACK_SIZE + ALT_STACK_SIZE);
|
||||
if (hdl)
|
||||
free(hdl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _DkThreadDelayExecution (unsigned long * duration)
|
||||
|
||||
@@ -53,9 +53,6 @@ typedef struct mutex_handle {
|
||||
#define LOCK_INIT MUTEX_HANDLE_INIT
|
||||
#define INIT_LOCK(lock) INIT_MUTEX_HANDLE(lock);
|
||||
|
||||
#define _DkInternalLock _DkMutexLock
|
||||
#define _DkInternalUnlock _DkMutexUnlock
|
||||
|
||||
/* Locking and unlocking of Mutexes */
|
||||
int _DkMutexLock (struct mutex_handle * mut);
|
||||
int _DkMutexLockTimeout (struct mutex_handle * mut, uint64_t timeout);
|
||||
@@ -183,115 +180,12 @@ typedef struct pal_handle
|
||||
|
||||
#define HANDLE_TYPE(handle) ((handle)->hdr.type)
|
||||
|
||||
struct arch_frame {
|
||||
#ifdef __x86_64__
|
||||
uint64_t rsp, rbp, rbx, rsi, rdi, r12, r13, r14, r15;
|
||||
#else
|
||||
# error "unsupported architecture"
|
||||
#endif
|
||||
};
|
||||
extern void __check_pending_event (void);
|
||||
|
||||
#ifdef __x86_64__
|
||||
# define store_register(reg, var) \
|
||||
asm volatile ("movq %%" #reg ", %0" : "=a" (var) :: "memory");
|
||||
#define LEAVE_PAL_CALL() do { __check_pending_event(); } while (0)
|
||||
|
||||
# define store_register_in_frame(reg, f) store_register(reg, (f)->reg)
|
||||
|
||||
# define arch_store_frame(f) \
|
||||
store_register_in_frame(rsp, f) \
|
||||
store_register_in_frame(rbp, f) \
|
||||
store_register_in_frame(rbx, f) \
|
||||
store_register_in_frame(rsi, f) \
|
||||
store_register_in_frame(rdi, f) \
|
||||
store_register_in_frame(r12, f) \
|
||||
store_register_in_frame(r13, f) \
|
||||
store_register_in_frame(r14, f) \
|
||||
store_register_in_frame(r15, f)
|
||||
|
||||
# define restore_register(reg, var, clobber...) \
|
||||
asm volatile ("movq %0, %%" #reg :: "g" (var) : "memory", ##clobber);
|
||||
|
||||
# define restore_register_in_frame(reg, f) \
|
||||
restore_register(reg, (f)->reg, \
|
||||
"r15", "r14", "r13", "r12", "rdi", "rsi", "rbx")
|
||||
|
||||
# define arch_restore_frame(f) \
|
||||
restore_register_in_frame(r15, f) \
|
||||
restore_register_in_frame(r14, f) \
|
||||
restore_register_in_frame(r13, f) \
|
||||
restore_register_in_frame(r12, f) \
|
||||
restore_register_in_frame(rdi, f) \
|
||||
restore_register_in_frame(rsi, f) \
|
||||
restore_register_in_frame(rbx, f) \
|
||||
restore_register_in_frame(rbp, f) \
|
||||
restore_register_in_frame(rsp, f)
|
||||
#else /* __x86_64__ */
|
||||
# error "unsupported architecture"
|
||||
#endif
|
||||
|
||||
#define PAL_FRAME_IDENTIFIER (0xdeaddeadbeefbeef)
|
||||
|
||||
struct pal_frame {
|
||||
volatile uint64_t identifier;
|
||||
void * func;
|
||||
const char * funcname;
|
||||
struct arch_frame arch;
|
||||
};
|
||||
|
||||
/* When a PAL call is issued, a special PAL_FRAME is placed on the stack.
|
||||
* This stores both a magic identifier, debugging information,
|
||||
* as well as callee-saved state. This is used as a way to deal
|
||||
* with PAL-internal failures where the goal is to exit the PAL and return a
|
||||
* failure.
|
||||
*
|
||||
* Arguably, an alternative is to unwind the stack and handle error cases at
|
||||
* each stage. In general, this is probably more robust, but would take work
|
||||
* in the short term. The one exception where the current strategy is
|
||||
* probably better is when the PAL gets in a state where the code is
|
||||
* unrecoverable, but ideally, this shouldn't happen.
|
||||
*/
|
||||
|
||||
/* DEP 12/25/17: This frame storage thing is important to mark volatile.
|
||||
* The compiler should not optimize out any of these changes, and
|
||||
* because some accesses can happen during an exception, these are not
|
||||
* visible to the compiler in an otherwise stack-local variable (so the
|
||||
* compiler will try to optimize out these assignments.
|
||||
*/
|
||||
static inline
|
||||
void __store_frame (volatile struct pal_frame * frame,
|
||||
void * func, const char * funcname)
|
||||
{
|
||||
arch_store_frame(&frame->arch)
|
||||
frame->func = func;
|
||||
frame->funcname = funcname;
|
||||
asm volatile ("nop" ::: "memory");
|
||||
frame->identifier = PAL_FRAME_IDENTIFIER;
|
||||
}
|
||||
|
||||
#define ENTER_PAL_CALL(name) \
|
||||
struct pal_frame frame; \
|
||||
__store_frame(&frame, &(name), #name)
|
||||
|
||||
|
||||
static inline
|
||||
void __clear_frame (volatile struct pal_frame * frame)
|
||||
{
|
||||
if (frame->identifier == PAL_FRAME_IDENTIFIER) {
|
||||
asm volatile ("nop" ::: "memory");
|
||||
frame->identifier = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define LEAVE_PAL_CALL() \
|
||||
do { \
|
||||
__clear_frame(&frame); \
|
||||
} while (0)
|
||||
|
||||
#define LEAVE_PAL_CALL_RETURN(retval) \
|
||||
do { \
|
||||
__clear_frame(&frame); \
|
||||
return (retval); \
|
||||
} while (0)
|
||||
#define LEAVE_PAL_CALL_RETURN(retval) \
|
||||
do { __check_pending_event(); return (retval); } while (0)
|
||||
|
||||
#if TRACE_HEAP_LEAK == 1
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "pal.h"
|
||||
#include "pal_internal.h"
|
||||
#include "pal_linux_error.h"
|
||||
#include "list.h"
|
||||
|
||||
#define PAL_LOADER RUNTIME_FILE("pal-Linux")
|
||||
|
||||
@@ -172,4 +173,35 @@ extern char __text_start, __text_end, __data_start, __data_end;
|
||||
#define DATA_START (void *) (&__text_start)
|
||||
#define DATA_END (void *) (&__text_end)
|
||||
|
||||
#define ADDR_IN_PAL(addr) \
|
||||
((void *) (addr) > TEXT_START && (void *) (addr) < TEXT_END)
|
||||
|
||||
DEFINE_LIST(event_queue);
|
||||
struct event_queue {
|
||||
LIST_TYPE(event_queue) list;
|
||||
int event_num;
|
||||
};
|
||||
|
||||
DEFINE_LISTP(event_queue);
|
||||
typedef struct pal_tcb {
|
||||
struct pal_tcb * self;
|
||||
int pending_event;
|
||||
LISTP_TYPE(event_queue) pending_queue;
|
||||
PAL_HANDLE handle;
|
||||
void * alt_stack;
|
||||
int (*callback) (void *);
|
||||
void * param;
|
||||
} PAL_TCB;
|
||||
|
||||
int pal_thread_init (void * tcbptr);
|
||||
|
||||
static inline PAL_TCB * get_tcb (void)
|
||||
{
|
||||
PAL_TCB * tcb;
|
||||
asm ("movq %%gs:%c1,%q0"
|
||||
: "=r" (tcb)
|
||||
: "i" (offsetof(PAL_TCB, self)));
|
||||
return tcb;
|
||||
}
|
||||
|
||||
#endif /* PAL_LINUX_H */
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#define USER_ADDRESS_RESERVED 0x100000000
|
||||
#define USER_ADDRESS_LOWEST 0x10000
|
||||
|
||||
#define THREAD_STACK_SIZE (PRESET_PAGESIZE * 2)
|
||||
#define ALT_STACK_SIZE (PRESET_PAGESIZE)
|
||||
|
||||
/* internal wrap native pipe inside pipe streams */
|
||||
#define USE_PIPE_SYSCALL 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user