Fix the emulation of RDTSC instruction with ocall

This commit is contained in:
LI Qing 2020-03-13 18:14:04 +08:00 committed by tate.thl
parent e2edaa49c0
commit f7ce60e764
6 changed files with 85 additions and 31 deletions

@ -54,6 +54,7 @@ enclave {
void occlum_ocall_gettimeofday([out] struct timeval* tv); void occlum_ocall_gettimeofday([out] struct timeval* tv);
void occlum_ocall_clock_gettime(clockid_t clockid, [out] struct timespec* ts); void occlum_ocall_clock_gettime(clockid_t clockid, [out] struct timespec* ts);
void occlum_ocall_rdtsc([out] uint32_t* low, [out] uint32_t* high);
void occlum_ocall_nanosleep([in] const struct timespec* req); void occlum_ocall_nanosleep([in] const struct timespec* req);

@ -1,9 +1,15 @@
use super::*; use super::*;
use process::Task;
use sgx_types::*; use sgx_types::*;
const RDTSC_OPCODE: u16 = 0x310F; const RDTSC_OPCODE: u16 = 0x310F;
static mut FAKE_RDTSC_VALUE: u64 = 0;
static FAKE_RDTSC_INC_VALUE: u64 = 1000; extern "C" {
fn occlum_ocall_rdtsc(low: *mut u32, high: *mut u32) -> sgx_status_t;
fn __get_current_task() -> *const Task;
fn switch_td_to_kernel(task: *const Task);
fn switch_td_to_user(task: *const Task);
}
#[no_mangle] #[no_mangle]
pub extern "C" fn handle_rdtsc_exception(info: *mut sgx_exception_info_t) -> u32 { pub extern "C" fn handle_rdtsc_exception(info: *mut sgx_exception_info_t) -> u32 {
@ -15,11 +21,19 @@ pub extern "C" fn handle_rdtsc_exception(info: *mut sgx_exception_info_t) -> u32
{ {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
// rdtsc support here is temporary, only for SKL, later CPU's will support this inside enclave
unsafe { unsafe {
FAKE_RDTSC_VALUE += FAKE_RDTSC_INC_VALUE; let task = __get_current_task();
info.cpu_context.rax = (FAKE_RDTSC_VALUE & 0xFFFFFFFF); switch_td_to_kernel(task);
info.cpu_context.rdx = (FAKE_RDTSC_VALUE >> 32); let (low, high) = {
let mut low = 0;
let mut high = 0;
let sgx_status = occlum_ocall_rdtsc(&mut low, &mut high);
assert!(sgx_status == sgx_status_t::SGX_SUCCESS);
(low, high)
};
info.cpu_context.rax = low as u64;
info.cpu_context.rdx = high as u64;
switch_td_to_user(task);
} }
info.cpu_context.rip += 2; info.cpu_context.rip += 2;

@ -7,7 +7,7 @@ pub use self::process::{Status, IDLE_PROCESS};
pub use self::process_table::get; pub use self::process_table::get;
pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, do_sched_yield, CpuSet}; pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, do_sched_yield, CpuSet};
pub use self::spawn::{do_spawn, do_spawn_without_exec, ElfFile, FileAction, ProgramHeaderExt}; pub use self::spawn::{do_spawn, do_spawn_without_exec, ElfFile, FileAction, ProgramHeaderExt};
pub use self::task::{current_pid, get_current, run_task}; pub use self::task::{current_pid, get_current, run_task, Task};
pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup}; pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup};
pub use self::wait::{WaitQueue, Waiter}; pub use self::wait::{WaitQueue, Waiter};
@ -82,7 +82,6 @@ mod task;
mod thread; mod thread;
mod wait; mod wait;
use self::task::Task;
use super::*; use super::*;
use fs::{File, FileRef, FileTable}; use fs::{File, FileRef, FileTable};
use misc::ResourceLimitsRef; use misc::ResourceLimitsRef;

@ -37,6 +37,28 @@ static uint64_t get_syscall_stack(struct Task* this_task) {
#define RESET_CURRENT_TASK() \ #define RESET_CURRENT_TASK() \
__set_stack_guard(stack_guard); __set_stack_guard(stack_guard);
void switch_td_to_kernel(const struct Task* task) {
thread_data_t* td = get_thread_data();
// TODO: do do not support stack expansion, need a new design on SGX2 platform.
// Set the stack_commit_addr to 0, as the result no stack expansion happens at any situations
__atomic_store_n(&td->stack_commit_addr, 0, __ATOMIC_RELAXED);
td->stack_base_addr = task->kernel_stack_base;
td->stack_limit_addr = task->kernel_stack_limit;
td->stack_commit_addr = task->kernel_stack_limit;
}
void switch_td_to_user(const struct Task* task) {
thread_data_t* td = get_thread_data();
// TODO: do do not support stack expansion, need a new design on SGX2 platform.
// Set the stack_commit_addr to 0, as the result no stack expansion happens at any situations
__atomic_store_n(&td->stack_commit_addr, 0, __ATOMIC_RELAXED);
td->stack_base_addr = task->user_stack_base;
td->stack_limit_addr = task->user_stack_limit;
td->stack_commit_addr = task->user_stack_limit;
}
int do_run_task(struct Task* task) { int do_run_task(struct Task* task) {
jmp_buf libos_state = {0}; jmp_buf libos_state = {0};
thread_data_t* td = get_thread_data(); thread_data_t* td = get_thread_data();
@ -45,12 +67,7 @@ int do_run_task(struct Task* task) {
task->kernel_stack_base = td->stack_base_addr; task->kernel_stack_base = td->stack_base_addr;
task->kernel_stack_limit = td->stack_limit_addr; task->kernel_stack_limit = td->stack_limit_addr;
//Do do not support stack expansion, need a new design on SGX2 platform. switch_td_to_user(task);
//Set the stack_commit_addr to 0, as the result no stack expansion happens at any situations
__atomic_store_n(&td->stack_commit_addr, 0, __ATOMIC_RELAXED);
td->stack_base_addr = task->user_stack_base;
td->stack_limit_addr = task->user_stack_limit;
td->stack_commit_addr = task->user_stack_limit;
SET_CURRENT_TASK(task); SET_CURRENT_TASK(task);
@ -67,13 +84,7 @@ int do_run_task(struct Task* task) {
void do_exit_task(void) { void do_exit_task(void) {
struct Task* task = __get_current_task(); struct Task* task = __get_current_task();
jmp_buf* jb = task->saved_state; jmp_buf* jb = task->saved_state;
thread_data_t* td = get_thread_data();
//Do do not support stack expansion, need a new design on SGX2 platform. switch_td_to_kernel(task);
//Set the stack_commit_addr to 0, as the result no stack expansion happens at any situations
__atomic_store_n(&td->stack_commit_addr, 0, __ATOMIC_RELAXED);
td->stack_base_addr = task->kernel_stack_base;
td->stack_limit_addr = task->kernel_stack_limit;
td->stack_commit_addr = task->kernel_stack_limit;
longjmp(*jb, 1); longjmp(*jb, 1);
} }

@ -24,3 +24,10 @@ int occlum_ocall_thread_getcpuclock(struct timespec *tp) {
return clock_gettime(thread_clock_id, tp); return clock_gettime(thread_clock_id, tp);
} }
void occlum_ocall_rdtsc(uint32_t* low, uint32_t* high) {
uint64_t rax, rdx;
asm volatile("rdtsc" : "=a"(rax), "=d"(rdx));
*low = (uint32_t)rax;
*high = (uint32_t)rdx;
}

@ -1,19 +1,41 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include "test.h"
// ============================================================================
// Helper functions for rdtsc
// ============================================================================
static inline uint64_t native_rdtsc() { static inline uint64_t native_rdtsc() {
uint32_t hi, lo; uint64_t low, high;
asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); asm volatile("rdtsc" : "=a"(low), "=d"(high));
return (( (uint64_t)lo)|( ((uint64_t)hi)<<32 )); return (high << 32) | low;
} }
int main(int argc, char **argv) // ============================================================================
{ // Test cases for rdtsc
/* Gets rdtsc information and tests the SGX support of the rdtsc */ // ============================================================================
uint64_t r;
r = native_rdtsc();
printf("rdtsc: %lu\n", r);
int test_rdtsc() {
uint64_t start_count = native_rdtsc();
if (start_count == 0) {
THROW_ERROR("call rdtsc failed");
}
uint64_t end_count = native_rdtsc();
if (end_count <= start_count) {
THROW_ERROR("check rdtsc return value failed");
}
return 0; return 0;
} }
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_rdtsc),
};
int main() {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}