From cf355ee9744fe4de763d52767cef7d17c0c9ece1 Mon Sep 17 00:00:00 2001 From: Dayeol Lee Date: Sun, 7 Oct 2018 17:26:25 -0700 Subject: [PATCH] [SM] Multicore Security Monitor (#4) 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 --- machine/mcall.h | 2 - machine/mentry.S | 18 ++- machine/mtrap.c | 14 +-- machine/mtrap.h | 1 + sm/enclave.c | 257 +++++++++++++++++++++++--------------- sm/enclave.h | 17 +-- sm/pmp.c | 318 ++++++++++++++++++++++++++++++----------------- sm/pmp.h | 58 ++++++++- sm/sm-sbi.c | 23 ++-- sm/sm-sbi.h | 2 - sm/sm.c | 56 ++++++--- 11 files changed, 494 insertions(+), 272 deletions(-) diff --git a/machine/mcall.h b/machine/mcall.h index bc3a6eff6..64d8655db 100644 --- a/machine/mcall.h +++ b/machine/mcall.h @@ -13,8 +13,6 @@ #define SBI_SM_CREATE_ENCLAVE 101 #define SBI_SM_DESTROY_ENCLAVE 102 -#define SBI_SM_COPY_TO_ENCLAVE 103 -#define SBI_SM_COPY_FROM_ENCLAVE 104 #define SBI_SM_RUN_ENCLAVE 105 #define SBI_SM_EXIT_ENCLAVE 1101 #define SBI_SM_NOT_IMPLEMENTED 1111 diff --git a/machine/mentry.S b/machine/mentry.S index 02be269fd..7e686b965 100644 --- a/machine/mentry.S +++ b/machine/mentry.S @@ -15,7 +15,7 @@ trap_table: .word pmp_trap .word misaligned_store_trap .word pmp_trap - .word u_ecall_trap // 8 --> ECALL from U-mode + .word bad_trap .word mcall_trap // 9 --> ECALL from S-mode .word bad_trap .word bad_trap @@ -24,6 +24,10 @@ trap_table: .word __trap_from_machine_mode .word bad_trap .word bad_trap +#ifdef SM_ENABLED +# define HANDLE_IPI_PMP_VECTOR 16 + .word handle_pmp_ipi +#endif .option norvc .section .text.init,"ax",@progbits @@ -80,6 +84,7 @@ trap_vector: lw a0, MENTRY_IPI_PENDING_OFFSET(a0) sw x0, MENTRY_IPI_PENDING_OFFSET(a0) #endif + and a1, a0, IPI_SOFT beqz a1, 1f csrs mip, MIP_SSIP @@ -96,6 +101,11 @@ trap_vector: beqz a1, 1f wfi j 1b +#ifdef SM_ENABLED +1: + andi a1, a0, IPI_PMP + bnez a1, .Lipi_pmp +#endif 1: j .Lmret @@ -190,6 +200,12 @@ restore_regs: LOAD sp, 2*REGBYTES(sp) mret +#ifdef SM_ENABLED +.Lipi_pmp: + li a1, HANDLE_IPI_PMP_VECTOR + j .Lhandle_trap_in_machine_mode +#endif + .Ltrap_from_machine_mode: csrr sp, mscratch addi sp, sp, -INTEGER_CONTEXT_SIZE diff --git a/machine/mtrap.c b/machine/mtrap.c index ea85f22e3..292f62bf6 100644 --- a/machine/mtrap.c +++ b/machine/mtrap.c @@ -94,8 +94,10 @@ static uintptr_t mcall_set_timer(uint64_t when) set_csr(mie, MIP_MTIP); return 0; } - -static void send_ipi_many(uintptr_t* pmask, int event) +#ifndef SM_ENABLED +static +#endif +void send_ipi_many(uintptr_t* pmask, int event) { _Static_assert(MAX_HARTS <= 8 * sizeof(*pmask), "# harts > uintptr_t bits"); uintptr_t mask = hart_mask; @@ -109,7 +111,7 @@ static void send_ipi_many(uintptr_t* pmask, int event) if (event == IPI_SOFT) return; - + // wait until all events have been handled. // prevent deadlock by consuming incoming IPIs. uint32_t incoming_ipi = 0; @@ -172,12 +174,6 @@ void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) case SBI_SM_DESTROY_ENCLAVE: retval = mcall_sm_destroy_enclave(arg0); break; - case SBI_SM_COPY_TO_ENCLAVE: - retval = mcall_sm_copy_to_enclave(arg0, arg1, arg2, arg3); - break; - case SBI_SM_COPY_FROM_ENCLAVE: - retval = mcall_sm_copy_from_enclave(arg0, arg1, arg2); - break; case SBI_SM_RUN_ENCLAVE: retval = mcall_sm_run_enclave(arg0, arg1); break; diff --git a/machine/mtrap.h b/machine/mtrap.h index c4d43d001..d63ce0e63 100644 --- a/machine/mtrap.h +++ b/machine/mtrap.h @@ -79,6 +79,7 @@ static inline void wfi() #define IPI_FENCE_I 0x2 #define IPI_SFENCE_VMA 0x4 #define IPI_HALT 0x8 +#define IPI_PMP 0x10 #define MACHINE_STACK_SIZE RISCV_PGSIZE #define MENTRY_HLS_OFFSET (INTEGER_CONTEXT_SIZE + SOFT_FLOAT_CONTEXT_SIZE) diff --git a/sm/enclave.c b/sm/enclave.c index 14ad1373f..56fe2a2ce 100644 --- a/sm/enclave.c +++ b/sm/enclave.c @@ -4,39 +4,67 @@ #include "pmp.h" #include "page.h" #include +#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 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; @@ -79,62 +107,105 @@ 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> 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); @@ -142,66 +213,39 @@ int destroy_enclave(int 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; @@ -209,27 +253,42 @@ reg run_enclave(int eid, uintptr_t 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; } diff --git a/sm/enclave.h b/sm/enclave.h index 70b02ce1a..8210def47 100644 --- a/sm/enclave.h +++ b/sm/enclave.h @@ -4,6 +4,7 @@ #include "pmp.h" typedef enum { + DESTROYED = -2, INVALID = -1, FRESH = 0, INITIALIZED, @@ -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 diff --git a/sm/pmp.c b/sm/pmp.c index 4d3f742b2..8dd7ccae7 100644 --- a/sm/pmp.c +++ b/sm/pmp.c @@ -1,62 +1,32 @@ #include "pmp.h" +#include "mtrap.h" +#include "atomic.h" +#include "fdt.h" +#include "disabled_hart_mask.h" -uint32_t reg_bitmap = 0; -uint32_t region_def_bitmap = 0; - -struct pmp_region regions[PMP_MAX_N_REGION]; - -#if __riscv_xlen == 64 -# define LIST_OF_PMP_REGS X(0,0) X(1,0) X(2,0) X(3,0) \ - X(4,0) X(5,0) X(6,0) X(7,0) \ - X(8,2) X(9,2) X(10,2) X(11,2) \ - X(12,2) X(13,2) X(14,2) X(15,2) -# define PMP_PER_GROUP 8 -#else -# define LIST_OF_PMP_REGS X(0,0) X(1,0) X(2,0) X(3,0) \ - X(4,1) X(5,1) X(6,1) X(7,1) \ - X(8,2) X(9,2) X(10,2) X(11,2) \ - X(12,3) X(13,3) X(14,3) X(15,3) -# define PMP_PER_GROUP 4 -#endif +static uint32_t reg_bitmap = 0; +static uint32_t region_def_bitmap = 0; +static struct pmp_region regions[PMP_MAX_N_REGION]; -#define PMP_SET(n, g, addr, pmpc) \ -{ uintptr_t oldcfg = read_csr(pmpcfg##g); \ - pmpc |= (oldcfg & ~((uintptr_t)0xff << 8*(n%PMP_PER_GROUP))); \ - asm volatile ("la t0, 1f\n\t" \ - "csrrw t0, mtvec, t0\n\t" \ - "csrw pmpaddr"#n", %0\n\t" \ - "csrw pmpcfg"#g", %1\n\t" \ - ".align 2\n\t" \ - "1: csrw mtvec, t0" \ - : : "r" (addr), "r" (pmpc) : "t0"); \ -} +/* PMP IPI mailbox */ +static int ipi_mailbox[MAX_HARTS] = {0,}; +static int ipi_region_idx = -1; +static enum ipi_type {IPI_PMP_INVALID=-1, + IPI_PMP_SET, + IPI_PMP_UNSET} ipi_type = IPI_PMP_INVALID; -#define PMP_UNSET(n, g) \ -{ uintptr_t pmpc = read_csr(pmpcfg##g); \ - pmpc &= ~((uintptr_t)0xff << 8*(n%PMP_PER_GROUP)); \ - asm volatile ("la t0, 1f \n\t" \ - "csrrw t0, mtvec, t0 \n\t" \ - "csrw pmpaddr"#n", %0\n\t" \ - "csrw pmpcfg"#g", %1\n\t" \ - ".align 2\n\t" \ - "1: csrw mtvec, t0" \ - : : "r" (0), "r" (pmpc) : "t0"); \ -} +/* PMP IPI global lock */ +static spinlock_t pmp_ipi_global_lock = SPINLOCK_INIT; -#define PMP_ERROR(error, msg) {\ - printm("%s:" msg "\n", __func__);\ - return error; \ -} +static spinlock_t pmp_lock = SPINLOCK_INIT; -inline int is_pmp_region_set(int region_idx) { - return (regions[region_idx].reg_idx >= 0); -} -inline int is_pmp_region_valid(int region_idx) { +#ifdef SM_ENABLED +extern void send_ipi_many(uintptr_t*, int); +#endif +static int is_pmp_region_valid(int region_idx) { return TEST_BIT(region_def_bitmap, region_idx); } -inline int is_pmp_reg_set(int reg_idx) { - return TEST_BIT(reg_bitmap, reg_idx); -} + int search_rightmost_unset(uint32_t bitmap, int max) { int i; uint32_t mask = 0x1; @@ -71,44 +41,116 @@ int search_rightmost_unset(uint32_t bitmap, int max) { int get_free_region_idx() { return search_rightmost_unset(region_def_bitmap, PMP_MAX_N_REGION); } + int get_free_reg_idx() { return search_rightmost_unset(reg_bitmap, PMP_N_REG); } -int pmp_set_bind_reg(int region_idx, int reg_idx) +void handle_pmp_ipi(uintptr_t* regs, uintptr_t dummy, uintptr_t mepc) +{ + if(ipi_type == IPI_PMP_SET) + pmp_set(ipi_region_idx); + else + pmp_unset(ipi_region_idx); + + ipi_mailbox[read_csr(mhartid)] = 0; + return; +} + +static void send_pmp_ipi(uintptr_t recipient) +{ + if (((disabled_hart_mask >> recipient) & 1)) return; + /* never send IPI to my self; it will result in a deadlock */ + if (recipient == read_csr(mhartid)) return; + atomic_or(&OTHER_HLS(recipient)->mipi_pending, IPI_PMP); + mb(); + *OTHER_HLS(recipient)->ipi = 1; + ipi_mailbox[recipient] = 1; +} + +static void send_and_sync_pmp_ipi(int region_idx, enum ipi_type type) +{ + uintptr_t mask = hart_mask; + ipi_region_idx = region_idx; + ipi_type = type; + + for(uintptr_t i=0, m=mask; m; i++, m>>=1) + { + if(m & 1) { + send_pmp_ipi(i); + } + } + + /* wait until every other hart sets PMP */ + for(uintptr_t i=0, m=mask; m; i++, m>>=1) { + if(m & 1) { + while( atomic_read(&ipi_mailbox[i]) ) { + continue; + } + } + } +} + +int pmp_unset_global(int region_idx) +{ + if(!is_pmp_region_valid(region_idx)) + PMP_ERROR(-EINVAL, "Invalid PMP region index"); + + /* We avoid any complex PMP-related IPI management + * by ensuring only one hart can enter this region at a time */ +#ifdef __riscv_atomic + spinlock_lock(&pmp_ipi_global_lock); + send_and_sync_pmp_ipi(region_idx, IPI_PMP_UNSET); + spinlock_unlock(&pmp_ipi_global_lock); +#endif + /* unset PMP of itself */ + pmp_unset(region_idx); + + return 0; +} + +/* populate pmp set command to every other hart */ +int pmp_set_global(int region_idx) +{ + if(!is_pmp_region_valid(region_idx)) + PMP_ERROR(-EINVAL, "Invalid PMP region index"); + + /* We avoid any complex PMP-related IPI management + * by ensuring only one hart can enter this region at a time */ +#ifdef __riscv_atomic + spinlock_lock(&pmp_ipi_global_lock); + send_and_sync_pmp_ipi(region_idx, IPI_PMP_SET); + spinlock_unlock(&pmp_ipi_global_lock); +#endif + /* set PMP of itself */ + pmp_set(region_idx); + return 0; +} + +int pmp_set(int region_idx) { if(!is_pmp_region_valid(region_idx)) PMP_ERROR(-EINVAL, "Invalid PMP region index"); - if(is_pmp_region_set(region_idx)) - PMP_ERROR(-EINVAL, "PMP region already set"); - + int reg_idx = regions[region_idx].reg_idx; uintptr_t pmpcfg = (uintptr_t) regions[region_idx].cfg << (8*(reg_idx%PMP_PER_GROUP)); uintptr_t pmpaddr = regions[region_idx].addr; - //printm("pmp_set(): reg %d, pmpcfg:%lx, pmpaddr<<2:%lx\n", reg_idx, pmpcfg, pmpaddr<<2); - - SET_BIT(reg_bitmap, reg_idx); - regions[region_idx].reg_idx = reg_idx; + //spinlock_lock(&pmp_lock); + //printm("pmp_set() [hart %d]: pmpreg %d, address:%lx, size:%lx\n", + // read_csr(mhartid), reg_idx, regions[region_idx].addr, regions[region_idx].size); + //spinlock_unlock(&pmp_lock); int n=reg_idx, g=reg_idx>>2; switch(n) { -#define X(n,g) case n: { PMP_SET(n, g, pmpaddr, pmpcfg); return 0; } +#define X(n,g) case n: { PMP_SET(n, g, pmpaddr, pmpcfg); break; } LIST_OF_PMP_REGS #undef X + default: + die("pmp_set failed: this must not be tolerated\n"); } - // NEVER reach here - // bbl is not properly configured. - PMP_ERROR(-EFAULT, "Unreachable code"); -} - -int pmp_set(int region_idx) -{ - int reg_idx = get_free_reg_idx(); - if(reg_idx < 0) - PMP_ERROR(-EBUSY, "No available PMP register"); - return pmp_set_bind_reg(region_idx, reg_idx); + return 0; } int pmp_unset(int region_idx) @@ -116,22 +158,21 @@ int pmp_unset(int region_idx) if(!is_pmp_region_valid(region_idx)) PMP_ERROR(-EINVAL,"Invalid PMP region index"); - if(!is_pmp_region_set(region_idx)) - PMP_ERROR(-EINVAL, "PMP region not set"); - int reg_idx = regions[region_idx].reg_idx; - int n=reg_idx, g=reg_idx>>2; switch(n) { #define X(n,g) case n: { PMP_UNSET(n, g); break;} LIST_OF_PMP_REGS #undef X default: - return -1; + die("pmp_unset failed: this must not be tolerated\n"); } - - UNSET_BIT(reg_bitmap, reg_idx); - regions[region_idx].reg_idx = -1; + + //spinlock_lock(&pmp_lock); + //printm("pmp_unset() [hart %d]: pmpreg %d, address:%lx, size:%lx\n", + // read_csr(mhartid), reg_idx, regions[region_idx].addr, regions[region_idx].size); + //spinlock_unlock(&pmp_lock); + return 0; } @@ -139,7 +180,7 @@ int pmp_region_debug_print(int region_idx) { if(!is_pmp_region_valid(region_idx)) PMP_ERROR(-EINVAL,"Invalid PMP region index"); - + uintptr_t start = regions[region_idx].start; uint64_t size = regions[region_idx].size; uint8_t perm = regions[region_idx].perm; @@ -158,71 +199,120 @@ int pmp_region_debug_print(int region_idx) return 0; } -int pmp_region_init(uintptr_t start, uint64_t size, uint8_t perm) + +int pmp_region_init_atomic(uintptr_t start, uint64_t size, uint8_t perm, enum pmp_priority priority) { - // do not allow over 256 MB - if(size > PMP_MAX_SIZE) - PMP_ERROR(-ENOSYS, "PMP size over 256MB not implemented"); + int ret; + spinlock_lock(&pmp_lock); + ret = pmp_region_init(start, size, perm, priority); + spinlock_unlock(&pmp_lock); + return ret; +} - // size should be power of 2 - if(!(size && !(size&(size-1)))) - PMP_ERROR(-EINVAL, "PMP size should be power of 2"); - +int pmp_region_init(uintptr_t start, uint64_t size, uint8_t perm,enum pmp_priority priority) +{ + uintptr_t pmp_address; + int reg_idx = -1; + int region_idx = -1; + /* if region covers the entire RAM */ + if(size == -1UL && start == 0) + { + pmp_address = -1UL; + } + else + { + // size should be power of 2 + if(!(size && !(size&(size-1)))) + PMP_ERROR(-EINVAL, "PMP size should be power of 2"); + + // size should be page granularity + if(size & (RISCV_PGSIZE - 1)) + PMP_ERROR(-EINVAL, "PMP granularity is RISCV_PGSIZE"); + + // the starting address must be naturally aligned + if(start & (size-1)) + PMP_ERROR(-EINVAL, "PMP region should be naturally aligned"); + + pmp_address = (start | (size/2-1)) >> 2; + } //find avaiable pmp region idx - int region_idx = get_free_region_idx(); - if(region_idx < 0) + region_idx = get_free_region_idx(); + if(region_idx < 0 || region_idx > PMP_MAX_N_REGION) PMP_ERROR(-EFAULT, "Reached the maximum number of PMP regions"); + switch(priority) + { + case(PMP_PRI_ANY): { + reg_idx = get_free_reg_idx(); + if(reg_idx < 0) + PMP_ERROR(-EFAULT, "No available PMP register"); + if(TEST_BIT(reg_bitmap, reg_idx) || reg_idx >= PMP_N_REG) + PMP_ERROR(-EFAULT, "PMP register unavailable"); + break; + } + case(PMP_PRI_TOP): { + reg_idx = 0; + if(TEST_BIT(reg_bitmap, reg_idx)) + PMP_ERROR(-EFAULT, "PMP register unavailable"); + break; + } + case(PMP_PRI_BOTTOM): { + reg_idx = PMP_N_REG - 1; + if(TEST_BIT(reg_bitmap, reg_idx)) + PMP_ERROR(-EFAULT, "PMP register unavailable"); + break; + } + default: { + PMP_ERROR(-EINVAL, "Invalid priority"); + } + } + // initialize the region (only supports NAPOT) regions[region_idx].start = start; regions[region_idx].size = size; regions[region_idx].perm = perm; regions[region_idx].cfg = (PMP_NAPOT | perm); - regions[region_idx].addr = (start | (size/2-1)) >> 2; - regions[region_idx].reg_idx = -1; + regions[region_idx].addr = pmp_address; + regions[region_idx].reg_idx = reg_idx; SET_BIT(region_def_bitmap, region_idx); - + SET_BIT(reg_bitmap, reg_idx); + return region_idx; } -int pmp_region_free(int region_idx) +int pmp_region_free_atomic(int region_idx) { + + spinlock_lock(&pmp_lock); + if(!is_pmp_region_valid(region_idx)) + { + spinlock_unlock(&pmp_lock); PMP_ERROR(-EINVAL, "Invalid PMP region index"); - + } + /* if(is_pmp_region_set(region_idx)) { int ret = pmp_unset(region_idx); if(ret) { return ret; } - } - + }*/ + + int reg_idx = regions[region_idx].reg_idx; + UNSET_BIT(region_def_bitmap, region_idx); + UNSET_BIT(reg_bitmap, reg_idx); regions[region_idx].start = 0; regions[region_idx].size = 0; regions[region_idx].perm = 0; regions[region_idx].cfg = 0; regions[region_idx].addr = 0; - UNSET_BIT(region_def_bitmap, region_idx); + regions[region_idx].reg_idx = -1; + + spinlock_unlock(&pmp_lock); return 0; } -int set_os_pmp_region() -{ - int region_idx = get_free_region_idx(); - int reg_idx = PMP_N_REG - 1; //last PMP reg - uint8_t perm = PMP_W | PMP_X | PMP_R; - regions[region_idx].start = 0; - regions[region_idx].size = -1UL; - regions[region_idx].perm = perm; - regions[region_idx].cfg = (PMP_NAPOT | perm); - regions[region_idx].addr = (-1UL); - regions[region_idx].reg_idx = -1; - SET_BIT(region_def_bitmap, region_idx); - - return pmp_set_bind_reg(region_idx, reg_idx); -} - void* pmp_get_addr(int region_idx) { if(!TEST_BIT(region_def_bitmap, region_idx)) diff --git a/sm/pmp.h b/sm/pmp.h index b292ab5b5..fffb724a0 100644 --- a/sm/pmp.h +++ b/sm/pmp.h @@ -7,18 +7,68 @@ #define PMP_N_REG 16 //number of PMP registers #define PMP_MAX_N_REGION PMP_N_REG //maximum number of PMP regions -#define PMP_MAX_SIZE 0x10000000 //maximum size of a PMP region #define SET_BIT(bitmap, n) (bitmap |= (0x1 << n)) #define UNSET_BIT(bitmap, n) (bitmap &= ~(0x1 << n)) #define TEST_BIT(bitmap, n) (bitmap & (0x1 << n)) +enum pmp_priority { + PMP_PRI_ANY, + PMP_PRI_TOP, + PMP_PRI_BOTTOM, +}; + +#if __riscv_xlen == 64 +# define LIST_OF_PMP_REGS X(0,0) X(1,0) X(2,0) X(3,0) \ + X(4,0) X(5,0) X(6,0) X(7,0) \ + X(8,2) X(9,2) X(10,2) X(11,2) \ + X(12,2) X(13,2) X(14,2) X(15,2) +# define PMP_PER_GROUP 8 +#else +# define LIST_OF_PMP_REGS X(0,0) X(1,0) X(2,0) X(3,0) \ + X(4,1) X(5,1) X(6,1) X(7,1) \ + X(8,2) X(9,2) X(10,2) X(11,2) \ + X(12,3) X(13,3) X(14,3) X(15,3) +# define PMP_PER_GROUP 4 +#endif + +#define PMP_SET(n, g, addr, pmpc) \ +{ uintptr_t oldcfg = read_csr(pmpcfg##g); \ + pmpc |= (oldcfg & ~((uintptr_t)0xff << 8*(n%PMP_PER_GROUP))); \ + asm volatile ("la t0, 1f\n\t" \ + "csrrw t0, mtvec, t0\n\t" \ + "csrw pmpaddr"#n", %0\n\t" \ + "csrw pmpcfg"#g", %1\n\t" \ + ".align 2\n\t" \ + "1: csrw mtvec, t0" \ + : : "r" (addr), "r" (pmpc) : "t0"); \ +} + +#define PMP_UNSET(n, g) \ +{ uintptr_t pmpc = read_csr(pmpcfg##g); \ + pmpc &= ~((uintptr_t)0xff << 8*(n%PMP_PER_GROUP)); \ + asm volatile ("la t0, 1f \n\t" \ + "csrrw t0, mtvec, t0 \n\t" \ + "csrw pmpaddr"#n", %0\n\t" \ + "csrw pmpcfg"#g", %1\n\t" \ + ".align 2\n\t" \ + "1: csrw mtvec, t0" \ + : : "r" (0), "r" (pmpc) : "t0"); \ +} + +#define PMP_ERROR(error, msg) {\ + printm("%s:" msg "\n", __func__);\ + return error; \ +} + int pmp_region_debug_print(int region); -int pmp_region_init(uintptr_t start, uint64_t size, uint8_t perm); -int pmp_region_free(int region); +int pmp_region_init_atomic(uintptr_t start, uint64_t size, uint8_t perm, enum pmp_priority pri); +int pmp_region_init(uintptr_t start, uint64_t size, uint8_t perm, enum pmp_priority pri); +int pmp_region_free_atomic(int region); int pmp_set(int n); +int pmp_set_global(int n); int pmp_unset(int n); -int set_os_pmp_region(void); +int pmp_unset_global(int n); void* pmp_get_addr(int region); uint64_t pmp_get_size(int region); diff --git a/sm/sm-sbi.c b/sm/sm-sbi.c index 15fb64094..7b11ad444 100644 --- a/sm/sm-sbi.c +++ b/sm/sm-sbi.c @@ -1,32 +1,23 @@ #include "sm-sbi.h" + #include "pmp.h" #include "enclave.h" #include int mcall_sm_create_enclave(unsigned long base, unsigned long size) { - return create_enclave((uintptr_t) base, (uintptr_t) size); + int ret; + ret = create_enclave((uintptr_t) base, (uintptr_t) size); + return ret; } int mcall_sm_destroy_enclave(unsigned long eid) { + int ret; if(get_host_satp(eid) != read_csr(satp)) return -EFAULT; - return destroy_enclave((int)eid); -} - -int mcall_sm_copy_from_enclave(unsigned long eid, unsigned long ptr, unsigned long size) -{ - if(get_host_satp(eid) != read_csr(satp)) - return -EFAULT; - return copy_from_enclave(eid, (void*) ptr, (size_t) size); -} - -int mcall_sm_copy_to_enclave(unsigned long eid, unsigned long addr, unsigned long ptr, unsigned long size) -{ - if(get_host_satp(eid) != read_csr(satp)) - return -EFAULT; - return copy_to_enclave(eid, (uintptr_t) addr,(uintptr_t) ptr, (size_t) size); + ret = destroy_enclave((int)eid); + return ret; } int mcall_sm_run_enclave(unsigned long eid, unsigned long ptr) diff --git a/sm/sm-sbi.h b/sm/sm-sbi.h index 5485d7cfa..a76217e40 100644 --- a/sm/sm-sbi.h +++ b/sm/sm-sbi.h @@ -5,8 +5,6 @@ #include int mcall_sm_create_enclave(unsigned long base, unsigned long size); int mcall_sm_destroy_enclave(unsigned long eid); -int mcall_sm_copy_from_enclave(unsigned long eid, unsigned long ptr, unsigned long size); -int mcall_sm_copy_to_enclave(unsigned long eid, unsigned long addr, unsigned long ptr, unsigned long size); int mcall_sm_run_enclave(unsigned long eid, unsigned long ptr); int mcall_sm_exit_enclave(unsigned long ret); int mcall_sm_not_implemented(unsigned long a0); diff --git a/sm/sm.c b/sm/sm.c index 7e13445f2..8d1252188 100644 --- a/sm/sm.c +++ b/sm/sm.c @@ -1,6 +1,11 @@ #include "sm.h" #include "mtrap.h" #include "pmp.h" +#include "atomic.h" + +static int sm_init_done = 0; +static int sm_region_id, os_region_id; +static spinlock_t sm_init_lock = SPINLOCK_INIT; typedef unsigned char byte; @@ -8,28 +13,29 @@ extern byte sanctum_sm_signature[64]; extern byte sanctum_dev_public_key[32]; extern unsigned int sanctum_sm_size[1]; -int smm_init(uintptr_t start, uint64_t size, uint8_t perm) +int smm_init() { - int region = pmp_region_init(start, size, perm); + uint8_t perm = 0; + int region = pmp_region_init(SMM_BASE, SMM_SIZE, perm, PMP_PRI_TOP); if(region < 0) - { - printm("sm: failed to initialize a PMP region\n"); return -1; - } - //printm("sm: SMM PMP region index = %d\n", region); - int reg = pmp_set(region); - - if(reg < 0) - { - printm("sm: failed to set PMP\n"); - pmp_region_debug_print(region); + return region; +} + +int osm_init() +{ + uint8_t perm = PMP_W | PMP_X | PMP_R; + printm("osm_init\n"); + int region = pmp_region_init(0, -1UL, perm, PMP_PRI_BOTTOM); + printm("osm_init done\n"); + if(region < 0) return -1; - } - return 0; + return region; } + void sm_print_cert() { int i; @@ -57,11 +63,27 @@ void sm_print_cert() void sm_init(void) { // initialize SMM - smm_init(SMM_BASE, SMM_SIZE, 0); - // reserve the last PMP register for the OS - set_os_pmp_region(); + spinlock_lock(&sm_init_lock); + + if(!sm_init_done) { + sm_region_id = smm_init(); + if(sm_region_id < 0) + die("[SM] intolerable error - failed to initialize SM memory"); + + os_region_id = osm_init(); + if(os_region_id < 0) + die("[SM] intolerable error - failed to initialize OS memory"); + + sm_init_done = 1; + } + + pmp_set(sm_region_id); + pmp_set(os_region_id); + + spinlock_unlock(&sm_init_lock); + return; // for debug // sm_print_cert(); }