/* 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_misc.c * * This file contains APIs for miscellaneous use. */ #include #include #include "api.h" #include "pal.h" #include "pal_debug.h" #include "pal_defs.h" #include "pal_error.h" #include "pal_internal.h" #include "pal_linux.h" #include "pal_linux_defs.h" #include "pal_security.h" #include "sgx_api.h" unsigned long _DkSystemTimeQuery(void) { unsigned long microsec; int ret = ocall_gettime(µsec); if (ret) return -PAL_ERROR_DENIED; return microsec; } size_t _DkRandomBitsRead(void* buffer, size_t size) { uint32_t rand; for (size_t i = 0; i < size; i += sizeof(rand)) { rand = rdrand(); memcpy(buffer + i, &rand, MIN(sizeof(rand), size - i)); } return 0; } int _DkInstructionCacheFlush(const void* addr, int size) { __UNUSED(addr); __UNUSED(size); return -PAL_ERROR_NOTIMPLEMENTED; } int _DkSegmentRegisterSet(int reg, const void* addr) { /* GS is internally used, denied any access to it */ if (reg != PAL_SEGMENT_FS) return -PAL_ERROR_DENIED; SET_ENCLAVE_TLS(fsbase, (void*)addr); wrfsbase((uint64_t)addr); return 0; } int _DkSegmentRegisterGet(int reg, void** addr) { /* GS is internally used, denied any access to it */ if (reg != PAL_SEGMENT_FS) return -PAL_ERROR_DENIED; *addr = (void*)GET_ENCLAVE_TLS(fsbase); return 0; } #define CPUID_CACHE_SIZE 64 #define CPUID_CACHE_INVALID ((unsigned int)-1) static PAL_LOCK cpuid_cache_lock = LOCK_INIT; static struct pal_cpuid { unsigned int recently; unsigned int leaf, subleaf; unsigned int values[4]; } pal_cpuid_cache[CPUID_CACHE_SIZE]; static int pal_cpuid_cache_top = 0; static unsigned int pal_cpuid_clock = 0; int get_cpuid_from_cache(unsigned int leaf, unsigned int subleaf, unsigned int values[4]) { _DkInternalLock(&cpuid_cache_lock); for (int i = 0; i < pal_cpuid_cache_top; i++) if (pal_cpuid_cache[i].leaf == leaf && pal_cpuid_cache[i].subleaf == subleaf) { values[0] = pal_cpuid_cache[i].values[0]; values[1] = pal_cpuid_cache[i].values[1]; values[2] = pal_cpuid_cache[i].values[2]; values[3] = pal_cpuid_cache[i].values[3]; pal_cpuid_cache[i].recently = ++pal_cpuid_clock; _DkInternalUnlock(&cpuid_cache_lock); return 0; } _DkInternalUnlock(&cpuid_cache_lock); return -PAL_ERROR_DENIED; } void add_cpuid_to_cache(unsigned int leaf, unsigned int subleaf, unsigned int values[4]) { struct pal_cpuid* chosen; _DkInternalLock(&cpuid_cache_lock); if (pal_cpuid_cache_top < CPUID_CACHE_SIZE) { for (int i = 0; i < pal_cpuid_cache_top; i++) if (pal_cpuid_cache[i].leaf == leaf && pal_cpuid_cache[i].subleaf == subleaf) { _DkInternalUnlock(&cpuid_cache_lock); return; } chosen = &pal_cpuid_cache[pal_cpuid_cache_top++]; } else { unsigned int oldest_clock = pal_cpuid_cache[0].recently; chosen = &pal_cpuid_cache[0]; if (pal_cpuid_cache[0].leaf == leaf && pal_cpuid_cache[0].subleaf == subleaf) { _DkInternalUnlock(&cpuid_cache_lock); return; } for (int i = 1; i < pal_cpuid_cache_top; i++) { if (pal_cpuid_cache[i].leaf == leaf && pal_cpuid_cache[i].subleaf == subleaf) { _DkInternalUnlock(&cpuid_cache_lock); return; } if (pal_cpuid_cache[i].recently > oldest_clock) { chosen = &pal_cpuid_cache[i]; oldest_clock = pal_cpuid_cache[i].recently; } } } chosen->leaf = leaf; chosen->subleaf = subleaf; chosen->values[0] = values[0]; chosen->values[1] = values[1]; chosen->values[2] = values[2]; chosen->values[3] = values[3]; chosen->recently = ++pal_cpuid_clock; _DkInternalUnlock(&cpuid_cache_lock); } static inline uint32_t extension_enabled(uint32_t xfrm, uint32_t bit_idx) { uint32_t feature_bit = 1U << bit_idx; return xfrm & feature_bit; } static __sgx_mem_aligned sgx_report_t report; static __sgx_mem_aligned sgx_target_info_t target_info; static __sgx_mem_aligned sgx_report_data_t report_data; /** * Initialize the data structures used for CPUID emulation. */ void init_cpuid(void) { memset(&report, 0, sizeof(report)); memset(&target_info, 0, sizeof(target_info)); memset(&report_data, 0, sizeof(report_data)); sgx_report(&target_info, &report_data, &report); } /** * Sanity check untrusted CPUID inputs. * * The basic idea is that there are only a handful of extensions and we know the size needed to * store each extension's state. Use this to sanitize host's untrusted cpuid output. We also know * through xfrm what extensions are enabled inside the enclave. */ static void sanity_check_cpuid(uint32_t leaf, uint32_t subleaf, uint32_t values[4]) { uint64_t xfrm = report.body.attributes.xfrm; enum cpu_extension { x87 = 0, SSE, AVX, MPX_1, MPX_2, AVX512_1, AVX512_2, AVX512_3, PKRU = 9 }; const uint32_t extension_sizes_bytes[] = { [AVX] = 256, [MPX_1] = 64, [MPX_2] = 64, [AVX512_1] = 64, [AVX512_2] = 512, [AVX512_3] = 1024, [PKRU] = 8}; /* Note that AVX offset is 576 bytes and MPX_1 starts at 960. The AVX state size is 256, leaving * 128 bytes unaccounted for. */ const uint32_t extension_offset_bytes[] = { [AVX] = 576, [MPX_1] = 960, [MPX_2] = 1024, [AVX512_1] = 1088, [AVX512_2] = 1152, [AVX512_3] = 1664, [PKRU] = 2688}; enum register_index { EAX = 0, EBX, ECX, EDX }; const uint32_t EXTENDED_STATE_LEAF = 0xd; if (leaf == EXTENDED_STATE_LEAF) { switch (subleaf) { case 0x0: /* From the SDM: "EDX:EAX is a bitmap of all the user state components that can be * managed using the XSAVE feature set. A bit can be set in XCR0 if and only if the * corresponding bit is set in this bitmap. Every processor that supports the XSAVE * feature set will set EAX[0] (x87 state) and EAX[1] (SSE state)." * * On EENTER/ERESUME, the system installs xfrm into XCR0. Hence, we return xfrm here in * EAX. */ values[EAX] = xfrm; /* From the SDM: "EBX enumerates the size (in bytes) required by the XSAVE instruction * for an XSAVE area containing all the user state components corresponding to bits * currently set in XCR0." */ uint32_t xsave_size = 0; /* Start from AVX since x87 and SSE are always captured using XSAVE. Also, x87 and SSE * state size is implicitly included in the extension's offset, e.g., AVX's offset is * 576 which includes x87 and SSE state as well as the XSAVE header. */ for (int i = AVX; i <= PKRU; i++) { if (extension_enabled(xfrm, i)) { xsave_size = extension_offset_bytes[i] + extension_sizes_bytes[i]; } } values[EBX] = xsave_size; /* From the SDM: "ECX enumerates the size (in bytes) required by the XSAVE instruction * for an XSAVE area containing all the user state components supported by this * processor." * * We are assuming here that inside the enclave, ECX and EBX for leaf 0xD and subleaf * 0x1 should always be identical, while outside they can potentially be * different. Also, outside of SGX EBX can change at runtime, while ECX is a static * property. */ values[ECX] = values[EBX]; values[EDX] = 0; break; case 0x1: { const uint32_t xsave_legacy_size = 512; const uint32_t xsave_header = 64; uint32_t save_size_bytes = xsave_legacy_size + xsave_header; /* Start with AVX, since x87 and SSE state is already included when initializing * `save_size_bytes`. */ for (int i = AVX; i <= PKRU; i++) { if (extension_enabled(xfrm, i)) { save_size_bytes += extension_sizes_bytes[i]; } } /* EBX reports the actual size occupied by those extensions irrespective of their * offsets within the xsave area. */ values[EBX] = save_size_bytes; break; } case AVX: case MPX_1: case MPX_2: case AVX512_1: case AVX512_2: case AVX512_3: case PKRU: if (extension_enabled(xfrm, subleaf)) { if (values[EAX] != extension_sizes_bytes[subleaf]) { SGX_DBG(DBG_E, "Unexpected value in host CPUID. Exiting...\n"); _DkProcessExit(1); } } else { if (values[EAX] != 0) { SGX_DBG(DBG_E, "Unexpected value in host CPUID. Exiting...\n"); _DkProcessExit(1); } } break; } } } int _DkCpuIdRetrieve(unsigned int leaf, unsigned int subleaf, unsigned int values[4]) { if (!get_cpuid_from_cache(leaf, subleaf, values)) return 0; if (IS_ERR(ocall_cpuid(leaf, subleaf, values))) return -PAL_ERROR_DENIED; sanity_check_cpuid(leaf, subleaf, values); add_cpuid_to_cache(leaf, subleaf, values); return 0; }