Skip to content

Commit

Permalink
[SM] Multicore Security Monitor (#4)
Browse files Browse the repository at this point in the history
The security monitor supports SMP up to 8 cores

* SMM is protected over all the cores
* Enclave Creation/Destruction: PMP registers are globally synchronized using an IPI
* Enclave Run/Exit: only the local PMP register is set or unset
* Removed unused SBI functions (`copy_to_enclave` and `copy_from_enclave`)
* [BUG] Sanctum bootloader does not work with SMP yet; must debug later
  • Loading branch information
dayeol authored Oct 8, 2018
1 parent cef3f03 commit 8a33d4b
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 260 deletions.
257 changes: 158 additions & 99 deletions enclave.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,67 @@
#include "pmp.h"
#include "page.h"
#include <string.h>
#include "atomic.h"

#define ENCL_MAX 16
#define RET_ON_ERR(ret) {if(ret<0) return ret;}

static uint64_t encl_bitmap = 0;
static int running_encl_id = -1;

extern void save_host_regs(void);
struct enclave_t enclaves[ENCL_MAX];

#define RET_ON_ERR(ret) {if(ret<0) return ret;}
static spinlock_t encl_lock = SPINLOCK_INIT;

struct enclave_t enclaves[ENCL_MAX];
/* FIXME: this takes O(n), change it to use a hash table */
int encl_satp_to_eid(reg satp)
{
int i;
for(i=0; i<ENCL_MAX; i++)
{
if(enclaves[i].encl_satp == satp)
return i;
}
return -1;
}
/* FIXME: this takes O(n), change it to use a hash table */
int host_satp_to_eid(reg satp)
{
int i;
for(i=0; i<ENCL_MAX; i++)
{
if(enclaves[i].host_satp == satp)
return i;
}
return -1;
}

int encl_alloc_idx()
{
int i;

spinlock_lock(&encl_lock);

for(i=0; i<ENCL_MAX; i++)
{
if(!(encl_bitmap & (0x1 << i)))
break;
}
if(i != ENCL_MAX)
SET_BIT(encl_bitmap, i);

if(i == ENCL_MAX)
return -1;
spinlock_unlock(&encl_lock);

SET_BIT(encl_bitmap, i);
return i;
if(i != ENCL_MAX)
return i;
else
return -1;
}

int encl_free_idx(int idx)
{
if(!TEST_BIT(encl_bitmap, idx))
return -1;

spinlock_lock(&encl_lock);
UNSET_BIT(encl_bitmap, idx);
spinlock_unlock(&encl_lock);
return 0;
}

Expand All @@ -60,7 +88,7 @@ int detect_region_overlap(int eid, uintptr_t addr, uintptr_t size)
((uintptr_t) epm_base + epm_size > addr);
}

int init_enclave(uintptr_t base, uintptr_t size)
int init_enclave_memory(uintptr_t base, uintptr_t size)
{
int ret;
int ptlevel = (VA_BITS - RISCV_PGSHIFT) / RISCV_PGLEVEL_BITS;
Expand All @@ -79,157 +107,188 @@ int init_enclave(uintptr_t base, uintptr_t size)
int create_enclave(uintptr_t base, uintptr_t size)
{
uint8_t perm = 0;
int eid;
int ret, region;
int i;
// - TODO: if size larger than minimum requirement (16 KB)
int region_overlap = 0;

// 1. create a PMP region binded to the enclave
ret = pmp_region_init_atomic(base, size, perm, PMP_PRI_ANY);
if(ret < 0)
goto error;
region = ret;

// - if base and (base+size) not belong to other enclaves
spinlock_lock(&encl_lock);
for(i=0; i<ENCL_MAX; i++)
{
if(!TEST_BIT(encl_bitmap, i))
continue;
if(detect_region_overlap(i, base, size))
{
printm("region overlaps with enclave %d\n", i);
return -EINVAL;
}
}
region_overlap |= detect_region_overlap(i, base, size);
if(region_overlap)
break;
}
spinlock_unlock(&encl_lock);

// - if size is multiple of 4KB (smaller than 4KB not supported)
if(size & (RISCV_PGSIZE-1))
return -EINVAL;
if(region_overlap)
{
printm("region overlaps with enclave %d\n", i);
ret = -EINVAL;
goto free_region;
}

// 1. create a PMP region binded to the enclave
ret = pmp_region_init(base, size, perm);
RET_ON_ERR(ret);
// 2. allocate eid
ret = encl_alloc_idx();
if(ret < 0)
goto free_region;
eid = ret;

region = ret;
// 2. set pmp
ret = pmp_set(region);
RET_ON_ERR(ret);

// 3. initialize and verify enclave memory layout.
init_enclave(base, size);
// 3. set pmp
ret = pmp_set_global(region);
//ret = pmp_set(region);
if(ret < 0)
goto free_encl_idx;

// 4. allocate eid
ret = encl_alloc_idx();
RET_ON_ERR(ret);

// 4. initialize and verify enclave memory layout.
init_enclave_memory(base, size);

// 5. initialize enclave metadata
enclaves[ret].eid = ret;
enclaves[ret].rid = region;
enclaves[ret].state = FRESH;
enclaves[ret].host_satp = read_csr(satp);
enclaves[ret].encl_satp = ((base >> RISCV_PGSHIFT) | SATP_MODE_CHOICE);
enclaves[eid].eid = eid;
enclaves[eid].rid = region;
enclaves[eid].host_satp = read_csr(satp);
enclaves[eid].encl_satp = ((base >> RISCV_PGSHIFT) | SATP_MODE_CHOICE);
enclaves[eid].n_thread = 0;

spinlock_lock(&encl_lock);
enclaves[eid].state = INITIALIZED;
spinlock_unlock(&encl_lock);

return 0;

free_encl_idx:
encl_free_idx(eid);
free_region:
pmp_region_free_atomic(region);
error:
return ret;
}

int destroy_enclave(int eid)
{
if(!TEST_BIT(encl_bitmap, eid))
int destroyable;

spinlock_lock(&encl_lock);
destroyable = TEST_BIT(encl_bitmap, eid) &&
(enclaves[eid].state >= 0) &&
enclaves[eid].state != RUNNING;
/* update the enclave state first so that
* no SM can run the enclave any longer */
if(destroyable)
enclaves[eid].state = DESTROYED;
spinlock_unlock(&encl_lock);

if(!destroyable)
return -1;

// 1. clear all the data in the enclave page
// requires no lock (single runner)
void* base = pmp_get_addr(enclaves[eid].rid);
uintptr_t size = pmp_get_size(enclaves[eid].rid);
memset((void*) base, 0, size);
//memset((void*) base, 0, size);

// 2. free pmp region
pmp_region_free(enclaves[eid].rid);
pmp_unset_global(enclaves[eid].rid);
pmp_region_free_atomic(enclaves[eid].rid);

enclaves[eid].eid = 0;
enclaves[eid].rid = 0;
enclaves[eid].host_satp = 0;
enclaves[eid].encl_satp = 0;
enclaves[eid].n_thread = 0;

// 3. release eid
encl_free_idx(eid);

return 0;
}

int copy_to_enclave(int eid, uintptr_t encl_addr, uintptr_t ptr, size_t size)
reg run_enclave(int eid, uintptr_t ptr)
{
if(!TEST_BIT(encl_bitmap, eid))
return -1;

struct enclave_t encl = enclaves[eid];

/* TODO: NOT IMPLEMENTED */

return 0;
}
int runable;
int hart_id;

spinlock_lock(&encl_lock);
runable = TEST_BIT(encl_bitmap, eid) && (enclaves[eid].state >= 0);
if(runable) {
enclaves[eid].state = RUNNING;
enclaves[eid].n_thread++;
}
spinlock_unlock(&encl_lock);

int copy_from_enclave(int eid, void* ptr, size_t size)
{
if(!TEST_BIT(encl_bitmap, eid))
if(!runable)
return -1;

struct enclave_t encl = enclaves[eid];
void* epm = pmp_get_addr(encl.rid);

/* TODO: NOT IMPLEMENTED */
hart_id = read_csr(mhartid);

return 0;
}

reg run_enclave(int eid, uintptr_t ptr)
{
if(!TEST_BIT(encl_bitmap, eid))
return -1;
struct enclave_t encl = enclaves[eid];

// save host return pc
enclaves[eid].host_mepc = read_csr(mepc);

// save host interrupt handler
enclaves[eid].host_stvec = read_csr(stvec);
/* save host context */
enclaves[eid].host_mepc[hart_id] = read_csr(mepc);
enclaves[eid].host_stvec[hart_id] = read_csr(stvec);

// entry point after return (mret)
write_csr(mepc, 0xffffffffc0000000 ); // address of trampoline (runtime)

// switch to enclave page table
write_csr(satp, encl.encl_satp);
write_csr(satp, enclaves[eid].encl_satp);

// disable timer interrupt
clear_csr(mie, MIP_MTIP);

// unset PMP
pmp_unset(encl.rid);

// FIXME: this works for now
// because only one enclave will run on a SM.
// We should make SM identify the enclave by using { encl_satp -> eid } map
running_encl_id = eid;

/*
asm volatile(
"mv tp, %0\n"
"jal save_host_regs\n"
: : "rK"(&encl.host_ctx));
*/
pmp_unset(enclaves[eid].rid);

// return enclave entry point (this is the first argument to the runtime)
return (reg)ptr;
}

uint64_t exit_enclave(uint64_t retval)
{
if(running_encl_id < 0)
int eid = encl_satp_to_eid(read_csr(satp));
int exitable;
int hart_id = read_csr(mhartid);

if(eid < 0)
return -1;

spinlock_lock(&encl_lock);
exitable = enclaves[eid].state == RUNNING;
spinlock_unlock(&encl_lock);

if(!exitable)
return -1;

// get the running enclave on this SM
struct enclave_t encl = enclaves[running_encl_id];
struct enclave_t encl = enclaves[eid];

// set PMP
pmp_set(encl.rid);
running_encl_id = -1;

// restore interrupt handler
write_csr(stvec, encl.host_stvec);
/* restore host context */
write_csr(stvec, encl.host_stvec[hart_id]);
write_csr(mepc, encl.host_mepc[hart_id]);

// switch to host page table
write_csr(satp, encl.host_satp);

// enable timer interrupt
set_csr(mie, MIP_MTIP);

// restore host return pc
write_csr(mepc, encl.host_mepc);

// switch to host page table
write_csr(satp, encl.host_satp);
// update enclave state
spinlock_lock(&encl_lock);
enclaves[eid].n_thread--;
if(enclaves[eid].n_thread == 0)
enclaves[eid].state = INITIALIZED;
spinlock_unlock(&encl_lock);

return retval;
}
Expand Down
17 changes: 9 additions & 8 deletions enclave.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pmp.h"

typedef enum {
DESTROYED = -2,
INVALID = -1,
FRESH = 0,
INITIALIZED,
Expand Down Expand Up @@ -57,19 +58,19 @@ struct enclave_t
int eid; //enclave id
int rid; //region id
unsigned long host_satp; //supervisor satp
unsigned long encl_satp;
enclave_state_t state;
unsigned long host_mepc; //supervisor return pc
unsigned long host_stvec; //supervisor stvec
/* context */
struct ctx_t host_ctx;
unsigned long encl_satp; // enclave's page table base
enclave_state_t state; // global state of the enclave
unsigned int n_thread;

/* execution context */
unsigned long host_mepc[MAX_HARTS]; //supervisor return pc
unsigned long host_stvec[MAX_HARTS]; //supervisor stvec
//struct ctx_t host_ctx;
};

unsigned long get_host_satp(int eid);
int create_enclave(uintptr_t base, uintptr_t size);
int destroy_enclave(int eid);
int copy_to_enclave(int eid, uintptr_t addr, uintptr_t ptr, size_t size);
int copy_from_enclave(int eid, void* ptr, size_t size);
reg run_enclave(int eid, uintptr_t ptr);
uint64_t exit_enclave(uint64_t ret);
#endif
Loading

0 comments on commit 8a33d4b

Please sign in to comment.