/* Copyright (C) 2014 Stony Brook University
This file is part of Graphene Library OS.
Graphene Library OS is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Graphene Library OS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see . */
/*
* db_signal.c
*
* This file contains APIs to set up handlers of exceptions issued by the
* host, and the methods to pass the exceptions to the upcalls.
*/
#include "pal_defs.h"
#include "pal_linux_defs.h"
#include "pal.h"
#include "pal_internal.h"
#include "pal_linux.h"
#include "pal_error.h"
#include "pal_security.h"
#include "api.h"
#include "ecall_types.h"
#include
#include
#include
#include
static bool
_DkGenericSignalHandle(int event_num, PAL_NUM arg, PAL_CONTEXT* context) {
PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event_num);
if (upcall) {
(*upcall)(NULL, arg, context);
return true;
}
return false;
}
#define ADDR_IN_PAL(addr) \
((void*)(addr) > TEXT_START && (void*)(addr) < TEXT_END)
/*
* Restore an sgx_cpu_context_t as generated by .Lhandle_exception. Execution will
* continue as specified by the rip in the context.
*/
noreturn static void restore_sgx_context(sgx_cpu_context_t* uc,
PAL_XREGS_STATE* xregs_state) {
if (xregs_state == NULL)
xregs_state = (PAL_XREGS_STATE*)xsave_reset_state;
_restore_sgx_context(uc, xregs_state);
}
noreturn static void restore_pal_context(sgx_cpu_context_t* uc, PAL_CONTEXT* ctx) {
uc->rax = ctx->rax;
uc->rbx = ctx->rbx;
uc->rcx = ctx->rcx;
uc->rdx = ctx->rdx;
uc->rsp = ctx->rsp;
uc->rbp = ctx->rbp;
uc->rsi = ctx->rsi;
uc->rdi = ctx->rdi;
uc->r8 = ctx->r8;
uc->r9 = ctx->r9;
uc->r10 = ctx->r10;
uc->r11 = ctx->r11;
uc->r12 = ctx->r12;
uc->r13 = ctx->r13;
uc->r14 = ctx->r14;
uc->r15 = ctx->r15;
uc->rflags = ctx->efl;
uc->rip = ctx->rip;
restore_sgx_context(uc, ctx->fpregs);
}
static void save_pal_context(PAL_CONTEXT* ctx, sgx_cpu_context_t* uc,
PAL_XREGS_STATE* xregs_state) {
memset(ctx, 0, sizeof(*ctx));
ctx->rax = uc->rax;
ctx->rbx = uc->rbx;
ctx->rcx = uc->rcx;
ctx->rdx = uc->rdx;
ctx->rsp = uc->rsp;
ctx->rbp = uc->rbp;
ctx->rsi = uc->rsi;
ctx->rdi = uc->rdi;
ctx->r8 = uc->r8;
ctx->r9 = uc->r9;
ctx->r10 = uc->r10;
ctx->r11 = uc->r11;
ctx->r12 = uc->r12;
ctx->r13 = uc->r13;
ctx->r14 = uc->r14;
ctx->r15 = uc->r15;
ctx->efl = uc->rflags;
ctx->rip = uc->rip;
union pal_csgsfs csgsfs = {
.cs = 0x33, // __USER_CS(5) | 0(GDT) | 3(RPL)
.fs = 0,
.gs = 0,
.ss = 0x2b, // __USER_DS(6) | 0(GDT) | 3(RPL)
};
ctx->csgsfs = csgsfs.csgsfs;
assert(xregs_state);
ctx->fpregs = xregs_state;
/* Emulate format for fp registers Linux sets up as signal frame.
* https://elixir.bootlin.com/linux/v5.4.13/source/arch/x86/kernel/fpu/signal.c#L86
* https://elixir.bootlin.com/linux/v5.4.13/source/arch/x86/kernel/fpu/signal.c#L459
*/
PAL_FPX_SW_BYTES* fpx_sw = &xregs_state->fpstate.sw_reserved;
fpx_sw->magic1 = PAL_FP_XSTATE_MAGIC1;
fpx_sw->extended_size = xsave_size;
fpx_sw->xfeatures = xsave_features;
memset(fpx_sw->padding, 0, sizeof(fpx_sw->padding));
if (xsave_enabled) {
fpx_sw->xstate_size = xsave_size + PAL_FP_XSTATE_MAGIC2_SIZE;
*(__typeof__(PAL_FP_XSTATE_MAGIC2)*)((void*)xregs_state + xsave_size) =
PAL_FP_XSTATE_MAGIC2;
} else {
fpx_sw->xstate_size = xsave_size;
}
}
/*
* return value:
* true: #UD is handled.
* the execution can be continued without propagating #UD.
* false: #UD is not handled.
* the exception needs to be raised up to LibOS or user application.
*/
static bool handle_ud(sgx_cpu_context_t * uc)
{
uint8_t * instr = (uint8_t *) uc->rip;
if (instr[0] == 0xcc) { /* skip int 3 */
uc->rip++;
return true;
} else if (instr[0] == 0x0f && instr[1] == 0xa2) {
/* cpuid */
unsigned int values[4];
if (!_DkCpuIdRetrieve(uc->rax & 0xffffffff,
uc->rcx & 0xffffffff, values)) {
uc->rip += 2;
uc->rax = values[0];
uc->rbx = values[1];
uc->rcx = values[2];
uc->rdx = values[3];
return true;
}
} else if (instr[0] == 0x0f && instr[1] == 0x31) {
/* rdtsc */
uc->rip += 2;
uc->rdx = 0;
uc->rax = 0;
return true;
} else if (instr[0] == 0x0f && instr[1] == 0x05) {
/* syscall: LibOS may know how to handle this */
return false;
}
SGX_DBG(DBG_E, "Unknown or illegal instruction at RIP 0x%016lx\n", uc->rip);
return false;
}
void _DkExceptionHandler(
unsigned int exit_info, sgx_cpu_context_t* uc, PAL_XREGS_STATE* xregs_state) {
assert(IS_ALIGNED_PTR(xregs_state, PAL_XSTATE_ALIGN));
union {
sgx_arch_exit_info_t info;
unsigned int intval;
} ei = { .intval = exit_info };
int event_num;
if (!ei.info.valid) {
event_num = exit_info;
} else {
switch (ei.info.vector) {
case SGX_EXCEPTION_VECTOR_BR:
event_num = PAL_EVENT_NUM_BOUND;
break;
case SGX_EXCEPTION_VECTOR_UD:
if (handle_ud(uc)) {
restore_sgx_context(uc, xregs_state);
/* NOTREACHED */
}
event_num = PAL_EVENT_ILLEGAL;
break;
case SGX_EXCEPTION_VECTOR_DE:
case SGX_EXCEPTION_VECTOR_MF:
case SGX_EXCEPTION_VECTOR_XM:
event_num = PAL_EVENT_ARITHMETIC_ERROR;
break;
case SGX_EXCEPTION_VECTOR_AC:
event_num = PAL_EVENT_MEMFAULT;
break;
case SGX_EXCEPTION_VECTOR_DB:
case SGX_EXCEPTION_VECTOR_BP:
default:
restore_sgx_context(uc, xregs_state);
/* NOTREACHED */
}
}
if (ADDR_IN_PAL(uc->rip) &&
/* event isn't asynchronous */
(event_num != PAL_EVENT_QUIT &&
event_num != PAL_EVENT_SUSPEND &&
event_num != PAL_EVENT_RESUME)) {
printf("*** An unexpected AEX vector occurred inside PAL. "
"Exiting the thread. *** \n"
"(vector = 0x%x, type = 0x%x valid = %d, RIP = +0x%08lx)\n"
"rax: 0x%08lx rcx: 0x%08lx rdx: 0x%08lx rbx: 0x%08lx\n"
"rsp: 0x%08lx rbp: 0x%08lx rsi: 0x%08lx rdi: 0x%08lx\n"
"r8 : 0x%08lx r9 : 0x%08lx r10: 0x%08lx r11: 0x%08lx\n"
"r12: 0x%08lx r13: 0x%08lx r14: 0x%08lx r15: 0x%08lx\n"
"rflags: 0x%08lx rip: 0x%08lx\n",
ei.info.vector, ei.info.exit_type, ei.info.valid,
uc->rip - (uintptr_t) TEXT_START,
uc->rax, uc->rcx, uc->rdx, uc->rbx,
uc->rsp, uc->rbp, uc->rsi, uc->rdi,
uc->r8, uc->r9, uc->r10, uc->r11,
uc->r12, uc->r13, uc->r14, uc->r15,
uc->rflags, uc->rip);
#ifdef DEBUG
printf("pausing for debug\n");
while (true)
__asm__ volatile("pause");
#endif
_DkThreadExit(/*clear_child_tid=*/NULL);
}
PAL_CONTEXT ctx;
save_pal_context(&ctx, uc, xregs_state);
/* TODO: save EXINFO from MISC region and populate below fields */
ctx.err = 0;
ctx.trapno = ei.info.valid ? ei.info.vector : event_num;
ctx.oldmask = 0;
ctx.cr2 = 0;
PAL_NUM arg = 0;
switch (event_num) {
case PAL_EVENT_ILLEGAL:
arg = uc->rip;
break;
case PAL_EVENT_MEMFAULT:
/* TODO
* SGX1 doesn't provide fault address.
* SGX2 gives providing page. (lower address bits are masked)
*/
break;
default:
/* nothing */
break;
}
_DkGenericSignalHandle(event_num, arg, &ctx);
restore_pal_context(uc, &ctx);
}
void _DkRaiseFailure(int error) {
_DkGenericSignalHandle(PAL_EVENT_FAILURE, error, /*context=*/NULL);
}
void _DkExceptionReturn(void* event) {
__UNUSED(event);
}
noreturn void _DkHandleExternalEvent(
PAL_NUM event, sgx_cpu_context_t* uc, PAL_XREGS_STATE* xregs_state) {
assert(event);
assert(IS_ALIGNED_PTR(xregs_state, PAL_XSTATE_ALIGN));
/* We only end up in _DkHandleExternalEvent() if interrupted during
* host syscall; Inform LibOS layer that PAL was interrupted (by setting PAL_ERRNO). */
_DkRaiseFailure(PAL_ERROR_INTERRUPTED);
PAL_CONTEXT ctx;
save_pal_context(&ctx, uc, xregs_state);
ctx.err = 0;
/* TODO: event is a PAL event; is that what LibOS/app wants to see? */
ctx.trapno = event;
ctx.oldmask = 0;
ctx.cr2 = 0;
if (!_DkGenericSignalHandle(event, 0, &ctx) && event != PAL_EVENT_RESUME) {
_DkThreadExit(/*clear_child_tid=*/NULL);
}
/*
* The modification to PAL_CONTEXT is discarded.
* It is assumed that LibOS won't change context (GPRs, fp registers)
* if RIP is in PAL.
*
* TODO: in long term, record the signal and trigger the signal handler
* when returning from PAL by the use of
* ENTER_PAL_CALL/LEAVE_PAL_CALL/LEAVE_PAL_CALL_RETURN.
*/
restore_sgx_context(uc, xregs_state);
}