├── .clang_complete ├── .gitignore ├── .gitmodules ├── .travis.yml ├── README.md ├── scripts ├── deps │ ├── all.sh │ ├── cmake.sh │ ├── llvm.sh │ ├── ninja.sh │ └── setup.sh └── run_gyp.sh └── src ├── bare ├── arch │ ├── riscv │ │ ├── base_types_arch.h │ │ ├── bit_masking_arch.h │ │ ├── cpu_context_arch.h │ │ ├── memory_arch.h │ │ ├── page_tables_arch.h │ │ ├── phys_atomics_arch.h │ │ └── phys_ptr_arch.h │ └── test │ │ ├── base_types_arch.h │ │ ├── bit_masking_arch.h │ │ ├── cpu_context_arch.cc │ │ ├── cpu_context_arch.h │ │ ├── gtest_main.cc │ │ ├── memory_arch.cc │ │ ├── memory_arch.h │ │ ├── page_tables_arch.h │ │ ├── phys_atomics_arch.h │ │ ├── phys_ptr_arch.cc │ │ └── phys_ptr_arch.h ├── bare.gyp ├── base_types.h ├── base_types_test.cc ├── bit_masking.h ├── bit_masking_test.cc ├── cpu_context.h ├── cpu_context_test.cc ├── memory.h ├── memory_test.cc ├── page_tables.h ├── page_tables_test.cc ├── phys_atomics.h ├── phys_atomics_test.cc ├── phys_ptr.h ├── phys_ptr_test.cc ├── traits.h └── traits_test.cc ├── common.gypi ├── crypto ├── crypto.gyp ├── hash.cc ├── hash.h └── hash_test.cc ├── deps ├── gtest.gyp ├── libcxx.gyp └── libcxxabi.gyp ├── main.gyp └── monitor ├── api_compile_check.cc ├── boot_init.cc ├── boot_init.h ├── boot_init_test.cc ├── cpu_core.cc ├── cpu_core.h ├── cpu_core_inl.h ├── cpu_core_inl_test.cc ├── cpu_core_test.cc ├── dram_regions.cc ├── dram_regions.h ├── dram_regions_inl.h ├── dram_regions_inl_test.cc ├── enclave.cc ├── enclave.h ├── enclave_init.cc ├── enclave_inl.h ├── enclave_inl_test.cc ├── mailbox.cc ├── mailbox.h ├── mailbox_test.cc ├── measure_inl.h ├── measure_inl_test.cc ├── metadata.cc ├── metadata.h ├── metadata_inl.h ├── metadata_inl_test.cc ├── monitor.gyp └── public └── api.h /.clang_complete: -------------------------------------------------------------------------------- 1 | -Isrc/ 2 | -std=c++11 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors. 2 | *.sw* 3 | .DS_Store 4 | 5 | # Build infrastracture. 6 | build/ 7 | 8 | # Build products. 9 | out/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/ninja"] 2 | path = deps/ninja 3 | url = https://github.com/martine/ninja.git 4 | [submodule "deps/gyp"] 5 | path = deps/gyp 6 | url = https://chromium.googlesource.com/external/gyp.git 7 | [submodule "deps/llvm"] 8 | path = deps/llvm 9 | url = https://chromium.googlesource.com/chromiumos/third_party/llvm.git 10 | [submodule "deps/compiler-rt"] 11 | path = deps/compiler-rt 12 | url = https://chromium.googlesource.com/chromiumos/third_party/compiler-rt.git 13 | [submodule "deps/clang"] 14 | path = deps/clang 15 | url = https://chromium.googlesource.com/chromiumos/third_party/clang.git 16 | [submodule "deps/libcxxabi"] 17 | path = deps/libcxxabi 18 | url = https://chromium.googlesource.com/chromium/llvm-project/libcxxabi.git 19 | [submodule "deps/newlib-cygwin"] 20 | path = deps/newlib-cygwin 21 | url = git://sourceware.org/git/newlib-cygwin.git 22 | [submodule "deps/libcxx"] 23 | path = deps/libcxx 24 | url = https://chromium.googlesource.com/chromium/llvm-project/libcxx.git 25 | [submodule "deps/googletest"] 26 | path = deps/googletest 27 | url = https://github.com/google/googletest/ 28 | [submodule "deps/cmake"] 29 | path = deps/cmake 30 | url = https://cmake.org/cmake.git 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | # sudo: required 3 | os: linux 4 | # dist: trusty 5 | language: generic 6 | addons: 7 | apt: 8 | sources: 9 | - llvm-toolchain-precise-3.7 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - clang-3.7 13 | - g++-5 14 | 15 | env: 16 | global: 17 | - CC=clang-3.7 18 | - CXX=clang++-3.7 19 | matrix: 20 | # - BUILD=Debug 21 | - BUILD=Release 22 | 23 | install: 24 | # Check baseline memory usage; useful to know when OOMs occur 25 | - free 26 | - vmstat 27 | - ps aux --sort=-rss | head -n 10 28 | 29 | # Run the scripts in scripts/deps/all.sh one at a time. This buys a little 30 | # time, as Travis limits each step here to 50 minutes. 31 | - ./scripts/deps/setup.sh 32 | - ./scripts/deps/ninja.sh 33 | - ./scripts/deps/cmake.sh 34 | - ./scripts/deps/llvm.sh 35 | 36 | before_script: 37 | - ./build/run_gyp.sh 38 | 39 | script: 40 | - ./build/ninja -C out/$BUILD 41 | - ./out/$BUILD/bare_tests 42 | - ./out/$BUILD/crypto_tests 43 | - ./out/$BUILD/monitor_tests 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sanctum 2 | 3 | **This repository is deprecated. Please use https://github.com/ilebedev/sanctum.** 4 | 5 | Research prototype for a SGX clone that protects against all software attacks, 6 | including timing attacks, and requires minimal architectural extensions to a 7 | RISC V processor. 8 | 9 | ## Building the Code 10 | 11 | ### Prerequisites 12 | 13 | Sanctum requires [git](https://git-scm.com/) and a C++ toolchain that can build 14 | [CMake](https://cmake.org/) and [LLVM](http://llvm.org/). 15 | 16 | ### One-Time Setup 17 | 18 | ```bash 19 | ./scripts/deps/all.sh 20 | ./build/run_gyp.sh 21 | ``` 22 | 23 | ### Build 24 | 25 | If the `.gyp` files have been changed, GYP has to be re-run. 26 | 27 | ```bash 28 | ./build/run_gyp.sh 29 | ``` 30 | 31 | ```bash 32 | ./build/ninja -C out/Release 33 | ./build/ninja -C out/Debug 34 | ``` 35 | 36 | 37 | ## Development 38 | 39 | ### Running Unit Tests 40 | 41 | ```bash 42 | ./out/Debug/monitor_tests 43 | ``` 44 | 45 | ### Useful Ninja Parameters 46 | 47 | Serialized build, for debugging gyp issues. 48 | 49 | ```bash 50 | ./build/ninja -C out/Debug -j1 51 | ``` 52 | 53 | Don't stop on first error, build as much as possible. 54 | 55 | ```bash 56 | ./build/ninja -C out/Debug -k0 57 | ``` 58 | 59 | ### Autocomplete 60 | 61 | This project should be set up correctly to use 62 | [autocomplete-clang](https://atom.io/packages/autocomplete-clang). 63 | 64 | 65 | ## Copyright 66 | 67 | This work is Copyright (c) 2015 Victor Costan, all rights reserved. 68 | 69 | Pending paper publication, this will be available under the MIT license. 70 | -------------------------------------------------------------------------------- /scripts/deps/all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Sets up the build system dependencies. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | # NOTE: All the scripts below also need to be listed in .travis.yml 9 | # We cannot call all.sh directly in .travis.yml, because that would 10 | # exceed the 50-minute time limit for a single build step. 11 | 12 | ./scripts/deps/setup.sh 13 | ./scripts/deps/ninja.sh 14 | ./scripts/deps/cmake.sh 15 | ./scripts/deps/llvm.sh 16 | -------------------------------------------------------------------------------- /scripts/deps/cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Builds and symlinks CMake. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | mkdir -p build/cmake-build 9 | cd build/cmake-build 10 | ../../deps/cmake/bootstrap 11 | make 12 | 13 | cd ../.. 14 | rm -f build/cmake 15 | ln -s "$PWD/build/cmake-build/bin/cmake" build/ 16 | -------------------------------------------------------------------------------- /scripts/deps/llvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Builds and symlinks LLVM. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | mkdir -p build/llvm-build 9 | cd build/llvm-build 10 | 11 | # NOTE: CMake's ninja generator barfs if it can't find ninja on the path. 12 | PATH="$PATH:$PWD/../" ../cmake -G "Ninja" \ 13 | -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo \ 14 | -DLLVM_ENABLE_RTTI:BOOL=ON \ 15 | -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ 16 | -DLLVM_INCLUDE_TESTS:BOOL=OFF \ 17 | -DLLVM_EXTERNAL_CLANG_SOURCE_DIR:PATH="$PWD/../../deps/clang" \ 18 | ../../deps/llvm 19 | ../ninja 20 | 21 | cd ../.. 22 | rm -f build/llvm 23 | ln -s "$PWD/build/llvm-build/Release+Debug+Asserts/" build/llvm 24 | -------------------------------------------------------------------------------- /scripts/deps/ninja.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Builds and symlinks Ninja. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | # NOTE: Ninja really wants to be built inside its source tree. 9 | cd deps/ninja 10 | 11 | ./configure.py --bootstrap 12 | rm -f null.o # NOTE: This gets created for some weird reason. 13 | 14 | cd ../.. 15 | rm -f build/ninja 16 | ln -s "$PWD/deps/ninja/ninja" build/ninja 17 | -------------------------------------------------------------------------------- /scripts/deps/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Project-specific setup. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | # Check out all the build dependencies stored in Git submodules. 9 | git submodule update --init 10 | 11 | # Symlink build scripts in the build directory. 12 | mkdir -p build/ 13 | ln -s "$PWD/scripts/run_gyp.sh" build/run_gyp.sh 14 | -------------------------------------------------------------------------------- /scripts/run_gyp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Rebuilds ninja files out of gyp files. 4 | 5 | set -o errexit # Stop the script on the first error. 6 | set -o nounset # Catch un-initialized variables. 7 | 8 | deps/gyp/gyp \ 9 | --format=ninja \ 10 | --toplevel-dir=. \ 11 | src/main.gyp 12 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/base_types_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_BASE_TYPES_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_BASE_TYPES_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | // NOTE: The sizes below reflect RV64. 8 | 9 | using size_t = unsigned long long; 10 | using uintptr_t = unsigned long long; 11 | using uint64_t = unsigned long long; 12 | using uint32_t = unsigned int; 13 | using uint8_t = unsigned char; 14 | 15 | }; // namespace sanctum::bare 16 | }; // namespace sanctum 17 | #endif // !definded(BARE_ARCH_RISCV_BASE_TYPES_ARCH_H_INCLUDED) 18 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/bit_masking_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_BIT_MASKING_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_BIT_MASKING_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | constexpr inline bool is_big_endian() { 8 | return false; 9 | } 10 | 11 | }; // namespace sanctum::bare 12 | }; // namespace sanctum 13 | #endif // !definded(BARE_ARCH_RISCV_BIT_MASKING_ARCH_H_INCLUDED) 14 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/cpu_context_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_CPU_CONTEXT_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_CPU_CONTEXT_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | inline size_t read_core_count() { 8 | // TODO: asm intrinsic 9 | return 0; 10 | } 11 | inline size_t current_core() { 12 | // TODO: asm intrinsic 13 | return 0; 14 | } 15 | inline void flush_tlbs() { 16 | // TODO: asm intrinsic 17 | } 18 | inline void flush_private_caches() { 19 | // TODO: asm intrinsic 20 | } 21 | inline void set_cache_index_shift(size_t cache_index_shift) { 22 | // TODO: asm intrinsic 23 | } 24 | inline void set_eptbr(uintptr_t value) { 25 | // TODO: asm intrinsic 26 | } 27 | inline void set_ptbr(uintptr_t value) { 28 | // TODO: asm intrinsic 29 | } 30 | inline void set_epar_base(uintptr_t value) { 31 | // TODO: asm intrinsic 32 | } 33 | inline void set_par_base(uintptr_t value) { 34 | // TODO: asm intrinsic 35 | } 36 | inline void set_epar_mask(uintptr_t value) { 37 | // TODO: asm intrinsic 38 | } 39 | inline void set_par_mask(uintptr_t value) { 40 | // TODO: asm intrinsic 41 | } 42 | inline void set_ev_base(uintptr_t value) { 43 | // TODO: asm intrinsic 44 | } 45 | inline void set_ev_mask(uintptr_t value) { 46 | // TODO: asm intrinsic 47 | } 48 | inline void set_drb_map(uintptr_t phys_addr) { 49 | // TODO: asm intrinsic 50 | } 51 | inline void set_edrb_map(uintptr_t phys_addr) { 52 | // TODO: asm intrinsic 53 | } 54 | 55 | struct exec_state_t { 56 | uintptr_t pc; // x1 57 | uintptr_t stack; // x14 58 | uintptr_t gprs[29]; // x2-13, x15-x31 59 | }; 60 | 61 | }; // namespace sanctum::bare 62 | }; // namespace sanctum 63 | #endif // !defined(BARE_ARCH_RISCV_CPU_CONTEXT_ARCH_H_INCLUDED) 64 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/memory_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_MEMORY_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_MEMORY_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | inline void set_dmar_base(uintptr_t value) { 8 | // TODO: asm intrinsics 9 | } 10 | inline void set_dmar_mask(uintptr_t value) { 11 | // TODO: asm intrinsics 12 | } 13 | inline size_t read_dram_size() { 14 | // TODO: asm intrinsics 15 | return 0; 16 | } 17 | inline size_t read_cache_levels() { 18 | // TODO: asm intrinsics 19 | return 0; 20 | } 21 | inline bool is_shared_cache(size_t cache_level) { 22 | // TODO: asm intrinsics 23 | return 0; 24 | } 25 | inline size_t read_cache_line_size(size_t cache_level) { 26 | // TODO: asm intrinsics 27 | return 0; 28 | } 29 | inline size_t read_cache_set_count(size_t cache_level) { 30 | // TODO: asm intrinsics 31 | return 0; 32 | } 33 | inline size_t read_min_cache_index_shift() { 34 | // TODO: asm intrinsics 35 | return 0; 36 | } 37 | inline size_t read_max_cache_index_shift() { 38 | // TODO: asm intrinsics 39 | return 0; 40 | } 41 | 42 | template inline void bzero(phys_ptr start, size_t bytes) { 43 | // TODO: asm intrinsics 44 | } 45 | template inline void bcopy(phys_ptr dest, phys_ptr source, 46 | size_t bytes) { 47 | // TODO: asm intrinsics 48 | } 49 | 50 | }; // namespace sanctum::bare 51 | }; // namespace sanctum 52 | #endif // !defined(BARE_ARCH_RISCV_MEMORY_ARCH_H_INCLUDED) 53 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/page_tables_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_PAGE_TABLES_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_PAGE_TABLES_ARCH_H_INCLUDED 3 | 4 | #include "../../phys_ptr.h" 5 | 6 | namespace sanctum { 7 | namespace bare { 8 | 9 | constexpr size_t page_shift() { 10 | return 12; 11 | } 12 | 13 | // NOTE: The constants below reflect RV39. 14 | 15 | constexpr size_t page_table_levels() { 16 | return 3; 17 | } 18 | constexpr inline size_t page_table_shift(size_t level) { 19 | return 9; 20 | } 21 | constexpr inline size_t page_table_entry_shift(size_t level) { 22 | return 3; // 8 bytes per page table entry 23 | } 24 | 25 | inline bool is_valid_page_table_entry(uintptr_t entry_addr, size_t level) { 26 | return *(phys_ptr{entry_addr}) & 1; 27 | } 28 | inline uintptr_t page_table_entry_target(uintptr_t entry_addr, size_t level) { 29 | uintptr_t target_mask = ~((1 << page_shift()) - 1); 30 | return *(phys_ptr{entry_addr}) & target_mask; 31 | } 32 | inline void write_page_table_entry(uintptr_t entry_addr, size_t level, 33 | uintptr_t target, uintptr_t acl) { 34 | uintptr_t acl_mask = (1 << page_shift()) - 1; 35 | acl &= acl_mask; // Mask off non-ACL bits. 36 | acl |= 1; // Force valid to true. 37 | *(phys_ptr{entry_addr}) = target | acl; 38 | } 39 | 40 | }; // namespace sanctum::bare 41 | }; // namespace sanctum 42 | #endif // !definded(BARE_ARCH_RISCV_PAGE_TABLES_ARCH_H_INCLUDED) 43 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/phys_atomics_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | // Lock-free boolean type. 8 | struct atomic_flag { 9 | public: 10 | // Initializes the flag to an unknown value. 11 | inline atomic_flag() noexcept = default; 12 | 13 | // Atomic flags cannot be copied. 14 | atomic_flag(const atomic_flag&) = delete; 15 | atomic_flag& operator=(const atomic_flag&) = delete; 16 | atomic_flag& operator=(const atomic_flag&) volatile = delete; 17 | 18 | inline bool test_and_set() volatile noexcept { 19 | // TODO: asm intrinsic 20 | return false; 21 | } 22 | inline bool test_and_set() noexcept { 23 | // TODO: asm intrinsic 24 | return false; 25 | } 26 | inline void clear() volatile noexcept { 27 | // TODO: asm intrinsic 28 | } 29 | inline void clear() noexcept { 30 | // TODO: asm intrinsic 31 | } 32 | 33 | private: 34 | int flag; 35 | }; 36 | 37 | template<> struct atomic { 38 | uintptr_t __value; 39 | }; 40 | 41 | inline template<> void atomic_init(phys_ptr> object, 42 | uintptr_t value) noexcept { 43 | // TODO: asm intrinsic 44 | } 45 | inline template<> uintptr_t atomic_load(phys_ptr> object) 46 | noexcept { 47 | // TODO: asm intrinsic 48 | return 0; 49 | } 50 | inline template<> void atomic_store(phys_ptr> object, 51 | uintptr_t value) noexcept { 52 | // TODO: asm intrinsic 53 | } 54 | inline template<> uintptr_t atomic_fetch_add( 55 | phys_ptr> object, uintptr_t value) noexcept { 56 | // TODO: asm intrinsic 57 | return 0; 58 | } 59 | inline template<> uintptr_t atomic_fetch_sub( 60 | phys_ptr> object, uintptr_t value) noexcept { 61 | // TODO: asm intrinsic 62 | return 0; 63 | } 64 | 65 | 66 | }; // namespace sanctum::bare 67 | }; // namespace sanctum 68 | #endif // !definded(BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED) 69 | -------------------------------------------------------------------------------- /src/bare/arch/riscv/phys_ptr_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_PHYS_PTR_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_PHYS_PTR_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | // Specializing physptr_t for uint32_t and uint64_t is guaranteed to cover 8 | // size_t and uintptr_t. 9 | static_assert(sizeof(uintptr_t) == sizeof(uint32_t) || 10 | sizeof(uintptr_t) == sizeof(uint64_t), "uintptr_t isn't 32-bit or 64-bit"); 11 | static_assert(sizeof(size_t) == sizeof(uint32_t) || 12 | sizeof(size_t) == sizeof(uint64_t), "size_t isn't 32-bit or 64-bit"); 13 | 14 | // On RISC-V, the monitor runs in machine mode, where all loads and stores use 15 | // physical addresses. 16 | 17 | template<> inline phys_ref::operator uint64_t() const { 18 | return *(reinterpret_cast(addr)); 19 | } 20 | template<> inline phys_ref& phys_ref:: 21 | operator =(const uint64_t& value) { 22 | *(reinterpret_cast(addr)) = value; 23 | return *this; 24 | } 25 | 26 | template<> inline phys_ref::operator uint32_t() const { 27 | return *(reinterpret_cast(addr)); 28 | } 29 | template<> inline phys_ref& phys_ref:: 30 | operator =(const uint32_t& value) { 31 | *(reinterpret_cast(addr)) = value; 32 | return *this; 33 | } 34 | 35 | }; // namespace sanctum::bare 36 | }; // namespace sanctum 37 | #endif // !definded(BARE_ARCH_RISCV_PHYS_PTR_ARCH_H_INCLUDED) 38 | -------------------------------------------------------------------------------- /src/bare/arch/test/base_types_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_BASE_TYPES_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_BASE_TYPES_ARCH_H_INCLUDED 3 | 4 | #include // size_t 5 | #include // uintptr_t, uint8_t, uint32_t, uint64_t 6 | 7 | namespace sanctum { 8 | namespace bare { 9 | 10 | using size_t = std::size_t; 11 | using uintptr_t = std::uintptr_t; 12 | using uint8_t = std::uint8_t; 13 | using uint32_t = std::uint32_t; 14 | using uint64_t = std::uint64_t; 15 | 16 | }; // namespace sanctum::bare 17 | }; // namespace sanctum 18 | #endif // !definded(BARE_ARCH_TEST_BASE_TYPES_ARCH_H_INCLUDED) 19 | -------------------------------------------------------------------------------- /src/bare/arch/test/bit_masking_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_BIT_MASKING_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_BIT_MASKING_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | constexpr inline bool is_big_endian() { 8 | // NOTE: We're just assuming that tests are compiled and executed on a 9 | // little-endian system. If that's not the case, the SHA-256 tests will 10 | // fail. Until someone complains, we won't bother figuring out a way to 11 | // determine this at compile time. 12 | return false; 13 | } 14 | 15 | }; // namespace sanctum::bare 16 | }; // namespace sanctum 17 | #endif // !definded(BARE_ARCH_TEST_BIT_MASKING_ARCH_H_INCLUDED) 18 | -------------------------------------------------------------------------------- /src/bare/arch/test/cpu_context_arch.cc: -------------------------------------------------------------------------------- 1 | #include "../../cpu_context.h" 2 | 3 | #include "../../phys_ptr.h" 4 | 5 | #include // Core configuration use assert for bound checking. 6 | 7 | using namespace sanctum::bare; 8 | 9 | namespace sanctum { 10 | namespace testing { 11 | 12 | size_t core_count = 0; 13 | size_t current_core = 0; 14 | size_t dram_region_bitmap_words = 0; 15 | 16 | size_t core_tlb_flush_count[max_cores]; 17 | size_t core_cache_flush_count[max_cores]; 18 | size_t core_cache_index_shift[max_cores]; 19 | uintptr_t core_ptbr[max_cores]; 20 | uintptr_t core_eptbr[max_cores]; 21 | uintptr_t core_ev_base[max_cores]; 22 | uintptr_t core_ev_mask[max_cores]; 23 | uintptr_t core_par_base[max_cores]; 24 | uintptr_t core_epar_base[max_cores]; 25 | uintptr_t core_par_mask[max_cores]; 26 | uintptr_t core_epar_mask[max_cores]; 27 | size_t core_drb_map[max_cores][max_dram_region_bitmap_words]; 28 | size_t core_edrb_map[max_cores][max_dram_region_bitmap_words]; 29 | 30 | void set_current_core(size_t core_id) { 31 | assert(core_id < core_count); 32 | current_core = core_id; 33 | } 34 | 35 | void set_core_count(size_t new_core_count) { 36 | assert(new_core_count > 0 && new_core_count <= max_cores); 37 | core_count = new_core_count; 38 | current_core = 0; 39 | } 40 | 41 | void set_dram_region_bitmap_words(size_t new_dram_region_bitmap_words) { 42 | assert(new_dram_region_bitmap_words <= max_dram_region_bitmap_words); 43 | dram_region_bitmap_words = new_dram_region_bitmap_words; 44 | } 45 | 46 | void snapshot_dram_region_bitmap(uintptr_t* bitmap_register, 47 | uintptr_t phys_addr) { 48 | phys_ptr map_ptr{phys_addr}; 49 | for (size_t i = 0; i < dram_region_bitmap_words; ++i, map_ptr += 1) 50 | bitmap_register[i] = *map_ptr; 51 | } 52 | 53 | }; // namespace sanctum::testing 54 | }; // namespace sanctum 55 | -------------------------------------------------------------------------------- /src/bare/arch/test/cpu_context_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_CPU_CONTEXT_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_CPU_CONTEXT_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace testing { 6 | 7 | extern size_t core_count; 8 | extern size_t current_core; 9 | constexpr size_t max_cores = 32; 10 | extern size_t dram_region_bitmap_words; 11 | constexpr size_t max_dram_region_bitmap_words = 8; 12 | 13 | // For testing, we maintain a count of the number of times that TLBs and 14 | // private caches were flushed. Tests reset the counters before exercising 15 | // flushing-related functionality, then compare counters against expected 16 | // values. 17 | extern size_t core_tlb_flush_count[], core_cache_flush_count[]; 18 | 19 | // For testing, the cache index shift register is virtualized. 20 | extern size_t core_cache_index_shift[]; 21 | 22 | // For testing, the page walker input registers are virtualized. 23 | extern uintptr_t core_ptbr[], core_eptbr[], core_ev_base[], core_ev_mask[]; 24 | extern uintptr_t core_par_base[], core_epar_base[]; 25 | extern uintptr_t core_par_mask[], core_epar_mask[]; 26 | extern size_t core_drb_map[][max_dram_region_bitmap_words]; 27 | extern size_t core_edrb_map[][max_dram_region_bitmap_words]; 28 | 29 | // Sets the return value of current_core(). 30 | void set_current_core(size_t core_id); 31 | 32 | // Sets the return value of read_core_count(). 33 | // 34 | // This also resets the current core to 0. 35 | void set_core_count(size_t new_core_count); 36 | 37 | // Sets the size of DRAM region bitmap registers, in words. 38 | // 39 | // The size of the registers cannot be read by monitor code. It just decides 40 | // how much memory is snapshotted into the virtualized per-core registers. 41 | void set_dram_region_bitmap_words(size_t new_dram_region_bitmap_words); 42 | 43 | // Copies (virtual) physical memory into a virtualized bitmap register. 44 | void snapshot_dram_region_bitmap(uintptr_t* bitmap_register, 45 | uintptr_t phys_addr); 46 | 47 | }; // namespace sanctum::testing 48 | }; // namespace sanctum 49 | 50 | namespace sanctum { 51 | namespace bare { 52 | 53 | inline size_t read_core_count() { return testing::core_count; } 54 | inline size_t current_core() { return testing::current_core; } 55 | 56 | inline void flush_tlbs() { 57 | testing::core_tlb_flush_count[current_core()] += 1; 58 | } 59 | inline void flush_private_caches() { 60 | testing::core_cache_flush_count[current_core()] += 1; 61 | } 62 | inline void set_cache_index_shift(size_t cache_index_shift) { 63 | testing::core_cache_index_shift[current_core()] = cache_index_shift; 64 | } 65 | inline void set_eptbr(uintptr_t value) { 66 | testing::core_eptbr[current_core()] = value; 67 | } 68 | inline void set_ptbr(uintptr_t value) { 69 | testing::core_ptbr[current_core()] = value; 70 | } 71 | inline void set_epar_base(uintptr_t value) { 72 | testing::core_epar_base[current_core()] = value; 73 | } 74 | inline void set_par_base(uintptr_t value) { 75 | testing::core_par_base[current_core()] = value; 76 | } 77 | inline void set_epar_mask(uintptr_t value) { 78 | testing::core_epar_mask[current_core()] = value; 79 | } 80 | inline void set_par_mask(uintptr_t value) { 81 | testing::core_par_mask[current_core()] = value; 82 | } 83 | inline void set_ev_base(uintptr_t value) { 84 | testing::core_ev_base[current_core()] = value; 85 | } 86 | inline void set_ev_mask(uintptr_t value) { 87 | testing::core_ev_mask[current_core()] = value; 88 | } 89 | inline void set_drb_map(uintptr_t phys_addr) { 90 | testing::snapshot_dram_region_bitmap( 91 | testing::core_drb_map[current_core()], phys_addr); 92 | } 93 | inline void set_edrb_map(uintptr_t phys_addr) { 94 | testing::snapshot_dram_region_bitmap( 95 | testing::core_edrb_map[current_core()], phys_addr); 96 | } 97 | 98 | struct register_state_t { 99 | uintptr_t pc; // x1 100 | uintptr_t stack; // x14 101 | uintptr_t gprs[29]; // x2-13, x15-x31 102 | }; 103 | 104 | }; // namespace sanctum::bare 105 | }; // namespace sanctum 106 | 107 | #endif // !defined(BARE_ARCH_TEST_CPU_CONTEXT_ARCH_H_INCLUDED) 108 | -------------------------------------------------------------------------------- /src/bare/arch/test/gtest_main.cc: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "bare/phys_ptr.h" 10 | 11 | void signal_handler(int sig) { 12 | void *array[16]; 13 | size_t size; 14 | 15 | // get void*'s for all entries on the stack 16 | size = backtrace(array, 16); 17 | 18 | // print out all the frames to stderr 19 | fprintf(stderr, "Error: signal %d:\n", sig); 20 | backtrace_symbols_fd(array, size, STDERR_FILENO); 21 | exit(1); 22 | } 23 | 24 | // This is the entry point for the CLI executable that runs all the monitor's 25 | // unit tests. 26 | int main(int argc, char** argv) { 27 | signal(SIGSEGV, signal_handler); 28 | 29 | // NOTE: The toy computer example in the Sanctum paper uses 256KB of RAM, and 30 | // most monitor tests are based on that example. 31 | sanctum::testing::init_phys_buffer(256 * 1024); 32 | 33 | ::testing::InitGoogleTest(&argc, argv); 34 | return RUN_ALL_TESTS(); 35 | } 36 | -------------------------------------------------------------------------------- /src/bare/arch/test/memory_arch.cc: -------------------------------------------------------------------------------- 1 | #include "../../memory.h" 2 | 3 | using namespace sanctum::bare; 4 | 5 | namespace sanctum { 6 | namespace testing { 7 | 8 | size_t dmar_base, dmar_mask; 9 | size_t dram_size; 10 | size_t cache_levels; 11 | size_t min_cache_index_shift; 12 | size_t max_cache_index_shift; 13 | 14 | bool is_shared_cache[max_cache_levels]; 15 | size_t cache_line_size[max_cache_levels]; 16 | size_t cache_set_count[max_cache_levels]; 17 | 18 | }; // namespace sanctum::testing 19 | }; // namespace sanctum 20 | 21 | -------------------------------------------------------------------------------- /src/bare/arch/test/memory_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_MEMORY_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_MEMORY_ARCH_H_INCLUDED 3 | 4 | #include // Cache metadata operations use assert for bound-checking. 5 | 6 | namespace sanctum { 7 | namespace testing { 8 | 9 | extern size_t dmar_base, dmar_mask; 10 | extern size_t dram_size; 11 | extern size_t cache_levels; 12 | extern size_t min_cache_index_shift, max_cache_index_shift; 13 | constexpr size_t max_cache_levels = 4; 14 | extern bool is_shared_cache[]; 15 | extern size_t cache_line_size[], cache_set_count[]; 16 | 17 | }; // namespace sanctum::testing 18 | }; // namespace sanctum 19 | 20 | namespace sanctum { 21 | namespace bare { 22 | 23 | inline void set_dmar_base(uintptr_t value) { 24 | testing::dmar_base = value; 25 | } 26 | inline void set_dmar_mask(uintptr_t value) { 27 | testing::dmar_mask = value; 28 | } 29 | inline size_t read_dram_size() { return testing::dram_size; } 30 | inline size_t read_cache_levels() { return testing::cache_levels; } 31 | inline bool is_shared_cache(size_t cache_level) { 32 | assert(cache_level < testing::cache_levels); 33 | return testing::is_shared_cache[cache_level]; 34 | } 35 | inline size_t read_cache_line_size(size_t cache_level) { 36 | assert(cache_level < testing::cache_levels); 37 | return testing::cache_line_size[cache_level]; 38 | } 39 | inline size_t read_cache_set_count(size_t cache_level) { 40 | assert(cache_level < testing::cache_levels); 41 | return testing::cache_set_count[cache_level]; 42 | } 43 | inline size_t read_min_cache_index_shift() { 44 | return testing::min_cache_index_shift; 45 | } 46 | inline size_t read_max_cache_index_shift() { 47 | return testing::max_cache_index_shift; 48 | } 49 | template inline void bzero(phys_ptr start, size_t bytes) { 50 | // NOTE: relying on compiler to optimize division to bitwise shift 51 | size_t words = bytes / sizeof(T); 52 | assert(uintptr_t(start + words) <= testing::phys_buffer_size); 53 | 54 | phys_ptr end = start + words; 55 | for (; start != end; start += 1) 56 | *start = static_cast(0); 57 | } 58 | template inline void bcopy(phys_ptr dest, phys_ptr source, 59 | size_t bytes) { 60 | // NOTE: relying on compiler to optimize division to bitwise shift 61 | size_t words = bytes / sizeof(T); 62 | assert(uintptr_t(source + words) <= testing::phys_buffer_size); 63 | assert(uintptr_t(dest + words) <= testing::phys_buffer_size); 64 | // NOTE: checking that the buffers are non-overlapping, per API contract 65 | assert(uintptr_t(source + words) <= uintptr_t(dest) || 66 | uintptr_t(dest + words) <= uintptr_t(source)); 67 | 68 | phys_ptr end = source + words; 69 | for (; source != end; source += 1, dest += 1) 70 | *dest = *source; 71 | } 72 | 73 | }; // namespace sanctum::bare 74 | }; // namespace sanctum 75 | 76 | #endif // !defined(BARE_ARCH_TEST_MEMORY_ARCH_H_INCLUDED) 77 | -------------------------------------------------------------------------------- /src/bare/arch/test/page_tables_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_PAGE_TABLES_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_PAGE_TABLES_ARCH_H_INCLUDED 3 | 4 | #include "../../cpu_context.h" // For current_core(). 5 | #include "../../phys_ptr.h" 6 | 7 | namespace sanctum { 8 | namespace bare { 9 | 10 | static_assert(sizeof(uintptr_t) >= 8, "Sanctum tests require 64-bit pointers"); 11 | 12 | constexpr size_t page_shift() { 13 | return 12; // Same as 64-bit RISC V 14 | } 15 | constexpr size_t page_table_levels() { 16 | return 3; // Same as 64-bit RISC V 17 | } 18 | constexpr inline size_t page_table_shift(size_t level) { 19 | // NOTE: we can't assert(level < page_table_levels()) because constexpr 20 | 21 | // NOTE: we intentionally diverge from RISC V here, because we need the 22 | // quirkiest page table setup possible to test the page walking code 23 | 24 | // Three levels: L1 has 512 entries/table, L2 has 1024 entries/table, L3 has 25 | // 512 entries/table 26 | return (level == 0) ? 9 : ((level == 1) ? 10 : 11); 27 | } 28 | constexpr inline size_t page_table_entry_shift(size_t level) { 29 | // NOTE: we can't assert(level < page_table_levels()) because constexpr 30 | 31 | // NOTE: we intentionally diverge from RISC V here, because we need the 32 | // quirkiest page table setup possible to test the page walking code 33 | 34 | // L1 has 8-byte entries, L2 and L3 have 16-byte entries. 35 | return (level == 0) ? 3 : 4; 36 | } 37 | 38 | inline bool is_valid_page_table_entry(uintptr_t entry_addr, size_t level) { 39 | return *(phys_ptr{entry_addr}) & 1; 40 | } 41 | inline uintptr_t page_table_entry_target(uintptr_t entry_addr, size_t level) { 42 | uintptr_t target_mask = ~((1 << page_shift()) - 1); 43 | return *(phys_ptr{entry_addr}) & target_mask; 44 | } 45 | inline void write_page_table_entry(uintptr_t entry_addr, size_t level, 46 | uintptr_t target, uintptr_t acl) { 47 | uintptr_t acl_mask = (1 << page_shift()) - 1; 48 | acl &= acl_mask; // Mask off non-ACL bits. 49 | acl |= 1; // Force valid to true. 50 | *(phys_ptr{entry_addr}) = target | acl; 51 | } 52 | 53 | }; // namespace sanctum::bare 54 | }; // namespace sanctum 55 | #endif // !definded(BARE_ARCH_TEST_PAGE_TABLES_ARCH_H_INCLUDED) 56 | -------------------------------------------------------------------------------- /src/bare/arch/test/phys_atomics_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | // NOTE: Test-mode flags aren't actually atomic, because of the interactions 8 | // with phys_ptr. Also, there's no threading in tests, so it shouldn't 9 | // matter. 10 | struct atomic_flag { 11 | uintptr_t __flag; 12 | }; 13 | 14 | inline bool atomic_flag_test_and_set(phys_ptr flag) noexcept { 15 | bool old_value = flag->*(&atomic_flag::__flag) != 0; 16 | flag->*(&atomic_flag::__flag) = 1; 17 | return old_value; 18 | } 19 | inline void atomic_flag_clear(phys_ptr flag) noexcept { 20 | flag->*(&atomic_flag::__flag) = 0; 21 | } 22 | 23 | // NOTE: Test-mode atomics aren't actually atomic, just like atomic_flag. 24 | template<> struct atomic { 25 | uintptr_t __value; 26 | }; 27 | 28 | template<> inline void atomic_init(phys_ptr> object, 29 | uintptr_t value) noexcept { 30 | object->*(&atomic::__value) = value; 31 | } 32 | template<> inline uintptr_t atomic_load(phys_ptr> object) 33 | noexcept { 34 | return object->*(&atomic::__value); 35 | } 36 | template<> inline void atomic_store(phys_ptr> object, 37 | uintptr_t value) noexcept { 38 | object->*(&atomic::__value) = value; 39 | } 40 | template<> inline uintptr_t atomic_fetch_add( 41 | phys_ptr> object, uintptr_t value) noexcept { 42 | uintptr_t old_value = object->*(&atomic::__value); 43 | object->*(&atomic::__value) += value; 44 | return old_value; 45 | } 46 | template<> inline uintptr_t atomic_fetch_sub( 47 | phys_ptr> object, uintptr_t value) noexcept { 48 | uintptr_t old_value = object->*(&atomic::__value); 49 | object->*(&atomic::__value) -= value; 50 | return old_value; 51 | } 52 | 53 | }; // namespace sanctum::bare 54 | }; // namespace sanctum 55 | #endif // !definded(BARE_ARCH_RISCV_PHYS_ATOMICS_ARCH_H_INCLUDED) 56 | -------------------------------------------------------------------------------- /src/bare/arch/test/phys_ptr_arch.cc: -------------------------------------------------------------------------------- 1 | #include "../../phys_ptr.h" 2 | 3 | using namespace sanctum::bare; 4 | 5 | namespace sanctum { 6 | namespace testing { 7 | 8 | char* phys_buffer = nullptr; 9 | size_t phys_buffer_size = 0; 10 | 11 | void init_phys_buffer(size_t buffer_size) { 12 | phys_buffer = new char[buffer_size]; 13 | phys_buffer_size = buffer_size; 14 | } 15 | 16 | }; // namespace sanctum::testing 17 | }; // namespace sanctum 18 | -------------------------------------------------------------------------------- /src/bare/arch/test/phys_ptr_arch.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ARCH_TEST_PHYS_PTR_ARCH_H_INCLUDED) 2 | #define BARE_ARCH_TEST_PHYS_PTR_ARCH_H_INCLUDED 3 | 4 | #include // Memory operations use assert for bound-checking. 5 | 6 | namespace sanctum { 7 | namespace testing { 8 | 9 | // For testing purposes, physical memory operations occur in this array. 10 | extern char* phys_buffer; 11 | extern size_t phys_buffer_size; 12 | // This should be called once during test setup. 13 | void init_phys_buffer(size_t buffer_size); 14 | 15 | }; // namespace sanctum::testing 16 | 17 | namespace bare { 18 | 19 | // Specializing physptr_t for uint32_t and uint64_t is guaranteed to cover 20 | // size_t and uintptr_t. 21 | static_assert(sizeof(uintptr_t) == sizeof(uint32_t) || 22 | sizeof(uintptr_t) == sizeof(uint64_t), "uintptr_t isn't 32-bit or 64-bit"); 23 | static_assert(sizeof(size_t) == sizeof(uint32_t) || 24 | sizeof(size_t) == sizeof(uint64_t), "size_t isn't 32-bit or 64-bit"); 25 | 26 | template<> inline phys_ref::operator uint64_t() const { 27 | // NOTE: the assert would be prohibitively expensive in real code, but this 28 | // implementation is only used by unit tests 29 | assert(addr + sizeof(uint64_t) <= testing::phys_buffer_size); 30 | return *(reinterpret_cast(&testing::phys_buffer[addr])); 31 | } 32 | template<> inline phys_ref& phys_ref:: 33 | operator =(const uint64_t& value) { 34 | // NOTE: the assert would be prohibitively expensive in real code, but this 35 | // implementation is only used by unit tests 36 | assert(addr + sizeof(uint64_t) <= testing::phys_buffer_size); 37 | *(reinterpret_cast(&testing::phys_buffer[addr])) = value; 38 | return *this; 39 | } 40 | 41 | template<> inline phys_ref::operator uint32_t() const { 42 | // NOTE: the assert would be prohibitively expensive in real code, but this 43 | // implementation is only used by unit tests 44 | assert(addr + sizeof(uint32_t) <= testing::phys_buffer_size); 45 | return *(reinterpret_cast(&testing::phys_buffer[addr])); 46 | } 47 | template<> inline phys_ref& phys_ref:: 48 | operator =(const uint32_t& value) { 49 | // NOTE: the assert would be prohibitively expensive in real code, but this 50 | // implementation is only used by unit tests 51 | assert(addr + sizeof(uint32_t) <= testing::phys_buffer_size); 52 | *(reinterpret_cast(&testing::phys_buffer[addr])) = value; 53 | return *this; 54 | } 55 | 56 | // TODO(pwnall): figure out why we need a size_t specialization 57 | 58 | template<> inline phys_ref::operator size_t() const { 59 | // NOTE: the assert would be prohibitively expensive in real code, but this 60 | // implementation is only used by unit tests 61 | assert(addr + sizeof(size_t) <= testing::phys_buffer_size); 62 | return *(reinterpret_cast(&testing::phys_buffer[addr])); 63 | } 64 | template<> inline phys_ref& phys_ref:: 65 | operator =(const size_t& value) { 66 | // NOTE: the assert would be prohibitively expensive in real code, but this 67 | // implementation is only used by unit tests 68 | assert(addr + sizeof(size_t) <= testing::phys_buffer_size); 69 | *(reinterpret_cast(&testing::phys_buffer[addr])) = value; 70 | return *this; 71 | } 72 | 73 | }; // namespace sanctum::bare 74 | }; // namespace sanctum 75 | #endif // !definded(BARE_ARCH_TEST_PHYS_PTR_ARCH_H_INCLUDED) 76 | -------------------------------------------------------------------------------- /src/bare/bare.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'variables': { 6 | 'bare_common_sources': [ 7 | 'base_types.h', 8 | 'bit_masking.h', 9 | 'cpu_context.h', 10 | 'memory.h', 11 | 'page_tables.h', 12 | 'phys_atomics.h', 13 | 'phys_ptr.h', 14 | 'traits.h', 15 | ], 16 | }, 17 | 'targets': [ 18 | { 19 | # Used by code that runs on bare metal, e.g. bootrom and monitor. 20 | 'target_name': 'bare', 21 | 'type': '<(library)', 22 | 'sources': [ 23 | '<@(bare_common_sources)', 24 | 'arch/riscv/base_types_arch.h', 25 | 'arch/riscv/bit_masking_arch.h', 26 | 'arch/riscv/cpu_context_arch.h', 27 | 'arch/riscv/memory_arch.h', 28 | 'arch/riscv/page_tables_arch.h', 29 | 'arch/riscv/phys_atomics_arch.h', 30 | 'arch/riscv/phys_ptr_arch.h', 31 | ], 32 | 'target_defaults': { 33 | 'cflags_cc+': [ 34 | '-fno-rtti', 35 | '-fno-exceptions', 36 | ], 37 | 'xcode_settings': { 38 | 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti 39 | 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions 40 | }, 41 | }, 42 | 'direct_dependent_settings': { 43 | 'include_dirs': [ 44 | '..', 45 | 'arch/riscv', 46 | ], 47 | }, 48 | 'all_dependent_settings': { 49 | 'cflags_cc': [ 50 | '-fno-rtti', 51 | '-fno-exceptions', 52 | ], 53 | 'xcode_settings': { 54 | 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti 55 | 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions 56 | }, 57 | }, 58 | }, 59 | { 60 | # Used by unit tests for code that runs on bare metal. 61 | 'target_name': 'bare_testing', 62 | 'type': '<(library)', 63 | 'sources': [ 64 | 'arch/test/base_types_arch.h', 65 | 'arch/test/bit_masking_arch.h', 66 | 'arch/test/cpu_context_arch.cc', 67 | 'arch/test/cpu_context_arch.h', 68 | 'arch/test/memory_arch.cc', 69 | 'arch/test/memory_arch.h', 70 | 'arch/test/page_tables_arch.h', 71 | 'arch/test/phys_atomics_arch.h', 72 | 'arch/test/phys_ptr_arch.cc', 73 | 'arch/test/phys_ptr_arch.h', 74 | 'arch/test/gtest_main.cc', 75 | ], 76 | 'include_dirs': [ 77 | '..', 78 | 'arch/test', 79 | ], 80 | 'dependencies': [ 81 | '../deps/gtest.gyp:gtest', 82 | '../deps/libcxx.gyp:libc++', 83 | ], 84 | 'direct_dependent_settings': { 85 | 'include_dirs': [ 86 | '..', 87 | 'arch/test', 88 | ], 89 | }, 90 | }, 91 | { 92 | # Unit tests for the "bare" library. 93 | 'target_name': 'bare_tests', 94 | 'type': 'executable', 95 | 'sources': [ 96 | 'base_types_test.cc', 97 | 'bit_masking_test.cc', 98 | 'cpu_context_test.cc', 99 | 'memory_test.cc', 100 | 'page_tables_test.cc', 101 | 'phys_atomics_test.cc', 102 | 'phys_ptr_test.cc', 103 | 'traits_test.cc', 104 | ], 105 | 'dependencies': [ 106 | ':bare_testing', 107 | '../deps/gtest.gyp:gtest', 108 | '../deps/libcxx.gyp:libc++', 109 | ], 110 | }, 111 | ], 112 | } 113 | -------------------------------------------------------------------------------- /src/bare/base_types.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_BASE_TYPES_H_INCLUDED) 2 | #define BARE_BASE_TYPES_H_INCLUDED 3 | 4 | // This header defines size_t, uintptr_t, and uint32_t. 5 | // 6 | // These types are specified in the C11 standard. 7 | 8 | // Per-architecture basic type definitions. 9 | #include "base_types_arch.h" 10 | 11 | namespace sanctum { 12 | namespace bare { // sanctum::bare 13 | 14 | static_assert(sizeof(size_t) == sizeof(sizeof(size_t)), 15 | "size_t incorrectly sized"); 16 | static_assert(sizeof(uintptr_t) >= sizeof(void *), "uintptr_t too small"); 17 | static_assert(sizeof(uint64_t) == 8, "uint64_t is not exactly 8 bytes"); 18 | static_assert(sizeof(uint32_t) == 4, "uint32_t is not exactly 4 bytes"); 19 | static_assert(sizeof(uint8_t) == 1, "uint8_t is not exactly one byte"); 20 | 21 | }; // namespace sanctum::bare 22 | }; // namespace sanctum 23 | #endif // !definded(BARE_BASE_TYPES_H_INCLUDED) 24 | -------------------------------------------------------------------------------- /src/bare/base_types_test.cc: -------------------------------------------------------------------------------- 1 | #include "bare/base_types.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::size_t; 6 | using sanctum::bare::uintptr_t; 7 | 8 | TEST(BaseTypesTest, Sizes) { 9 | static_assert(sizeof(uintptr_t) >= sizeof(void *), "uintptr_t too small"); 10 | static_assert(sizeof(size_t) >= 2, "size_t too small"); // C99 lower limit 11 | } 12 | -------------------------------------------------------------------------------- /src/bare/bit_masking.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_BIT_MASKING_H_INCLUDED) 2 | #define BARE_BIT_MASKING_H_INCLUDED 3 | 4 | #include "base_types.h" 5 | #include "page_tables.h" 6 | #include "phys_ptr.h" 7 | 8 | namespace sanctum { 9 | namespace bare { 10 | 11 | // Checks if an argument is a valid range mask. 12 | // 13 | // Valid range masks are written in binary as a sequence of 0s, followed by a 14 | // sequence of 1s. Equivalently, the size of a base/mask range must be a power 15 | // of two. 16 | inline constexpr bool is_valid_range_mask(uintptr_t mask) { 17 | return (mask & (mask + 1)) == 0; 18 | } 19 | // Checks if an address is aligned with regard to a mask. 20 | inline constexpr bool is_aligned_to_mask(uintptr_t address, uintptr_t mask) { 21 | return (address & mask) == 0; 22 | } 23 | // Checks the validity of a base/mask range. 24 | // 25 | // Valid range masks are written in binary as a sequence of 0s, followed by a 26 | // sequence of 1s. Equivalently, the size of a base/mask range must be a power 27 | // of two. 28 | // 29 | // A valid range base has 0s in the positions where the range mask has 1s. 30 | // Equivalently, the range's base must be size-aligned. 31 | inline constexpr bool is_valid_range(uintptr_t base, uintptr_t mask) { 32 | return is_aligned_to_mask(base, mask) && is_valid_range_mask(mask); 33 | } 34 | // Checks if an address is aligned to an address translation page. 35 | inline constexpr bool is_page_aligned(uintptr_t address) { 36 | return is_aligned_to_mask(address, page_size() - 1); 37 | } 38 | 39 | // The smallest number of pages needed to store an amount of memory. 40 | inline constexpr size_t pages_needed_for(size_t memory_size) { 41 | return (memory_size + page_size() - 1) >> page_shift(); 42 | } 43 | 44 | // The number of bits needed to represent a quantity. 45 | // 46 | // This is 1 + the position of the most significant 1 bit in the number. 47 | inline size_t address_bits_for(size_t memory_size) { 48 | // NOTE: we could constexpr this by using a recursive implementation that 49 | // relies on tail call optimization to not generate a large stack, but 50 | // ensuring taill optimization is performed is a big hassle, and it's 51 | // not worth it, given the current usage 52 | size_t bits = 0; 53 | for(memory_size -= 1; memory_size > 0; memory_size >>= 1) 54 | bits += 1; 55 | return bits; 56 | } 57 | 58 | // The smallest power of two that is greater or equal to a quantity. 59 | inline size_t ceil_power_of_two(size_t memory_size) { 60 | return 1 << address_bits_for(memory_size); 61 | } 62 | 63 | // True if the argument is a power of two. 64 | inline constexpr bool is_power_of_two(size_t memory_size) { 65 | return memory_size > 0 && is_valid_range_mask(memory_size - 1); 66 | } 67 | 68 | // Sets or clears a bit in a bitmap. 69 | // 70 | // `value` is true for setting the bit, or false for clearing the bit. 71 | inline void set_bitmap_bit(phys_ptr bitmap, size_t bit, bool value) { 72 | constexpr size_t bits_in_size_t = sizeof(size_t) * 8; 73 | 74 | // NOTE: relying on the compiler to optimize division to bitwise shift 75 | const size_t offset = bit / bits_in_size_t; 76 | const size_t mask = size_t(1) << (bit % bits_in_size_t); 77 | 78 | if (value) 79 | *(bitmap + offset) |= mask; 80 | else 81 | *(bitmap + offset) &= ~mask; 82 | } 83 | // Returns the value of a bit in a bitmap. 84 | inline bool read_bitmap_bit(phys_ptr bitmap, size_t bit) { 85 | constexpr size_t bits_in_size_t = sizeof(size_t) * 8; 86 | 87 | // NOTE: relying on the compiler to optimize division to bitwise shift 88 | const size_t offset = bit / bits_in_size_t; 89 | const size_t mask = size_t(1) << (bit % bits_in_size_t); 90 | 91 | return (*(bitmap + offset) & mask) != 0; 92 | } 93 | 94 | // True if this is a big-endian architecture. 95 | constexpr bool is_big_endian(); 96 | 97 | // Reverses the order of the bytes in the argument. 98 | // 99 | // This is used to convert between big-endian and little-endian values. 100 | // 101 | // The template is guaranteed to be specialized for uint32_t. 102 | template constexpr T reverse_bytes(T value); 103 | template<> constexpr inline uint32_t reverse_bytes(uint32_t value) { 104 | return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | 105 | ((value >> 24) & 0xff) | ((value >> 8) & 0xff00); 106 | } 107 | 108 | }; // namespace sanctum::bare 109 | }; // namespace sanctum 110 | 111 | // Architecture-dependent bit-masking definitions. 112 | #include "bit_masking_arch.h" 113 | 114 | #endif // !defined(BARE_BIT_MASKING_H_INCLUDED) 115 | -------------------------------------------------------------------------------- /src/bare/bit_masking_test.cc: -------------------------------------------------------------------------------- 1 | #include "bit_masking.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::address_bits_for; 6 | using sanctum::bare::ceil_power_of_two; 7 | using sanctum::bare::is_aligned_to_mask; 8 | using sanctum::bare::is_page_aligned; 9 | using sanctum::bare::is_power_of_two; 10 | using sanctum::bare::is_valid_range; 11 | using sanctum::bare::is_valid_range_mask; 12 | using sanctum::bare::pages_needed_for; 13 | using sanctum::bare::phys_ptr; 14 | using sanctum::bare::read_bitmap_bit; 15 | using sanctum::bare::reverse_bytes; 16 | using sanctum::bare::set_bitmap_bit; 17 | using sanctum::testing::phys_buffer; 18 | using sanctum::testing::phys_buffer_size; 19 | 20 | TEST(BitMaskingTest, IsValidRangeMask) { 21 | ASSERT_EQ(true, is_valid_range_mask(0x00)); 22 | ASSERT_EQ(true, is_valid_range_mask(0x01)); 23 | ASSERT_EQ(true, is_valid_range_mask(0x03)); 24 | ASSERT_EQ(true, is_valid_range_mask(0x07)); 25 | ASSERT_EQ(true, is_valid_range_mask(0x0F)); 26 | ASSERT_EQ(true, is_valid_range_mask(0x1F)); 27 | ASSERT_EQ(true, is_valid_range_mask(0x3F)); 28 | ASSERT_EQ(true, is_valid_range_mask(0x7F)); 29 | ASSERT_EQ(true, is_valid_range_mask(0xFF)); 30 | 31 | ASSERT_EQ(false, is_valid_range_mask(0x02)); 32 | ASSERT_EQ(false, is_valid_range_mask(0x04)); 33 | ASSERT_EQ(false, is_valid_range_mask(0x05)); 34 | ASSERT_EQ(false, is_valid_range_mask(0x06)); 35 | ASSERT_EQ(false, is_valid_range_mask(0x08)); 36 | ASSERT_EQ(false, is_valid_range_mask(0x09)); 37 | ASSERT_EQ(false, is_valid_range_mask(0x0A)); 38 | ASSERT_EQ(false, is_valid_range_mask(0x0E)); 39 | ASSERT_EQ(false, is_valid_range_mask(0xFE)); 40 | } 41 | 42 | TEST(BitMaskingTest, IsAlignedToMask) { 43 | ASSERT_EQ(true, is_aligned_to_mask(0x10, 0x0F)); 44 | ASSERT_EQ(false, is_aligned_to_mask(0x18, 0x0F)); 45 | ASSERT_EQ(true, is_aligned_to_mask(0x100, 0xFF)); 46 | ASSERT_EQ(false, is_aligned_to_mask(0x101, 0xFF)); 47 | ASSERT_EQ(true, is_aligned_to_mask(0x200, 0xFF)); 48 | ASSERT_EQ(false, is_aligned_to_mask(0x220, 0xFF)); 49 | ASSERT_EQ(true, is_aligned_to_mask(0x1000, 0xFF)); 50 | } 51 | 52 | TEST(BitMaskingTest, IsValidRange) { 53 | ASSERT_EQ(true, is_valid_range(0x10, 0x0F)); 54 | ASSERT_EQ(false, is_valid_range(0x10, 0x08)); 55 | ASSERT_EQ(false, is_valid_range(0x11, 0x0F)); 56 | ASSERT_EQ(false, is_valid_range(0x11, 0x08)); 57 | } 58 | 59 | TEST(BitMaskingTest, IsPageAligned) { 60 | ASSERT_EQ(true, is_page_aligned(0)); 61 | ASSERT_EQ(false, is_page_aligned(1)); 62 | ASSERT_EQ(true, is_page_aligned(0x1000)); 63 | ASSERT_EQ(false, is_page_aligned(0x1001)); 64 | ASSERT_EQ(true, is_page_aligned(0x2000)); 65 | ASSERT_EQ(false, is_page_aligned(0x2001)); 66 | ASSERT_EQ(true, is_page_aligned(0x4000)); 67 | ASSERT_EQ(false, is_page_aligned(0x4800)); 68 | } 69 | 70 | TEST(BitMaskingTest, PagesNeededFor) { 71 | ASSERT_EQ(0, pages_needed_for(0)); 72 | ASSERT_EQ(1, pages_needed_for(1)); 73 | ASSERT_EQ(1, pages_needed_for(0x800)); 74 | ASSERT_EQ(1, pages_needed_for(0xFFF)); 75 | ASSERT_EQ(1, pages_needed_for(0x1000)); 76 | ASSERT_EQ(2, pages_needed_for(0x1001)); 77 | ASSERT_EQ(2, pages_needed_for(0x1800)); 78 | ASSERT_EQ(2, pages_needed_for(0x1FFF)); 79 | ASSERT_EQ(2, pages_needed_for(0x2000)); 80 | ASSERT_EQ(3, pages_needed_for(0x2001)); 81 | ASSERT_EQ(4, pages_needed_for(0x3FFF)); 82 | ASSERT_EQ(4, pages_needed_for(0x4000)); 83 | ASSERT_EQ(5, pages_needed_for(0x4001)); 84 | ASSERT_EQ(16, pages_needed_for(65535)); 85 | ASSERT_EQ(16, pages_needed_for(65536)); 86 | ASSERT_EQ(17, pages_needed_for(65537)); 87 | } 88 | 89 | TEST(BitMaskingTest, AddressBitsFor) { 90 | ASSERT_EQ(0, address_bits_for(1)); 91 | ASSERT_EQ(1, address_bits_for(2)); 92 | ASSERT_EQ(2, address_bits_for(3)); 93 | ASSERT_EQ(2, address_bits_for(4)); 94 | ASSERT_EQ(3, address_bits_for(5)); 95 | ASSERT_EQ(3, address_bits_for(8)); 96 | ASSERT_EQ(4, address_bits_for(9)); 97 | ASSERT_EQ(4, address_bits_for(16)); 98 | ASSERT_EQ(16, address_bits_for(65535)); 99 | ASSERT_EQ(16, address_bits_for(65536)); 100 | } 101 | 102 | TEST(BitMaskingTest, CeilPowerOfTwo) { 103 | ASSERT_EQ(1, ceil_power_of_two(1)); 104 | ASSERT_EQ(2, ceil_power_of_two(2)); 105 | ASSERT_EQ(4, ceil_power_of_two(3)); 106 | ASSERT_EQ(4, ceil_power_of_two(4)); 107 | ASSERT_EQ(8, ceil_power_of_two(5)); 108 | ASSERT_EQ(8, ceil_power_of_two(8)); 109 | ASSERT_EQ(16, ceil_power_of_two(9)); 110 | ASSERT_EQ(16, ceil_power_of_two(16)); 111 | ASSERT_EQ(65536, ceil_power_of_two(65535)); 112 | ASSERT_EQ(65536, ceil_power_of_two(65536)); 113 | } 114 | 115 | TEST(BitMaskingTest, IsPowerOfTwo) { 116 | static_assert(true == is_power_of_two(1), "is_power_of_two(1)"); 117 | static_assert(true == is_power_of_two(2), "is_power_of_two(2)"); 118 | static_assert(false == is_power_of_two(3), "is_power_of_two(3)"); 119 | static_assert(true == is_power_of_two(4), "is_power_of_two(4)"); 120 | static_assert(false == is_power_of_two(5), "is_power_of_two(5)"); 121 | static_assert(false == is_power_of_two(6), "is_power_of_two(6)"); 122 | static_assert(false == is_power_of_two(7), "is_power_of_two(7)"); 123 | static_assert(true == is_power_of_two(8), "is_power_of_two(8)"); 124 | static_assert(false == is_power_of_two(9), "is_power_of_two(9)"); 125 | static_assert(false == is_power_of_two(15), "is_power_of_two(15)"); 126 | static_assert(true == is_power_of_two(16), "is_power_of_two(16)"); 127 | static_assert(false == is_power_of_two(17), "is_power_of_two(17)"); 128 | static_assert(false == is_power_of_two(65535), "is_power_of_two(65535)"); 129 | static_assert(true == is_power_of_two(65536), "is_power_of_two(65536)"); 130 | } 131 | 132 | TEST(BitMaskingTest, ReverseBytes) { 133 | ASSERT_EQ(0xff000000U, reverse_bytes(0xffU)); 134 | ASSERT_EQ(0xff0000U, reverse_bytes(0xff00U)); 135 | ASSERT_EQ(0xff00U, reverse_bytes(0xff0000U)); 136 | ASSERT_EQ(0xffU, reverse_bytes(0xff000000U)); 137 | ASSERT_EQ(0U, reverse_bytes(0U)); 138 | 139 | ASSERT_EQ(0x12345678U, reverse_bytes(0x78563412U)); 140 | ASSERT_EQ(0x78563412U, reverse_bytes(0x12345678U)); 141 | 142 | ASSERT_EQ(0xfedcba98U, reverse_bytes(0x98badcfeU)); 143 | ASSERT_EQ(0x98badcfeu, reverse_bytes(0xfedcba98U)); 144 | } 145 | 146 | TEST(BitMaskingTest, ReadSetBitmapBit) { 147 | constexpr uintptr_t addr = 160, addr2 = 200; 148 | constexpr uintptr_t zero_addr = 0; 149 | ASSERT_LE(256, phys_buffer_size); 150 | memset(phys_buffer, 0, 256); 151 | *(reinterpret_cast(phys_buffer + addr)) = 0xFFFF; 152 | 153 | phys_ptr ptr{addr}; 154 | ASSERT_EQ(true, read_bitmap_bit(ptr, 0)); 155 | ASSERT_EQ(true, read_bitmap_bit(ptr, 1)); 156 | ASSERT_EQ(true, read_bitmap_bit(ptr, 2)); 157 | ASSERT_EQ(true, read_bitmap_bit(ptr, 3)); 158 | 159 | set_bitmap_bit(ptr, 0, false); 160 | ASSERT_EQ(0xFFFE, *ptr); 161 | ASSERT_EQ(false, read_bitmap_bit(ptr, 0)); 162 | ASSERT_EQ(true, read_bitmap_bit(ptr, 1)); 163 | ASSERT_EQ(true, read_bitmap_bit(ptr, 2)); 164 | ASSERT_EQ(true, read_bitmap_bit(ptr, 3)); 165 | 166 | set_bitmap_bit(ptr, 3, false); 167 | ASSERT_EQ(0xFFF6, *ptr); 168 | ASSERT_EQ(false, read_bitmap_bit(ptr, 0)); 169 | ASSERT_EQ(true, read_bitmap_bit(ptr, 1)); 170 | ASSERT_EQ(true, read_bitmap_bit(ptr, 2)); 171 | ASSERT_EQ(false, read_bitmap_bit(ptr, 3)); 172 | 173 | set_bitmap_bit(ptr, 0, true); 174 | ASSERT_EQ(0xFFF7, *ptr); 175 | ASSERT_EQ(true, read_bitmap_bit(ptr, 0)); 176 | ASSERT_EQ(true, read_bitmap_bit(ptr, 1)); 177 | ASSERT_EQ(true, read_bitmap_bit(ptr, 2)); 178 | ASSERT_EQ(false, read_bitmap_bit(ptr, 3)); 179 | 180 | phys_ptr ptr2{addr2}; 181 | set_bitmap_bit(ptr, 320, true); 182 | ASSERT_EQ(1, *ptr2); 183 | ASSERT_EQ(false, read_bitmap_bit(ptr, 319)); 184 | ASSERT_EQ(true, read_bitmap_bit(ptr, 320)); 185 | ASSERT_EQ(false, read_bitmap_bit(ptr, 321)); 186 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 187 | 188 | set_bitmap_bit(ptr, 321, true); 189 | ASSERT_EQ(3, *ptr2); 190 | ASSERT_EQ(false, read_bitmap_bit(ptr, 319)); 191 | ASSERT_EQ(true, read_bitmap_bit(ptr, 320)); 192 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 193 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 194 | 195 | set_bitmap_bit(ptr, 320, false); 196 | ASSERT_EQ(2, *ptr2); 197 | ASSERT_EQ(false, read_bitmap_bit(ptr, 319)); 198 | ASSERT_EQ(false, read_bitmap_bit(ptr, 320)); 199 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 200 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 201 | 202 | size_t msb = 1; 203 | while(true) { 204 | size_t msb2 = msb << 1; 205 | if (msb2 == 0) 206 | break; 207 | msb = msb2; 208 | } 209 | set_bitmap_bit(ptr, 319, true); 210 | ASSERT_EQ(msb, *(ptr2 - 1)); 211 | ASSERT_EQ(false, read_bitmap_bit(ptr, 317)); 212 | ASSERT_EQ(false, read_bitmap_bit(ptr, 318)); 213 | ASSERT_EQ(true, read_bitmap_bit(ptr, 319)); 214 | ASSERT_EQ(false, read_bitmap_bit(ptr, 320)); 215 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 216 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 217 | 218 | set_bitmap_bit(ptr, 320, true); 219 | ASSERT_EQ(msb, *(ptr2 - 1)); 220 | ASSERT_EQ(3, *ptr2); 221 | ASSERT_EQ(false, read_bitmap_bit(ptr, 317)); 222 | ASSERT_EQ(false, read_bitmap_bit(ptr, 318)); 223 | ASSERT_EQ(true, read_bitmap_bit(ptr, 319)); 224 | ASSERT_EQ(true, read_bitmap_bit(ptr, 320)); 225 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 226 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 227 | 228 | set_bitmap_bit(ptr, 318, true); 229 | ASSERT_EQ(size_t(msb + size_t(msb >> 1)), *(ptr2 - 1)); 230 | ASSERT_EQ(3, *ptr2); 231 | ASSERT_EQ(false, read_bitmap_bit(ptr, 317)); 232 | ASSERT_EQ(true, read_bitmap_bit(ptr, 318)); 233 | ASSERT_EQ(true, read_bitmap_bit(ptr, 319)); 234 | ASSERT_EQ(true, read_bitmap_bit(ptr, 320)); 235 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 236 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 237 | 238 | set_bitmap_bit(ptr, 319, false); 239 | ASSERT_EQ(size_t(msb >> 1), *(ptr2 - 1)); 240 | ASSERT_EQ(3, *ptr2); 241 | ASSERT_EQ(false, read_bitmap_bit(ptr, 317)); 242 | ASSERT_EQ(true, read_bitmap_bit(ptr, 318)); 243 | ASSERT_EQ(false, read_bitmap_bit(ptr, 319)); 244 | ASSERT_EQ(true, read_bitmap_bit(ptr, 320)); 245 | ASSERT_EQ(true, read_bitmap_bit(ptr, 321)); 246 | ASSERT_EQ(false, read_bitmap_bit(ptr, 322)); 247 | 248 | *ptr = 0; 249 | *ptr2 = 0; 250 | *(ptr2 - 1) = 0; 251 | } 252 | -------------------------------------------------------------------------------- /src/bare/cpu_context.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_CPU_CONTEXT_H_INCLUDED) 2 | #define BARE_CPU_CONTEXT_H_INCLUDED 3 | 4 | #include "base_types.h" 5 | 6 | namespace sanctum { 7 | namespace bare { 8 | 9 | // Obtains the number of cores installed in the system. 10 | // 11 | // The implementation may be very slow, so the return value should be cached. 12 | // 13 | // The implementation may use privileged instructions. 14 | size_t read_core_count(); 15 | 16 | // Cores are numbered starting from 0. 17 | size_t current_core(); 18 | 19 | // Flush all TLBs on the current core. 20 | // 21 | // This does not flush any cache. 22 | void flush_tlbs(); 23 | 24 | // Flush all the caches belonging to the current core. 25 | // 26 | // This does not flush TLBs, and does not flush the shared last-level cache. 27 | void flush_private_caches(); 28 | 29 | // Sets the core's cache index shift. 30 | // 31 | // This must be set to identical values on all cores. Undefined behavior will 32 | // occur otherwise. 33 | // 34 | // This can only be issued by the security monitor. 35 | void set_cache_index_shift(size_t cache_index_shift); 36 | 37 | // Sets the EPTBR (enclave page table base register). 38 | // 39 | // This can only be issued by the security monitor. An invalid DRAM address 40 | // will lock up or reboot the machine. 41 | void set_eptbr(uintptr_t value); 42 | 43 | // Sets the PTBR (page table base register). 44 | // 45 | // This can only be issued by the security monitor. An invalid DRAM address 46 | // will lock up or reboot the machine. 47 | void set_ptbr(uintptr_t value); 48 | 49 | // Sets the EPARBASE (enclave protected address range base) register. 50 | // 51 | // This can only be issued by the security monitor. 52 | void set_epar_base(uintptr_t value); 53 | 54 | // Sets the PARBASE (protected address range base) register. 55 | // 56 | // This can only be issued by the security monitor. 57 | void set_par_base(uintptr_t value); 58 | 59 | // Sets the EPARMASK (enclave protected address range mask) register. 60 | // 61 | // This can only be issued by the security monitor. 62 | void set_epar_mask(uintptr_t value); 63 | 64 | // Sets the PARMASK (protected address range mask) register. 65 | // 66 | // This can only be issued by the security monitor. 67 | void set_par_mask(uintptr_t value); 68 | 69 | // Sets the EVBASE (enclave virtual address base register). 70 | // 71 | // This can only be issued by the security monitor. 72 | void set_ev_base(uintptr_t value); 73 | 74 | // Sets the EVMASK (enclave virtual address mask register). 75 | // 76 | // This can only be issued by the security monitor. 77 | void set_ev_mask(uintptr_t value); 78 | 79 | // Loads the DRBMAP (DRAM region bitmap) register from memory. 80 | void set_drb_map(uintptr_t phys_addr); 81 | 82 | // Loads the EDRBMAP (enclave DRAM region bitmap) register from memory. 83 | void set_edrb_map(uintptr_t phys_addr); 84 | 85 | // The execution context saved when the monitor is invoked. 86 | // 87 | // The structure is guaranteed to have the following members: 88 | // pc - program counter (RIP on x86) 89 | // sp - stack pointer 90 | struct register_state_t; 91 | 92 | }; // namespace sanctum::bare 93 | }; // namespace sanctum 94 | 95 | // Per-architecture definitions and operations. 96 | #include "cpu_context_arch.h" 97 | 98 | #endif // !defined(BARE_CPU_CONTEXT_H_INCLUDED) 99 | -------------------------------------------------------------------------------- /src/bare/memory.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_MEMORY_H_INCLUDED) 2 | #define BARE_MEMORY_H_INCLUDED 3 | 4 | #include "base_types.h" 5 | #include "phys_ptr.h" 6 | 7 | namespace sanctum { 8 | namespace bare { 9 | 10 | // Sets the DMARBASE (DMA range base) register in the DMA master. 11 | // 12 | // This can only be called by the security monitor. 13 | void set_dmar_base(uintptr_t value); 14 | 15 | // Sets the DMARMASK (DMA range mask) register in the DMA master. 16 | // 17 | // This can only be called by the security monitor. 18 | void set_dmar_mask(uintptr_t value); 19 | 20 | // Obtains the DRAM size from the memory subsystem. 21 | // 22 | // The implementation may be very slow, so the return value should be cached. 23 | // 24 | // The implementation may use privileged instructions. 25 | size_t read_dram_size(); 26 | 27 | // The number of levels of cache memory. 28 | // 29 | // The implementation may be very slow, so the return value should be cached. 30 | // 31 | // The implementation may use privileged instructions. 32 | size_t read_cache_levels(); 33 | 34 | // True if the given cache level is shared among cores. 35 | // 36 | // The implementation may be very slow, so the return value should be cached. 37 | // 38 | // The implementation may use privileged instructions. 39 | bool is_shared_cache(size_t cache_level); 40 | 41 | // The size of a cache line at a given level. 42 | // 43 | // The implementation may be very slow, so the return value should be cached. 44 | // 45 | // The implementation may use privileged instructions. 46 | size_t read_cache_line_size(size_t cache_level); 47 | 48 | // The number of cache sets at a given level. 49 | // 50 | // The implementation may be very slow, so the return value should be cached. 51 | // 52 | // The implementation may use privileged instructions. 53 | size_t read_cache_set_count(size_t cache_level); 54 | 55 | // The maximum value of the cache index shift for the platform. 56 | size_t read_min_cache_index_shift(); 57 | 58 | // The minimum value of the cache index shift for the platform. 59 | size_t read_max_cache_index_shift(); 60 | 61 | // Fills a buffer in physical memory with zeros. 62 | // 63 | // In order to allow for optimized assembly implementations, both the starting 64 | // address and buffer size must be a multiple of the cache line size. 65 | template void bzero(phys_ptr start, size_t bytes); 66 | 67 | // Copies data between two non-overlaping buffers in physical memory. 68 | // 69 | // In order to allow for optimized assembly implementations, both addresses, as 70 | // well as the buffer size must be a multiple of the cache line size. 71 | template void bcopy(phys_ptr dest, phys_ptr source, 72 | size_t bytes); 73 | 74 | }; // namespace sanctum::bare 75 | }; // namespace sanctum 76 | 77 | // Per-architecture implementations of memory operations. 78 | #include "memory_arch.h" 79 | 80 | #endif // !defined(BARE_MEMORY_H_INCLUDED) 81 | -------------------------------------------------------------------------------- /src/bare/memory_test.cc: -------------------------------------------------------------------------------- 1 | #include "bare/memory.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::is_shared_cache; 6 | using sanctum::bare::phys_ptr; 7 | using sanctum::bare::read_cache_levels; 8 | using sanctum::bare::read_cache_line_size; 9 | using sanctum::bare::read_cache_set_count; 10 | using sanctum::bare::read_dram_size; 11 | using sanctum::bare::read_min_cache_index_shift; 12 | using sanctum::bare::read_max_cache_index_shift; 13 | using sanctum::bare::set_dmar_base; 14 | using sanctum::bare::set_dmar_mask; 15 | using sanctum::testing::phys_buffer; 16 | using sanctum::testing::phys_buffer_size; 17 | 18 | TEST(MemoryTest, SetDmarBase) { 19 | set_dmar_base(0x21000000); 20 | ASSERT_EQ(0x21000000, sanctum::testing::dmar_base); 21 | set_dmar_base(0); 22 | ASSERT_EQ(0, sanctum::testing::dmar_base); 23 | } 24 | 25 | TEST(MemoryTest, SetDmarMask) { 26 | set_dmar_mask(0x1fffff); 27 | ASSERT_EQ(0x1fffff, sanctum::testing::dmar_mask); 28 | set_dmar_mask(0); 29 | ASSERT_EQ(0, sanctum::testing::dmar_mask); 30 | } 31 | 32 | TEST(MemoryTest, ReadDramSize) { 33 | sanctum::testing::dram_size = 1 << 30; 34 | ASSERT_EQ(1 << 30, read_dram_size()); 35 | 36 | sanctum::testing::dram_size = 1 << 28; 37 | ASSERT_EQ(1 << 28, read_dram_size()); 38 | } 39 | 40 | TEST(MemoryTest, ReadCacheLevels) { 41 | sanctum::testing::cache_levels = 3; 42 | ASSERT_EQ(3, read_cache_levels()); 43 | 44 | sanctum::testing::cache_levels = 2; 45 | ASSERT_EQ(2, read_cache_levels()); 46 | } 47 | 48 | TEST(MemoryTest, IsSharedCache) { 49 | sanctum::testing::cache_levels = 3; 50 | sanctum::testing::is_shared_cache[0] = false; 51 | sanctum::testing::is_shared_cache[1] = false; 52 | sanctum::testing::is_shared_cache[2] = true; 53 | ASSERT_EQ(false, is_shared_cache(0)); 54 | ASSERT_EQ(false, is_shared_cache(1)); 55 | ASSERT_EQ(true, is_shared_cache(2)); 56 | } 57 | 58 | TEST(MemoryTest, ReadCacheLineSize) { 59 | sanctum::testing::cache_levels = 3; 60 | sanctum::testing::cache_line_size[0] = 64; 61 | sanctum::testing::cache_line_size[1] = 32; 62 | sanctum::testing::cache_line_size[2] = 16; 63 | ASSERT_EQ(64, read_cache_line_size(0)); 64 | ASSERT_EQ(32, read_cache_line_size(1)); 65 | ASSERT_EQ(16, read_cache_line_size(2)); 66 | } 67 | 68 | TEST(MemoryTest, ReadCacheSetCount) { 69 | sanctum::testing::cache_levels = 3; 70 | sanctum::testing::cache_set_count[0] = 64; 71 | sanctum::testing::cache_set_count[1] = 512; 72 | sanctum::testing::cache_set_count[2] = 8192; 73 | ASSERT_EQ(64, read_cache_set_count(0)); 74 | ASSERT_EQ(512, read_cache_set_count(1)); 75 | ASSERT_EQ(8192, read_cache_set_count(2)); 76 | } 77 | 78 | TEST(MemoryTest, ReadMinCacheIndexShift) { 79 | sanctum::testing::min_cache_index_shift = 9; 80 | ASSERT_EQ(9, read_min_cache_index_shift()); 81 | 82 | sanctum::testing::min_cache_index_shift = 7; 83 | ASSERT_EQ(7, read_min_cache_index_shift()); 84 | } 85 | 86 | TEST(MemoryTest, ReadMaxCacheIndexShift) { 87 | sanctum::testing::max_cache_index_shift = 13; 88 | ASSERT_EQ(13, read_max_cache_index_shift()); 89 | 90 | sanctum::testing::max_cache_index_shift = 11; 91 | ASSERT_EQ(11, read_max_cache_index_shift()); 92 | } 93 | 94 | TEST(MemoryTest, Bzero) { 95 | ASSERT_LE(256, phys_buffer_size); 96 | memset(phys_buffer, 0xcc, 256); 97 | 98 | phys_ptr ptr{64}; 99 | bzero(ptr, 0); 100 | ASSERT_NE(0, *(ptr - 2)); 101 | ASSERT_NE(0, *(ptr - 1)); 102 | ASSERT_NE(0, *ptr); 103 | ASSERT_NE(0, *(ptr + 1)); 104 | ASSERT_NE(0, *(ptr + 2)); 105 | 106 | bzero(ptr, sizeof(size_t)); 107 | ASSERT_NE(0, *(ptr - 2)); 108 | ASSERT_NE(0, *(ptr - 1)); 109 | ASSERT_EQ(0, *ptr); 110 | ASSERT_NE(0, *(ptr + 1)); 111 | ASSERT_NE(0, *(ptr + 1)); 112 | ASSERT_NE(0, *(ptr + 2)); 113 | 114 | bzero(ptr, 96); 115 | ASSERT_NE(0, *(ptr - 2)); 116 | ASSERT_NE(0, *(ptr - 1)); 117 | ASSERT_EQ(0, *ptr); 118 | ASSERT_EQ(0, *(ptr + 1)); 119 | ASSERT_EQ(0, *(ptr + 2)); 120 | 121 | phys_ptr ptr2{64 + 96}; 122 | ASSERT_NE(0, *ptr2); 123 | ASSERT_NE(0, *(ptr2 + 1)); 124 | ASSERT_NE(0, *(ptr2 + 2)); 125 | ASSERT_EQ(0, *(ptr2 - 1)); 126 | ASSERT_EQ(0, *(ptr2 - 2)); 127 | 128 | memset(phys_buffer, 0, 256); 129 | } 130 | 131 | TEST(MemoryTest, Bcopy) { 132 | ASSERT_LE(256, phys_buffer_size); 133 | memset(phys_buffer, 0xcc, 128); 134 | memset(phys_buffer + 128, 0xdd, 128); 135 | 136 | phys_ptr ptr1{32}; 137 | phys_ptr ptr2{160}; 138 | size_t value1 = *ptr1, value2 = *ptr2; 139 | 140 | bcopy(ptr1, ptr2, 0); 141 | ASSERT_EQ(value1, *(ptr1 - 2)); 142 | ASSERT_EQ(value1, *(ptr1 - 1)); 143 | ASSERT_EQ(value1, *ptr1); 144 | ASSERT_EQ(value1, *(ptr1 + 1)); 145 | ASSERT_EQ(value1, *(ptr1 + 2)); 146 | 147 | bcopy(ptr1, ptr2, sizeof(size_t)); 148 | ASSERT_EQ(value1, *(ptr1 - 2)); 149 | ASSERT_EQ(value1, *(ptr1 - 1)); 150 | ASSERT_EQ(value2, *ptr1); 151 | ASSERT_EQ(value1, *(ptr1 + 1)); 152 | ASSERT_EQ(value1, *(ptr1 + 2)); 153 | 154 | bcopy(ptr1, ptr2, 48); 155 | ASSERT_EQ(value1, *(ptr1 - 2)); 156 | ASSERT_EQ(value1, *(ptr1 - 1)); 157 | ASSERT_EQ(value2, *ptr1); 158 | ASSERT_EQ(value2, *(ptr1 + 1)); 159 | ASSERT_EQ(value2, *(ptr1 + 2)); 160 | 161 | phys_ptr ptr12{32 + 48}; 162 | ASSERT_EQ(value2, *(ptr12 - 2)); 163 | ASSERT_EQ(value2, *(ptr12 - 1)); 164 | ASSERT_EQ(value1, *ptr12); 165 | ASSERT_EQ(value1, *(ptr12 + 1)); 166 | ASSERT_EQ(value1, *(ptr12 + 2)); 167 | 168 | memset(phys_buffer, 0, 256); 169 | } 170 | -------------------------------------------------------------------------------- /src/bare/page_tables.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_PAGE_TABLES_H_INCLUDED) 2 | #define BARE_PAGE_TABLES_H_INCLUDED 3 | 4 | #include "base_types.h" 5 | 6 | namespace sanctum { 7 | namespace bare { 8 | 9 | // Number of bits in an address that don't undergo address translation. 10 | constexpr size_t page_shift(); 11 | 12 | // Number of page table levels. 13 | // 14 | // We number levels from 0, which is assigned to the page table leaves. 15 | // 16 | // For example, in x86_64, the levels are 0 (PT), 1 (PD), 2 (PDPT), 3 (PML4), 17 | // and page_table_levels is 4. 18 | constexpr size_t page_table_levels(); 19 | 20 | // The number of address bits translated by a page table level. 21 | // 22 | // On most architectures, the number of bits is not level-dependent. 23 | constexpr inline size_t page_table_shift(size_t level); 24 | 25 | // Log2 of the size of a page table entry, at a given level, in bytes. 26 | // 27 | // On most architectures, each page table entry holds a pointer whose least 28 | // significant bits are reused for access control flags. Therefore, this is 29 | // generally log2(sizeof(uintptr_t)). 30 | constexpr inline size_t page_table_entry_shift(size_t level); 31 | 32 | // Reads the valid (a.k.a. present) bit in a page table entry. 33 | // 34 | // Page entries with the valid bit unset have no other valid fields. 35 | bool is_valid_page_table_entry(uintptr_t entry_addr, size_t level); 36 | 37 | // Reads the destination pointer in a page table entry. 38 | // 39 | // The pointer can be the physical address of the next level page table, or the 40 | // physical address for a virtual address. 41 | uintptr_t page_table_entry_target(uintptr_t entry_addr, size_t level); 42 | 43 | // Writes a page table entry. 44 | // 45 | // `target` points to the next level page table, or has the physical address 46 | // that comes from the translation. `acl` has platform-dependent access control 47 | // flags, such as W (writable) and NX (not-executable). 48 | // 49 | // Before being combined with `target`, the `acl` value is masked against a 50 | // value that only leaves in bits with known access control roles. For example, 51 | // the valid / present bit will be masked off of the ACL. 52 | void write_page_table_entry(uintptr_t entry_addr, size_t level, 53 | uintptr_t target, uintptr_t acl); 54 | 55 | }; // namespace sanctum::bare 56 | }; // namespace sanctum 57 | 58 | // Per-architecture page table constants and operations. 59 | #include "page_tables_arch.h" 60 | 61 | namespace sanctum { 62 | namespace bare { 63 | 64 | // Page size in bytes. 65 | constexpr size_t page_size() { 66 | return 1 << page_shift(); 67 | } 68 | 69 | // The size of a page table entry, at a given level, in bytes. 70 | constexpr inline size_t page_table_entry_size(size_t level) { 71 | return 1 << page_table_entry_shift(level); 72 | } 73 | 74 | // The number of page table entries at a given level. 75 | constexpr inline size_t page_table_entries(size_t level) { 76 | return 1 << page_table_shift(level); 77 | } 78 | 79 | // The size of a page table at a given level, in bytes. 80 | constexpr inline size_t page_table_size(size_t level) { 81 | return page_table_entries(level) * page_table_entry_size(level); 82 | } 83 | 84 | // The size of a page table at a given level, in pages. 85 | constexpr inline size_t page_table_pages(size_t level) { 86 | return page_table_size(level) >> page_shift(); 87 | } 88 | 89 | // Used to implement page_table_translated_bits. 90 | constexpr inline size_t __page_table_translated_bits(size_t level, size_t sum) { 91 | return (level == page_table_levels()) ? sum : 92 | __page_table_translated_bits(level + 1, sum + page_table_shift(level)); 93 | } 94 | 95 | // The total number of bits translated by the page table. 96 | // 97 | // This should be optimized to a constant by the compiler. 98 | constexpr inline size_t page_table_translated_bits() { 99 | return __page_table_translated_bits(0, page_shift()); 100 | } 101 | 102 | }; // namespace sanctum::bare 103 | }; // namespace sanctum 104 | #endif // !defined(BARE_PAGE_TABLES_H_INCLUDED) 105 | -------------------------------------------------------------------------------- /src/bare/page_tables_test.cc: -------------------------------------------------------------------------------- 1 | #include "bare/page_tables.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::is_valid_page_table_entry; 6 | using sanctum::bare::page_size; 7 | using sanctum::bare::page_shift; 8 | using sanctum::bare::page_table_entries; 9 | using sanctum::bare::page_table_entry_shift; 10 | using sanctum::bare::page_table_entry_size; 11 | using sanctum::bare::page_table_entry_target; 12 | using sanctum::bare::page_table_levels; 13 | using sanctum::bare::page_table_pages; 14 | using sanctum::bare::page_table_shift; 15 | using sanctum::bare::page_table_size; 16 | using sanctum::bare::page_table_translated_bits; 17 | using sanctum::bare::size_t; 18 | using sanctum::bare::uintptr_t; 19 | using sanctum::bare::write_page_table_entry; 20 | using sanctum::testing::phys_buffer; 21 | using sanctum::testing::phys_buffer_size; 22 | using sanctum::testing::set_current_core; 23 | using sanctum::testing::set_core_count; 24 | 25 | TEST(PageTablesTest, Geometry) { 26 | // NOTE: using static_assert to ensure that constexpr functions work as 27 | // expected 28 | static_assert(page_table_levels() == 3, "page_table_levels"); 29 | static_assert(page_shift() == 12, "page_shift"); 30 | static_assert(page_size() == 4096, "page_size"); 31 | 32 | static_assert(page_table_shift(0) == 9, "L0 page_table_shift"); 33 | static_assert(page_table_entries(0) == 512, "L0 page_table_entries"); 34 | static_assert(page_table_entry_shift(0) == 3, "L0 page_table_entry_shift"); 35 | static_assert(page_table_entry_size(0) == 8, "L0 page_table_entry_size"); 36 | static_assert(page_table_size(0) == 4096, "L0 page_table_size"); 37 | static_assert(page_table_pages(0) == 1, "L0 page_table_pages"); 38 | 39 | static_assert(page_table_shift(1) == 10, "L1 page_table_shift"); 40 | static_assert(page_table_entries(1) == 1024, "L1 page_table_entries"); 41 | static_assert(page_table_entry_shift(1) == 4, "L1 page_table_entry_shift"); 42 | static_assert(page_table_entry_size(1) == 16, "L1 page_table_entry_size"); 43 | static_assert(page_table_size(1) == 16384, "L1 page_table_size"); 44 | static_assert(page_table_pages(1) == 4, "L1 page_table_pages"); 45 | 46 | static_assert(page_table_shift(2) == 11, "L2 page_table_shift"); 47 | static_assert(page_table_entries(2) == 2048, "L2 page_table_entries"); 48 | static_assert(page_table_entry_shift(2) == 4, "L2 page_table_entry_shift"); 49 | static_assert(page_table_entry_size(2) == 16, "L2 page_table_entry_size"); 50 | static_assert(page_table_size(2) == 32768, "L2 page_table_size"); 51 | static_assert(page_table_pages(2) == 8, "L2 page_table_pages"); 52 | 53 | static_assert(page_table_translated_bits() == 42, 54 | "page_table_translated_bits"); 55 | } 56 | 57 | TEST(PageTablesTest, IsValidPageTableEntry) { 58 | uintptr_t addr = 160; 59 | ASSERT_LE(256, phys_buffer_size); 60 | memset(phys_buffer, 0, 256); 61 | 62 | *(reinterpret_cast(phys_buffer + addr)) = 0; 63 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 0)); 64 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 1)); 65 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 2)); 66 | *(reinterpret_cast(phys_buffer + addr)) = 1; 67 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 0)); 68 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 1)); 69 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 2)); 70 | 71 | *(reinterpret_cast(phys_buffer + addr)) = 0xcafebabe000; 72 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 0)); 73 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 1)); 74 | ASSERT_EQ(false, is_valid_page_table_entry(addr, 2)); 75 | *(reinterpret_cast(phys_buffer + addr)) = 0xcafebabe001; 76 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 0)); 77 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 1)); 78 | ASSERT_EQ(true, is_valid_page_table_entry(addr, 2)); 79 | } 80 | 81 | TEST(PageTablesTest, PageTableEntryTarget) { 82 | uintptr_t addr = 160; 83 | ASSERT_LE(256, phys_buffer_size); 84 | memset(phys_buffer, 0, 256); 85 | 86 | *(reinterpret_cast(phys_buffer + addr)) = 0; 87 | ASSERT_EQ(0, page_table_entry_target(addr, 0)); 88 | ASSERT_EQ(0, page_table_entry_target(addr, 1)); 89 | ASSERT_EQ(0, page_table_entry_target(addr, 2)); 90 | *(reinterpret_cast(phys_buffer + addr)) = 1; 91 | ASSERT_EQ(0, page_table_entry_target(addr, 0)); 92 | ASSERT_EQ(0, page_table_entry_target(addr, 1)); 93 | ASSERT_EQ(0, page_table_entry_target(addr, 2)); 94 | 95 | *(reinterpret_cast(phys_buffer + addr)) = 0xcafebabe000; 96 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 0)); 97 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 1)); 98 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 2)); 99 | *(reinterpret_cast(phys_buffer + addr)) = 0xcafebabe001; 100 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 0)); 101 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 1)); 102 | ASSERT_EQ(0xcafebabe000, page_table_entry_target(addr, 2)); 103 | *(reinterpret_cast(phys_buffer + addr)) = 0xcafebabffff; 104 | ASSERT_EQ(0xcafebabf000, page_table_entry_target(addr, 0)); 105 | ASSERT_EQ(0xcafebabf000, page_table_entry_target(addr, 1)); 106 | ASSERT_EQ(0xcafebabf000, page_table_entry_target(addr, 2)); 107 | } 108 | 109 | TEST(PageTablesTest, WritePageTableEntry) { 110 | uintptr_t addr = 160; 111 | ASSERT_LE(256, phys_buffer_size); 112 | memset(phys_buffer, 0, 256); 113 | 114 | write_page_table_entry(addr, 0, 0xcafebabe000, 0); 115 | ASSERT_EQ(0xcafebabe001, 116 | *(reinterpret_cast(phys_buffer + addr))); 117 | write_page_table_entry(addr, 1, 0xcafebabe000, 0); 118 | ASSERT_EQ(0xcafebabe001, 119 | *(reinterpret_cast(phys_buffer + addr))); 120 | write_page_table_entry(addr, 2, 0xcafebabe000, 0); 121 | ASSERT_EQ(0xcafebabe001, 122 | *(reinterpret_cast(phys_buffer + addr))); 123 | 124 | write_page_table_entry(addr, 0, 0xcafebabe000, 0xffffffffff); 125 | ASSERT_EQ(0xcafebabefff, 126 | *(reinterpret_cast(phys_buffer + addr))); 127 | write_page_table_entry(addr, 1, 0xcafebabe000, 0xffffffffff); 128 | ASSERT_EQ(0xcafebabefff, 129 | *(reinterpret_cast(phys_buffer + addr))); 130 | write_page_table_entry(addr, 2, 0xcafebabe000, 0xffffffffff); 131 | ASSERT_EQ(0xcafebabefff, 132 | *(reinterpret_cast(phys_buffer + addr))); 133 | } 134 | -------------------------------------------------------------------------------- /src/bare/phys_atomics.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_ATOMIC_H_INCLUDED) 2 | #define BARE_ATOMIC_H_INCLUDED 3 | 4 | #include "phys_ptr.h" 5 | 6 | namespace sanctum { 7 | namespace bare { 8 | 9 | // C++11 lock-free atomic flag. 10 | 11 | struct atomic_flag; 12 | bool atomic_flag_test_and_set(phys_ptr flag) noexcept; 13 | void atomic_flag_clear(phys_ptr flag) noexcept; 14 | 15 | // C++11 atomic integers. 16 | // 17 | // The only specializations implemented by the bare-metal library are 18 | // atomic and atomic. 19 | 20 | template struct atomic; 21 | 22 | template 23 | void atomic_init(phys_ptr> object, T value) noexcept; 24 | template T atomic_load(phys_ptr> object) noexcept; 25 | template 26 | void atomic_store(phys_ptr> object, T value) noexcept; 27 | template 28 | T atomic_fetch_add(phys_ptr> object, T value) noexcept; 29 | template 30 | T atomic_fetch_sub(phys_ptr> object, T value) noexcept; 31 | 32 | 33 | }; // namespace sanctum::bare 34 | }; // namespace sanctum 35 | 36 | // Per-architecture implementations of atomic operations. 37 | #include "phys_atomics_arch.h" 38 | 39 | #endif // !definded(BARE_ATOMIC_H_INCLUDED) 40 | -------------------------------------------------------------------------------- /src/bare/phys_atomics_test.cc: -------------------------------------------------------------------------------- 1 | #include "bare/phys_atomics.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::atomic; 6 | using sanctum::bare::atomic_flag; 7 | using sanctum::bare::atomic_flag_test_and_set; 8 | using sanctum::bare::phys_ptr; 9 | using sanctum::bare::size_t; 10 | using sanctum::bare::uintptr_t; 11 | using sanctum::testing::phys_buffer; 12 | using sanctum::testing::phys_buffer_size; 13 | 14 | TEST(AtomicFlagTest, TestAndSet) { 15 | uintptr_t addr1 = 160, addr2 = 200; 16 | ASSERT_LE(256, phys_buffer_size); 17 | memset(phys_buffer, 0, 256); 18 | phys_ptr ptr1{addr1}, ptr2{addr2}; 19 | 20 | ASSERT_EQ(false, atomic_flag_test_and_set(ptr1)); 21 | ASSERT_EQ(true, 22 | (reinterpret_cast(phys_buffer + addr1))->__flag); 23 | ASSERT_EQ(false, 24 | (reinterpret_cast(phys_buffer + addr2))->__flag); 25 | ASSERT_EQ(false, atomic_flag_test_and_set(ptr2)); 26 | ASSERT_EQ(true, 27 | (reinterpret_cast(phys_buffer + addr1))->__flag); 28 | ASSERT_EQ(true, 29 | (reinterpret_cast(phys_buffer + addr2))->__flag); 30 | ASSERT_EQ(true, atomic_flag_test_and_set(ptr1)); 31 | ASSERT_EQ(true, atomic_flag_test_and_set(ptr2)); 32 | ASSERT_EQ(true, 33 | (reinterpret_cast(phys_buffer + addr1))->__flag); 34 | ASSERT_EQ(true, 35 | (reinterpret_cast(phys_buffer + addr2))->__flag); 36 | 37 | (reinterpret_cast(phys_buffer + addr1))->__flag = false; 38 | (reinterpret_cast(phys_buffer + addr2))->__flag = false; 39 | } 40 | 41 | TEST(AtomicFlagTest, Clear) { 42 | uintptr_t addr1 = 160, addr2 = 200; 43 | ASSERT_LE(256, phys_buffer_size); 44 | memset(phys_buffer, 0, 256); 45 | phys_ptr ptr1{addr1}, ptr2{addr2}; 46 | 47 | atomic_flag_test_and_set(ptr1); 48 | atomic_flag_test_and_set(ptr2); 49 | ASSERT_EQ(true, 50 | (reinterpret_cast(phys_buffer + addr1))->__flag); 51 | ASSERT_EQ(true, 52 | (reinterpret_cast(phys_buffer + addr2))->__flag); 53 | 54 | atomic_flag_clear(ptr1); 55 | ASSERT_EQ(false, 56 | (reinterpret_cast(phys_buffer + addr1))->__flag); 57 | ASSERT_EQ(true, 58 | (reinterpret_cast(phys_buffer + addr2))->__flag); 59 | 60 | atomic_flag_clear(ptr2); 61 | ASSERT_EQ(false, 62 | (reinterpret_cast(phys_buffer + addr1))->__flag); 63 | ASSERT_EQ(false, 64 | (reinterpret_cast(phys_buffer + addr2))->__flag); 65 | 66 | ASSERT_EQ(false, atomic_flag_test_and_set(ptr1)); 67 | ASSERT_EQ(false, atomic_flag_test_and_set(ptr2)); 68 | 69 | (reinterpret_cast(phys_buffer + addr1))->__flag = false; 70 | (reinterpret_cast(phys_buffer + addr2))->__flag = false; 71 | } 72 | 73 | TEST(AtomicTest, HandlesUintptr) { 74 | uintptr_t addr = 160; 75 | uintptr_t value = 0xbeef, write_value = 0xdeed; 76 | ASSERT_LE(256, phys_buffer_size); 77 | memset(phys_buffer, 0, 256); 78 | phys_ptr> ptr{addr}; 79 | 80 | atomic_init(ptr, value); 81 | ASSERT_EQ(value, 82 | (reinterpret_cast*>(phys_buffer + addr))->__value); 83 | ASSERT_EQ(value, atomic_load(ptr)); 84 | 85 | atomic_store(ptr, write_value); 86 | ASSERT_EQ(write_value, 87 | (reinterpret_cast*>(phys_buffer + addr))->__value); 88 | ASSERT_EQ(write_value, atomic_load(ptr)); 89 | 90 | ASSERT_EQ(write_value, atomic_fetch_add(ptr, value)); 91 | ASSERT_EQ(write_value + value, 92 | (reinterpret_cast*>(phys_buffer + addr))->__value); 93 | ASSERT_EQ(write_value + value, atomic_load(ptr)); 94 | 95 | ASSERT_EQ(write_value + value, atomic_fetch_add(ptr, value)); 96 | ASSERT_EQ(write_value + 2 * value, 97 | (reinterpret_cast*>(phys_buffer + addr))->__value); 98 | ASSERT_EQ(write_value + 2 * value, atomic_load(ptr)); 99 | 100 | atomic_store(ptr, write_value); 101 | ASSERT_EQ(write_value, 102 | (reinterpret_cast*>(phys_buffer + addr))->__value); 103 | ASSERT_EQ(write_value, atomic_load(ptr)); 104 | 105 | ASSERT_EQ(write_value, atomic_fetch_sub(ptr, value)); 106 | ASSERT_EQ(write_value - value, 107 | (reinterpret_cast*>(phys_buffer + addr))->__value); 108 | ASSERT_EQ(write_value - value, atomic_load(ptr)); 109 | 110 | ASSERT_EQ(write_value - value, atomic_fetch_sub(ptr, value)); 111 | ASSERT_EQ(write_value - 2 * value, 112 | (reinterpret_cast*>(phys_buffer + addr))->__value); 113 | ASSERT_EQ(write_value - 2 * value, atomic_load(ptr)); 114 | 115 | (reinterpret_cast*>(phys_buffer + addr))->__value = 0; 116 | } 117 | 118 | TEST(AtomicTest, HandlesSize) { 119 | uintptr_t addr = 160; 120 | size_t value = 0xbeef, write_value = 0xdeed; 121 | ASSERT_LE(256, phys_buffer_size); 122 | memset(phys_buffer, 0, 256); 123 | phys_ptr> ptr{addr}; 124 | 125 | atomic_init(ptr, value); 126 | ASSERT_EQ(value, 127 | (reinterpret_cast*>(phys_buffer + addr))->__value); 128 | ASSERT_EQ(value, atomic_load(ptr)); 129 | 130 | atomic_store(ptr, write_value); 131 | ASSERT_EQ(write_value, 132 | (reinterpret_cast*>(phys_buffer + addr))->__value); 133 | ASSERT_EQ(write_value, atomic_load(ptr)); 134 | 135 | ASSERT_EQ(write_value, atomic_fetch_add(ptr, value)); 136 | ASSERT_EQ(write_value + value, 137 | (reinterpret_cast*>(phys_buffer + addr))->__value); 138 | ASSERT_EQ(write_value + value, atomic_load(ptr)); 139 | 140 | ASSERT_EQ(write_value + value, atomic_fetch_add(ptr, value)); 141 | ASSERT_EQ(write_value + 2 * value, 142 | (reinterpret_cast*>(phys_buffer + addr))->__value); 143 | ASSERT_EQ(write_value + 2 * value, atomic_load(ptr)); 144 | 145 | atomic_store(ptr, write_value); 146 | ASSERT_EQ(write_value, 147 | (reinterpret_cast*>(phys_buffer + addr))->__value); 148 | ASSERT_EQ(write_value, atomic_load(ptr)); 149 | 150 | ASSERT_EQ(write_value, atomic_fetch_sub(ptr, value)); 151 | ASSERT_EQ(write_value - value, 152 | (reinterpret_cast*>(phys_buffer + addr))->__value); 153 | ASSERT_EQ(write_value - value, atomic_load(ptr)); 154 | 155 | ASSERT_EQ(write_value - value, atomic_fetch_sub(ptr, value)); 156 | ASSERT_EQ(write_value - 2 * value, 157 | (reinterpret_cast*>(phys_buffer + addr))->__value); 158 | ASSERT_EQ(write_value - 2 * value, atomic_load(ptr)); 159 | 160 | (reinterpret_cast*>(phys_buffer + addr))->__value = 0; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /src/bare/phys_ptr.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_PHYS_PTR_H_INCLUDED) 2 | #define BARE_PHYS_PTR_H_INCLUDED 3 | 4 | #include "base_types.h" 5 | #include "traits.h" 6 | 7 | namespace sanctum { 8 | namespace bare { 9 | 10 | template class phys_ptr; 11 | template class phys_ptr::value>::type>; 13 | template class phys_ref; 14 | 15 | 16 | // References to the physical address space. 17 | // 18 | // This reference type shouldn't be used directly. It's better to use the 19 | // pointer type phys_ptr, and use pointer operations explicitly. 20 | template class phys_ref { 21 | public: 22 | // Guarantee the existence of a default copy constructor. 23 | constexpr inline phys_ref(const phys_ref&) noexcept = default; 24 | // Guarantee the existence of a move constructor. 25 | // 26 | // This is used when constructing a reference to be returned, e.g. 27 | // return phys_ref{address}; 28 | constexpr inline phys_ref(phys_ref&&) noexcept = default; 29 | 30 | // Dereference. 31 | operator T() const; 32 | // Reference assignment. 33 | phys_ref& operator =(const T&); 34 | 35 | // Copy assignment. 36 | // 37 | // This is used by code that creates references explicitly, like: 38 | // phys_ref ref = *b; 39 | // *a = ref; 40 | // 41 | // Implementing this lets us use phys_ref explicitly, in case we ever decide 42 | // to e.g., use it to express non-null pointers, like WebKit does. 43 | phys_ref& operator =(const phys_ref& other) { 44 | const T value = other.operator T(); 45 | return this->operator =(value); 46 | } 47 | // Move-assignment. 48 | // 49 | // This is used for de-referencing phys_ptr assignment (*a = *b). 50 | inline phys_ref& operator =(const phys_ref&& other) { 51 | // NOTE: we write this in terms of standard de-referencing and assignment 52 | // so we don't have to write assembly for those twice; it would only 53 | // make sense to implement this directly if the underlying 54 | // architecture had a memory-to-memory move, which isn't true for the 55 | // load/store machines that we're targeting 56 | const T value = other.operator T(); 57 | return this->operator =(value); 58 | } 59 | 60 | // Address-of. 61 | constexpr inline phys_ptr operator &() const noexcept { 62 | return phys_ptr{addr}; 63 | } 64 | 65 | // Convenience overloads. 66 | // 67 | // The RISC V ISA doesn't have any opcodes that use memory directly, except 68 | // for load/store, so there's no optimization opportunity in implementing 69 | // operators such as += in assembly. Therefore, we implement what we need 70 | // once, here. 71 | template inline phys_ref& operator +=(const U& other) { 72 | this->operator =(this->operator T() + other); 73 | return *this; 74 | } 75 | template inline phys_ref& operator -=(const U& other) { 76 | this->operator =(this->operator T() - other); 77 | return *this; 78 | } 79 | template inline phys_ref& operator |=(const U& other) { 80 | this->operator =(this->operator T() | other); 81 | return *this; 82 | } 83 | template inline phys_ref& operator &=(const U& other) { 84 | this->operator =(this->operator T() & other); 85 | return *this; 86 | } 87 | 88 | private: 89 | // Initialize from a physical address. 90 | constexpr explicit inline phys_ref(const uintptr_t& phys_addr) noexcept 91 | : addr(phys_addr) {}; 92 | 93 | // Friendship needed to access the constructor that takes an address. 94 | template friend class phys_ptr; 95 | 96 | const uintptr_t addr; 97 | }; 98 | 99 | 100 | // Pointers to the physical address space. 101 | template class phys_ptr { 102 | public: 103 | // Guarantee the existence of a default copy constructor. 104 | constexpr inline phys_ptr(const phys_ptr&) noexcept = default; 105 | // Guarantee the existence of a move constructor. 106 | // 107 | // This is used when returning a newly created pointer, e.g. 108 | // return phys_ptr{addr}; 109 | constexpr inline phys_ptr(phys_ptr&&) noexcept = default; 110 | // Guarantee the existence of a default copy assignment. 111 | inline phys_ptr& operator =(const phys_ptr&) noexcept = default; 112 | // Guarantee the existence of a move assignment. 113 | // 114 | // This is used when assigning a uintptr_t to an existing pointer, e.g. 115 | // ptr = phys_ptr(address); 116 | inline phys_ptr& operator =(phys_ptr&&) noexcept = default; 117 | 118 | // Initialize from a physical address. 119 | constexpr inline phys_ptr(const uintptr_t phys_addr) noexcept 120 | : addr(phys_addr) {}; 121 | // Convert to a numerical physical address. 122 | constexpr explicit inline operator uintptr_t() const noexcept { 123 | return addr; 124 | } 125 | 126 | // The * operator returns references. 127 | constexpr inline phys_ref operator *() const noexcept { 128 | return phys_ref{addr}; 129 | } 130 | constexpr inline phys_ref operator [](size_t offset) const noexcept { 131 | return phys_ref{reinterpret_cast( 132 | reinterpret_cast(addr) + offset)}; 133 | } 134 | 135 | // Pointer comparsion. 136 | constexpr inline bool operator ==(const phys_ptr& other) const noexcept { 137 | return addr == other.addr; 138 | } 139 | constexpr inline bool operator !=(const phys_ptr& other) const noexcept { 140 | return addr != other.addr; 141 | } 142 | 143 | // Pointer arithmetic. 144 | inline phys_ptr& operator +=(const size_t offset) noexcept { 145 | addr = reinterpret_cast(reinterpret_cast(addr) + offset); 146 | return *this; 147 | } 148 | constexpr inline phys_ptr operator +(const size_t offset) const 149 | noexcept { 150 | return phys_ptr{reinterpret_cast( 151 | reinterpret_cast(addr) + offset)}; 152 | } 153 | inline phys_ptr& operator -=(const size_t offset) noexcept { 154 | addr = reinterpret_cast(reinterpret_cast(addr) - offset); 155 | return *this; 156 | } 157 | constexpr inline phys_ptr operator -(const size_t offset) const 158 | noexcept { 159 | return phys_ptr{reinterpret_cast( 160 | reinterpret_cast(addr) - offset)}; 161 | } 162 | 163 | // nullptr replacement. 164 | static constexpr phys_ptr null() noexcept { return phys_ptr{0}; } 165 | 166 | private: 167 | uintptr_t addr; 168 | }; 169 | 170 | template class phys_ptr::value>::type> { 172 | public: 173 | // The code in the master template, slightly tweaked. 174 | // NOTE: there's a lot of redundancy here; using a base class wouldn't help 175 | // too much, because constructors and assignment operators would have 176 | // to be redefined 177 | constexpr inline phys_ptr(const phys_ptr&) noexcept = default; 178 | constexpr inline phys_ptr(phys_ptr&&) noexcept = default; 179 | inline phys_ptr& operator =(const phys_ptr&) noexcept = default; 180 | inline phys_ptr& operator =(phys_ptr&&) noexcept = default; 181 | constexpr inline phys_ptr(const uintptr_t phys_addr) noexcept 182 | : addr(phys_addr) {}; 183 | constexpr explicit inline operator uintptr_t() const noexcept { 184 | return addr; 185 | } 186 | constexpr inline phys_ref operator *() const noexcept { 187 | return phys_ref{addr}; 188 | } 189 | constexpr inline phys_ref operator [](size_t offset) const noexcept { 190 | return phys_ref{reinterpret_cast( 191 | reinterpret_cast(addr) + offset)}; 192 | } 193 | constexpr inline bool operator ==(const phys_ptr& other) const noexcept { 194 | return addr == other.addr; 195 | } 196 | constexpr inline bool operator !=(const phys_ptr& other) const noexcept { 197 | return addr != other.addr; 198 | } 199 | inline phys_ptr& operator +=(const size_t offset) noexcept { 200 | addr = reinterpret_cast(reinterpret_cast(addr) + offset); 201 | return *this; 202 | } 203 | constexpr inline phys_ptr operator +(const size_t offset) const noexcept { 204 | return phys_ptr{reinterpret_cast( 205 | reinterpret_cast(addr) + offset)}; 206 | } 207 | inline phys_ptr& operator -=(const size_t offset) noexcept { 208 | addr = reinterpret_cast(reinterpret_cast(addr) - offset); 209 | return *this; 210 | } 211 | constexpr inline phys_ptr operator -(const size_t offset) const 212 | noexcept { 213 | return phys_ptr{reinterpret_cast( 214 | reinterpret_cast(addr) - offset)}; 215 | } 216 | static constexpr phys_ptr null() noexcept { return phys_ptr{0}; } 217 | 218 | public: // Extensions for pointers to types with members. 219 | template constexpr inline phys_ref 220 | operator ->*(U T::* member_offset) const noexcept { 221 | return phys_ref{reinterpret_cast( 222 | &((reinterpret_cast(this->addr))->*(member_offset)))}; 223 | } 224 | template constexpr inline phys_ptr 225 | operator ->*(U (T::* member_offset)[N]) const noexcept { 226 | return phys_ptr{reinterpret_cast( 227 | (reinterpret_cast(this->addr))->*(member_offset))}; 228 | } 229 | 230 | // NOTE: It's tempting to add a reference operator (commented out below). 231 | // Unfortunately, -> must (ultimately) return a pointer type, so we 232 | // can't get the ptr->field notation to work. Instead, we must use the 233 | // clunky notation below, which invokes ->* instead. 234 | // ptr->*(&struct_name::field) 235 | // 236 | // constexpr inline phys_ref operator ->() const noexcept { 237 | // return phys_ref{addr}; 238 | // } 239 | 240 | private: 241 | uintptr_t addr; 242 | }; 243 | 244 | }; // namespace sanctum::bare 245 | }; // namespace sanctum 246 | 247 | // Per-architecture implementations of physical loads and stores. 248 | #include "phys_ptr_arch.h" 249 | 250 | namespace sanctum { 251 | namespace bare { 252 | 253 | static_assert(sizeof(phys_ref) == sizeof(uintptr_t), 254 | "phys_ref has unnecessary storge overhead"); 255 | static_assert(sizeof(phys_ptr) == sizeof(uintptr_t), 256 | "phys_ptr has unnecessary storge overhead"); 257 | 258 | // Specialize phys_ref for phys_ptr so we can de-reference phys_ptr refs. 259 | // 260 | // This allows us to have phys_ptr members in structures pointed by phys_ptr. 261 | template class phys_ref> { 262 | public: 263 | constexpr inline phys_ref(const phys_ref>&) noexcept = default; 264 | constexpr inline phys_ref(phys_ref>&&) noexcept = default; 265 | inline operator phys_ptr() const { 266 | return phys_ptr{ 267 | (reinterpret_cast*>(this))-> 268 | operator uintptr_t()}; 269 | } 270 | inline phys_ref>& operator =(const phys_ptr& value) { 271 | (reinterpret_cast*>(this))->operator =( 272 | value.operator uintptr_t()); 273 | return *this; 274 | } 275 | inline phys_ref>& operator =( 276 | const phys_ref>& other) { 277 | const phys_ptr value = other.operator phys_ptr(); 278 | return this->operator =(value); 279 | } 280 | inline phys_ref>& operator =(phys_ref>&& other) { 281 | const phys_ptr value = other.operator phys_ptr(); 282 | return this->operator =(value); 283 | } 284 | constexpr inline phys_ptr> operator &() const noexcept { 285 | return phys_ptr>{addr}; 286 | } 287 | template inline phys_ref>& operator +=( 288 | const U& other) { 289 | this->operator =(this->operator phys_ptr() + other); 290 | return *this; 291 | } 292 | template inline phys_ref>& operator -=( 293 | const U& other) { 294 | this->operator =(this->operator phys_ptr() - other); 295 | return *this; 296 | } 297 | 298 | private: 299 | // Initialize from a physical address. 300 | constexpr explicit inline phys_ref(const uintptr_t& phys_addr) noexcept 301 | : addr(phys_addr) {}; 302 | 303 | // Friendship needed to access the constructor that takes an address. 304 | template friend class phys_ptr; 305 | 306 | const uintptr_t addr; 307 | }; 308 | 309 | }; // namespace sanctum::bare 310 | }; // namespace sanctum 311 | #endif // !defined(BARE_PHYS_PTR_H_INCLUDED) 312 | -------------------------------------------------------------------------------- /src/bare/traits.h: -------------------------------------------------------------------------------- 1 | #if !defined(BARE_TRAITS_H_INCLUDED) 2 | #define BARE_TRAITS_H_INCLUDED 3 | 4 | namespace sanctum { 5 | namespace bare { 6 | 7 | // Useful subset of the type traits in the C++ 11/14 stdlib. 8 | 9 | // enable_if 10 | template struct enable_if {}; 11 | template struct enable_if { typedef T type; }; 12 | 13 | // remove_const, remove_volatile, remove_cv (used below) 14 | template struct remove_const { typedef T type; }; 15 | template struct remove_const { typedef T type; }; 16 | template struct remove_volatile { typedef T type; }; 17 | template struct remove_volatile { typedef T type; }; 18 | template struct remove_cv { 19 | typedef typename remove_volatile::type>::type type; 20 | }; 21 | 22 | // integral_constant (used below) 23 | template struct integral_constant { 24 | static constexpr const T value = v; 25 | typedef T value_type; 26 | typedef integral_constant type; 27 | inline constexpr operator value_type() const noexcept { return value; } 28 | inline constexpr value_type operator()() const noexcept { return value; } 29 | }; 30 | 31 | // type_type, false_type 32 | typedef integral_constant true_type; 33 | typedef integral_constant false_type; 34 | 35 | // is_same (used in tests) 36 | template struct is_same : public false_type { }; 37 | template struct is_same : public true_type { }; 38 | 39 | // is_class_or_union 40 | // 41 | // This is not in the C++11/14 standard, but can be implemented without special 42 | // compiler operators, and is more useful anyway. 43 | namespace __is_class_or_union_impl { 44 | struct __two { char __x[2]; }; 45 | template char __test(int T::*); 46 | template __two __test(...); 47 | }; 48 | template struct is_class_or_union : public integral_constant< 49 | bool, sizeof(__is_class_or_union_impl::__test(0)) == 1> { 50 | }; 51 | 52 | // TODO(pwnall): the last two definitions are here due to the research that 53 | // went into is_class_or_union; if they prove to be unnecessary, 54 | // remove them and their tests 55 | 56 | // is_union 57 | #if __has_feature(is_union) || (_GNUC_VER >= 403) 58 | template struct is_union : public integral_constant< 59 | bool, __is_union(T)> { 60 | }; 61 | #else 62 | // It's impossible to do this correctly, so we always default this to false. 63 | template struct is_union : public false_type {}; 64 | #endif // __has_feature(is_union) || (_GNUC_VER >= 403) 65 | 66 | // is_class 67 | template struct is_class : public integral_constant< 68 | bool, is_class_or_union::value && !is_union::value> { 69 | }; 70 | 71 | }; // namespace sanctum::bare 72 | }; // namespace sanctum 73 | 74 | #endif // !defined(BARE_TRAITS_H_INCLUDED) 75 | -------------------------------------------------------------------------------- /src/bare/traits_test.cc: -------------------------------------------------------------------------------- 1 | #include "traits.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::is_same; 6 | using sanctum::bare::remove_const; 7 | using sanctum::bare::remove_volatile; 8 | using sanctum::bare::remove_cv; 9 | using sanctum::bare::is_class_or_union; 10 | using sanctum::bare::is_union; 11 | using sanctum::bare::is_class; 12 | 13 | 14 | typedef union { 15 | int a; 16 | int b; 17 | } TestUnion; 18 | struct TestStruct { 19 | int a; 20 | int b; 21 | }; 22 | class TestClass { 23 | int a; 24 | int b; 25 | }; 26 | 27 | TEST(TraitsTest, IsSame) { 28 | static_assert(true == is_same::value, "is_same"); 29 | static_assert(false == is_same::value, "is_same"); 30 | static_assert(false == is_same::value, "is_same"); 31 | 32 | static_assert(true == is_same::value, 33 | "is_same"); 34 | static_assert(false == is_same::value, 35 | "is_same"); 36 | static_assert(false == is_same::value, 37 | "is_same"); 38 | 39 | static_assert(true == is_same::value, 40 | "is_same"); 41 | static_assert(false == is_same::value, 42 | "is_same"); 43 | static_assert(false == is_same::value, 44 | "is_same"); 45 | } 46 | 47 | TEST(TraitsTest, RemoveConst) { 48 | static_assert(true == is_same::type>::value, 50 | "remove_const"); 51 | static_assert(true == is_same::type>::value, 53 | "remove_const"); 54 | static_assert(true == is_same::type>::value, 56 | "remove_const"); 57 | static_assert(true == is_same::type>::value, 59 | "remove_const"); 60 | } 61 | 62 | TEST(TraitsTest, RemoveVolatile) { 63 | static_assert(true == is_same::type>::value, 65 | "remove_volatile"); 66 | static_assert(true == is_same::type>::value, 68 | "remove_volatile"); 69 | static_assert(true == is_same::type>::value, 71 | "remove_volatile"); 72 | static_assert(true == is_same::type>::value, 74 | "remove_volatile"); 75 | } 76 | 77 | TEST(TraitsTest, RemoveCv) { 78 | static_assert(true == is_same::type>::value, 80 | "remove_cv"); 81 | static_assert(true == is_same::type>::value, 83 | "remove_cv"); 84 | static_assert(true == is_same::type>::value, 86 | "remove_cv"); 87 | static_assert(true == is_same::type>::value, 89 | "remove_cv"); 90 | } 91 | 92 | TEST(TraitsTest, IsClassOrUnion) { 93 | static_assert(true == is_class_or_union::value, 94 | "is_class_or_union"); 95 | static_assert(true == is_class_or_union::value, 96 | "is_class_or_union"); 97 | static_assert(true == is_class_or_union::value, 98 | "is_class_or_union"); 99 | static_assert(false == is_class_or_union::value, 100 | "is_class_or_union"); 101 | static_assert(false == is_class_or_union::value, 102 | "is_class_or_union"); 103 | static_assert(false == is_class_or_union::value, 104 | "is_class_or_union"); 105 | static_assert(false == is_class_or_union::value, 106 | "is_class_or_union"); 107 | static_assert(false == is_class_or_union::value, 108 | "is_class_or_union"); 109 | static_assert(false == is_class_or_union::value, 110 | "is_class_or_union"); 111 | static_assert(false == is_class_or_union::value, 112 | "is_class_or_union"); 113 | static_assert(false == is_class_or_union::value, 114 | "is_class_or_union"); 115 | static_assert(false == is_class_or_union::value, 116 | "is_class_or_union"); 117 | } 118 | 119 | TEST(TraitsTest, IsUnion) { 120 | #if __has_feature(is_union) || (_GNUC_VER >= 403) 121 | // Unions can't be classified correctly without compiler support. 122 | static_assert(true == is_union::value, "is_union"); 123 | #endif // __has_feature(is_union) || (_GNUC_VER >= 403) 124 | 125 | static_assert(false == is_union::value, "is_union"); 126 | static_assert(false == is_union::value, "is_union"); 127 | static_assert(false == is_union::value, "is_union"); 128 | static_assert(false == is_union::value, 129 | "is_union"); 130 | static_assert(false == is_union::value, "is_union"); 131 | static_assert(false == is_union::value, "is_union"); 132 | static_assert(false == is_union::value, 133 | "is_union"); 134 | static_assert(false == is_union::value, "is_union"); 135 | static_assert(false == is_union::value, "is_union"); 136 | static_assert(false == is_union::value, "is_union"); 137 | static_assert(false == is_union::value, "is_union"); 138 | } 139 | 140 | TEST(TraitsTest, IsClass) { 141 | #if __has_feature(is_union) || (_GNUC_VER >= 403) 142 | // Unions can't be classified correctly without compiler support. 143 | static_assert(false == is_class::value, "is_class"); 144 | #endif // __has_feature(is_union) || (_GNUC_VER >= 403) 145 | 146 | static_assert(true == is_class::value, "is_class"); 147 | static_assert(true == is_class::value, "is_class"); 148 | static_assert(false == is_class::value, "is_class"); 149 | static_assert(false == is_class::value, 150 | "is_class"); 151 | static_assert(false == is_class::value, "is_class"); 152 | static_assert(false == is_class::value, "is_class"); 153 | static_assert(false == is_class::value, 154 | "is_class"); 155 | static_assert(false == is_class::value, "is_class"); 156 | static_assert(false == is_class::value, "is_class"); 157 | static_assert(false == is_class::value, "is_class"); 158 | static_assert(false == is_class::value, "is_class"); 159 | } 160 | -------------------------------------------------------------------------------- /src/common.gypi: -------------------------------------------------------------------------------- 1 | { 2 | 'variables': { 3 | 'clang': 'build/llvm-build/bin', 4 | 'library': 'static_library', 5 | 'mac_sdk': '10.12', 6 | 'mac_deployment_target': '10.7', 7 | }, 8 | 9 | 'make_global_settings': [ 10 | ['CC', '<(clang)/clang'], 11 | ['CXX', '<(clang)/clang++'], 12 | ['LINK', '<(clang)/clang++'], 13 | ], 14 | 'target_defaults': { 15 | 'cflags': [ 16 | '-Wall', '-Wextra', '-Werror', 17 | '-Wsign-compare', # See http://llvm.org/bugs/show_bug.cgi?id=10448 18 | '-fcolor-diagnostics', 19 | '-fstrict-aliasing', 20 | '-fno-threadsafe-statics', 21 | '-std=c11', 22 | '-Wheader-hygiene', 23 | ], 24 | 'cflags_cc': [ 25 | '-std=c++11', 26 | ], 27 | 'xcode_settings': { 28 | 'ALWAYS_SEARCH_USER_PATHS': 'NO', 29 | 'CLANG_CXX_LANGUAGE_STANDARD': 'c++11', # -std=c++11 30 | 'CLANG_CXX_LIBRARY': 'libc++', # -stdlib=libc++ 31 | 'GCC_C_LANGUAGE_STANDARD': 'c11', # -std=c11 32 | 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks 33 | 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings 34 | 'GCC_STRICT_ALIASING': 'YES', # -fstrict-aliasing 35 | 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics 36 | 'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES', # -Werror 37 | 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof 38 | 'SDKROOT': 'macosx<(mac_sdk)', 39 | 'MACOSX_DEPLOYMENT_TARGET': '<(mac_deployment_target)', 40 | }, 41 | 'configurations': { 42 | 'Debug': { 43 | 'defines': ['DEBUG', '_DEBUG'], 44 | 'cflags': ['-g', '-O0'], 45 | 'xcode_settings': { 46 | 'GCC_OPTIMIZATION_LEVEL': '0', # gyp defaults to -Os 47 | 'ONLY_ACTIVE_ARCH': 'YES', 48 | }, 49 | }, 50 | 'Release': { 51 | 'cflags': [ 52 | '-O3', 53 | # Needed by --gc-symbols ldflag. 54 | '-ffunction-sections', '-fdata-sections', 55 | ], 56 | 'ldflags': [ 57 | '-Wl,-O1', 58 | '-Wl,--as-needed', 59 | '-Wl,--gc-sections', # Remove unused symbols. 60 | ], 61 | 'xcode_settings': { 62 | 'GCC_OPTIMIZATION_LEVEL': '3', # -O3 63 | }, 64 | }, 65 | }, 66 | 'default_configuration': 'Release', 67 | 'conditions': [ 68 | ['OS=="mac"', { 69 | 'cflags!': [ 70 | '-Os', # gyp defaults to -Os on OSX 71 | ], 72 | }], 73 | ], 74 | }, 75 | } 76 | -------------------------------------------------------------------------------- /src/crypto/crypto.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'variables': { 6 | 'crypto_sources': [ 7 | 'hash.cc', 8 | 'hash.h', 9 | ], 10 | }, 11 | 'targets': [ 12 | { 13 | # The crypto library. 14 | 'target_name': 'crypto', 15 | 'type': '<(library)', 16 | 'sources': [ 17 | '<@(crypto_sources)', 18 | ], 19 | 'target_defaults': { 20 | 'cflags_cc+': [ 21 | ], 22 | 'xcode_settings': { 23 | }, 24 | }, 25 | 'direct_dependent_settings': { 26 | 'include_dirs': [ 27 | '..', 28 | ], 29 | }, 30 | 'dependencies': [ 31 | '../bare/bare.gyp:bare', 32 | ], 33 | }, 34 | { 35 | # Used by unit tests for code that uses the crypto library. 36 | 'target_name': 'crypto_testing', 37 | 'type': '<(library)', 38 | 'sources': [ 39 | '<@(crypto_sources)', 40 | ], 41 | 'dependencies': [ 42 | '../bare/bare.gyp:bare_testing', 43 | '../deps/gtest.gyp:gtest', 44 | '../deps/libcxx.gyp:libc++', 45 | ], 46 | 'direct_dependent_settings': { 47 | 'include_dirs': [ 48 | '..', 49 | ], 50 | }, 51 | }, 52 | { 53 | # Unit tests for the crypto library. 54 | 'target_name': 'crypto_tests', 55 | 'type': 'executable', 56 | 'sources': [ 57 | 'hash_test.cc', 58 | ], 59 | 'dependencies': [ 60 | ':crypto_testing', 61 | '../bare/bare.gyp:bare_testing', 62 | '../deps/gtest.gyp:gtest', 63 | '../deps/libcxx.gyp:libc++', 64 | ], 65 | }, 66 | ], 67 | } 68 | -------------------------------------------------------------------------------- /src/crypto/hash.cc: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | 3 | #include "bare/base_types.h" 4 | #include "bare/bit_masking.h" 5 | #include "bare/memory.h" 6 | 7 | using sanctum::bare::bcopy; 8 | using sanctum::bare::bzero; 9 | using sanctum::bare::is_big_endian; 10 | using sanctum::bare::phys_ptr; 11 | using sanctum::bare::reverse_bytes; 12 | using sanctum::bare::size_t; 13 | using sanctum::bare::uint32_t; 14 | using sanctum::bare::uintptr_t; 15 | 16 | namespace { 17 | 18 | // SHA-256 constants table. 19 | static uint32_t hash_k[] = { 20 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 21 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 22 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 23 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 24 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 25 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 26 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 27 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 28 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 29 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 30 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 31 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 32 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 33 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 34 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 35 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 36 | }; 37 | 38 | static_assert(sizeof(hash_k) == 64 * sizeof(uint32_t), 39 | "Incorrectly sized constant table"); 40 | 41 | constexpr inline uint32_t rotate_right(uint32_t value, size_t size) { 42 | return (value >> size) | (value << (32 - size)); 43 | } 44 | constexpr inline uint32_t clamp_to_32(uint32_t value) { 45 | return (sizeof(uint32_t) == 4) ? value : 46 | (value & ((1 << 31) | ((1 << 31) - 1))); 47 | } 48 | constexpr inline uint32_t to_big_endian(uint32_t value) { 49 | return is_big_endian() ? value : reverse_bytes(value); 50 | } 51 | 52 | }; // anonymous namespace 53 | 54 | namespace sanctum { 55 | namespace crypto { // sanctum::crypto 56 | 57 | void init_hash(phys_ptr state) { 58 | (state->*(&hash_state_t::h))[0] = 0x6a09e667; 59 | (state->*(&hash_state_t::h))[1] = 0xbb67ae85; 60 | (state->*(&hash_state_t::h))[2] = 0x3c6ef372; 61 | (state->*(&hash_state_t::h))[3] = 0xa54ff53a; 62 | (state->*(&hash_state_t::h))[4] = 0x510e527f; 63 | (state->*(&hash_state_t::h))[5] = 0x9b05688c; 64 | (state->*(&hash_state_t::h))[6] = 0x1f83d9ab; 65 | (state->*(&hash_state_t::h))[7] = 0x5be0cd19; 66 | 67 | bzero(state->*(&hash_state_t::padding), hash_block_size); 68 | 69 | uintptr_t padding_addr = uintptr_t(state->*(&hash_state_t::padding)); 70 | phys_ptr padding_start{padding_addr}; 71 | *padding_start = 72 | to_big_endian((static_cast(1) << (sizeof(uint32_t) * 8 - 1))); 73 | } 74 | 75 | void extend_hash(phys_ptr state, phys_ptr block) { 76 | phys_ptr hptr = state->*(&hash_state_t::h); 77 | uint32_t a = hptr[0]; 78 | uint32_t b = hptr[1]; 79 | uint32_t c = hptr[2]; 80 | uint32_t d = hptr[3]; 81 | uint32_t e = hptr[4]; 82 | uint32_t f = hptr[5]; 83 | uint32_t g = hptr[6]; 84 | uint32_t h = hptr[7]; 85 | 86 | phys_ptr w = state->*(&hash_state_t::work_area); 87 | for (size_t i = 0; i < 64; ++i) { 88 | uint32_t wi; 89 | if (i < 16) { 90 | wi = w[i & 0xf] = to_big_endian(block[i]); 91 | } else { 92 | uint32_t w15 = w[(i - 15) & 0xf], w2 = w[(i - 2) & 0xf]; 93 | uint32_t gamma0 = rotate_right(w15, 7) ^ rotate_right(w15, 18) ^ 94 | clamp_to_32(w15 >> 3); 95 | uint32_t gamma1 = rotate_right(w2, 17) ^ rotate_right(w2, 19) ^ 96 | clamp_to_32(w2 >> 10); 97 | uint32_t w7 = w[(i - 7) & 0xf]; 98 | uint32_t ii = i & 0xf; 99 | wi = w[ii] = clamp_to_32(w[ii] + gamma0 + w7 + gamma1); 100 | } 101 | 102 | uint32_t ch = (e & f) ^ ((~e) & g); 103 | uint32_t maj = (a & b) ^ (a & c) ^ (b & c); 104 | uint32_t sigma0 = rotate_right(a, 2) ^ rotate_right(a, 13) ^ 105 | rotate_right(a, 22); 106 | uint32_t sigma1 = rotate_right(e, 6) ^ rotate_right(e, 11) ^ 107 | rotate_right(e, 25); 108 | uint32_t temp1 = h + sigma1 + ch + hash_k[i] + wi; 109 | uint32_t temp2 = sigma0 + maj; 110 | 111 | h = g; g = f; f = e; 112 | e = d + temp1; 113 | d = c; c = b; b = a; 114 | a = temp1 + temp2; 115 | } 116 | 117 | hptr[0] = clamp_to_32(hptr[0] + a); 118 | hptr[1] = clamp_to_32(hptr[1] + b); 119 | hptr[2] = clamp_to_32(hptr[2] + c); 120 | hptr[3] = clamp_to_32(hptr[3] + d); 121 | hptr[4] = clamp_to_32(hptr[4] + e); 122 | hptr[5] = clamp_to_32(hptr[5] + f); 123 | hptr[6] = clamp_to_32(hptr[6] + g); 124 | hptr[7] = clamp_to_32(hptr[7] + h); 125 | 126 | // The bit counter is the last 64 bits of the hash. 127 | uintptr_t counter_addr = uintptr_t(state->*(&hash_state_t::padding)) + 128 | hash_block_size - 4; 129 | phys_ptr counter_ptr{counter_addr}; 130 | 131 | // TODO: handle overflow 132 | uint32_t hashed_bits = to_big_endian(*counter_ptr); 133 | *counter_ptr = to_big_endian(hashed_bits + hash_block_size * 8); 134 | } 135 | 136 | void finalize_hash(phys_ptr state) { 137 | uintptr_t padding_addr = uintptr_t(state->*(&hash_state_t::padding)); 138 | extend_hash(state, phys_ptr{padding_addr}); 139 | } 140 | 141 | }; // namespace sanctum::crypto 142 | }; // namespace sanctum 143 | -------------------------------------------------------------------------------- /src/crypto/hash.h: -------------------------------------------------------------------------------- 1 | #if !defined(CRYPTO_HASH_H_INCLUDED) 2 | #define CRYPTO_HASH_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/phys_ptr.h" 6 | 7 | namespace sanctum { 8 | namespace crypto { 9 | 10 | using sanctum::bare::phys_ptr; 11 | using sanctum::bare::size_t; 12 | using sanctum::bare::uint32_t; 13 | using sanctum::bare::uintptr_t; 14 | 15 | // The block size for the hashing algorithm, in bytes. 16 | // 17 | // The hashing algorithm implemented here processes data in blocks. 18 | // 19 | // The block size is guaranteed to be a multiple of the sizes of size_t and 20 | // uintptr_t. 21 | constexpr size_t hash_block_size = 64; // 512 bits 22 | static_assert(hash_block_size % sizeof(size_t) == 0, 23 | "hash_block_size not a multiple of size_t"); 24 | static_assert(hash_block_size % sizeof(uintptr_t) == 0, 25 | "hash_block_size not a multiple of uintptr_t"); 26 | 27 | // The size of the result of a hashing operation. 28 | constexpr size_t hash_result_size = 32; // 256 bits 29 | 30 | // The data structure used by the SHA-256 funtions. 31 | struct hash_state_t { 32 | uint32_t h[8]; // Stores the final hash. 33 | uint32_t work_area[16]; // Stores the message schedule. 34 | size_t padding[hash_block_size / sizeof(size_t)]; // The padding block. 35 | }; 36 | 37 | // Initializes a hashing data structure. 38 | // 39 | // This must be called before extend_hash() is used. 40 | void init_hash(phys_ptr state); 41 | 42 | // Extends a crypto hash by a block. 43 | // 44 | // The block is exactly hash_block_size bytes. init_hash() must be called to 45 | // set up the hashing data structure before this function is used. 46 | void extend_hash(phys_ptr state, phys_ptr block); 47 | 48 | // Finalizes the value in a hashing data structure. 49 | // 50 | // After this is called, extend_hash() must not be called again. The final hash 51 | // value is stored in the first hash_result_size bytes of the hash_state 52 | // structure. 53 | void finalize_hash(phys_ptr state); 54 | 55 | }; // namespace sanctum::crypto 56 | }; // namespace sanctum 57 | #endif // !defined(CRYPTO_HASH_H_INCLUDED) 58 | -------------------------------------------------------------------------------- /src/crypto/hash_test.cc: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | using sanctum::bare::phys_ptr; 6 | using sanctum::bare::uint32_t; 7 | using sanctum::crypto::extend_hash; 8 | using sanctum::crypto::finalize_hash; 9 | using sanctum::crypto::hash_block_size; 10 | using sanctum::crypto::hash_state_t; 11 | using sanctum::crypto::init_hash; 12 | using sanctum::testing::phys_buffer; 13 | using sanctum::testing::phys_buffer_size; 14 | 15 | TEST(HashTest, EmptyString) { 16 | uintptr_t hash_addr = 160; 17 | 18 | ASSERT_LE(160 + sizeof(hash_state_t), phys_buffer_size); 19 | 20 | phys_ptr state{hash_addr}; 21 | init_hash(state); 22 | finalize_hash(state); 23 | 24 | phys_ptr h = state->*(&hash_state_t::h); 25 | EXPECT_EQ(0xe3b0c442, h[0]); 26 | EXPECT_EQ(0x98fc1c14, h[1]); 27 | EXPECT_EQ(0x9afbf4c8, h[2]); 28 | EXPECT_EQ(0x996fb924, h[3]); 29 | EXPECT_EQ(0x27ae41e4, h[4]); 30 | EXPECT_EQ(0x649b934c, h[5]); 31 | EXPECT_EQ(0xa495991b, h[6]); 32 | EXPECT_EQ(0x7852b855, h[7]); 33 | } 34 | 35 | TEST(HashTest, BlockOfA) { 36 | uintptr_t hash_addr = 160; 37 | uintptr_t block_addr = 400; 38 | 39 | ASSERT_LE(400 + hash_block_size, phys_buffer_size); 40 | memset(phys_buffer + 400, 'A', hash_block_size); 41 | 42 | phys_ptr state{hash_addr}; 43 | init_hash(state); 44 | phys_ptr block{block_addr}; 45 | extend_hash(state, block); 46 | finalize_hash(state); 47 | 48 | phys_ptr h = state->*(&hash_state_t::h); 49 | EXPECT_EQ(0xd53eda7a, h[0]); 50 | EXPECT_EQ(0x637c99cc, h[1]); 51 | EXPECT_EQ(0x7fb566d9, h[2]); 52 | EXPECT_EQ(0x6e9fa109, h[3]); 53 | EXPECT_EQ(0xbf15c478, h[4]); 54 | EXPECT_EQ(0x410a3f5e, h[5]); 55 | EXPECT_EQ(0xb4d4c4e2, h[6]); 56 | EXPECT_EQ(0x6cd081f6, h[7]); 57 | } 58 | 59 | TEST(HashTest, BlockOfLetters) { 60 | uintptr_t hash_addr = 160; 61 | uintptr_t block_addr = 400; 62 | char block_data[] = 63 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq01234567"; 64 | 65 | ASSERT_LE(400 + hash_block_size, phys_buffer_size); 66 | memcpy(phys_buffer + 400, block_data, hash_block_size); 67 | 68 | phys_ptr state{hash_addr}; 69 | init_hash(state); 70 | phys_ptr block{block_addr}; 71 | extend_hash(state, block); 72 | finalize_hash(state); 73 | 74 | phys_ptr h = state->*(&hash_state_t::h); 75 | EXPECT_EQ(0x9e411773, h[0]); 76 | EXPECT_EQ(0x2130ab9d, h[1]); 77 | EXPECT_EQ(0xc766d118, h[2]); 78 | EXPECT_EQ(0x4ecb8dcc, h[3]); 79 | EXPECT_EQ(0xf82bc6a4, h[4]); 80 | EXPECT_EQ(0x2a12378a, h[5]); 81 | EXPECT_EQ(0x0fd067b9, h[6]); 82 | EXPECT_EQ(0x5d04ebff, h[7]); 83 | } 84 | 85 | TEST(HashTest, PageOfU) { 86 | uintptr_t hash_addr = 160; 87 | uintptr_t block_addr = 400; 88 | 89 | ASSERT_LE(400 + 4096, phys_buffer_size); 90 | memset(phys_buffer + 400, 'U', 4096); 91 | 92 | phys_ptr state{hash_addr}; 93 | init_hash(state); 94 | for (size_t i = 0; i < 4096; i += hash_block_size) { 95 | phys_ptr block{block_addr + i}; 96 | extend_hash(state, block); 97 | } 98 | finalize_hash(state); 99 | 100 | phys_ptr h = state->*(&hash_state_t::h); 101 | EXPECT_EQ(0x0561079e, h[0]); 102 | EXPECT_EQ(0x4fe3390b, h[1]); 103 | EXPECT_EQ(0xc1d8bb70, h[2]); 104 | EXPECT_EQ(0x6edb7d80, h[3]); 105 | EXPECT_EQ(0x243eeca7, h[4]); 106 | EXPECT_EQ(0xddf876ce, h[5]); 107 | EXPECT_EQ(0xfbaa8c16, h[6]); 108 | EXPECT_EQ(0x84db80c3, h[7]); 109 | } 110 | -------------------------------------------------------------------------------- /src/deps/gtest.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'targets': [ 6 | { 7 | 'target_name': 'gtest', 8 | 'type': '<(library)', 9 | 'sources': [ 10 | '../../deps/googletest/googletest/src/gtest.cc', 11 | '../../deps/googletest/googletest/src/gtest-death-test.cc', 12 | '../../deps/googletest/googletest/src/gtest-filepath.cc', 13 | '../../deps/googletest/googletest/src/gtest-port.cc', 14 | '../../deps/googletest/googletest/src/gtest-printers.cc', 15 | '../../deps/googletest/googletest/src/gtest-test-part.cc', 16 | '../../deps/googletest/googletest/src/gtest-typed-test.cc', 17 | ], 18 | 'include_dirs': [ 19 | '../../deps/googletest/googletest/include', 20 | '../../deps/googletest/googletest', 21 | ], 22 | 'direct_dependent_settings': { 23 | 'include_dirs': [ 24 | '../../deps/googletest/googletest/include', 25 | ] 26 | }, 27 | 'dependencies': [ 28 | 'libcxx.gyp:libc++', 29 | ], 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/deps/libcxx.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'targets': [ 6 | { 7 | 'target_name': 'libc++', 8 | 'type': '<(library)', 9 | 'dependencies=': [ 10 | 'libcxxabi.gyp:libc++abi', 11 | ], 12 | 'sources': [ 13 | '../../deps/libcxx/src/algorithm.cpp', 14 | '../../deps/libcxx/src/any.cpp', 15 | '../../deps/libcxx/src/bind.cpp', 16 | '../../deps/libcxx/src/chrono.cpp', 17 | '../../deps/libcxx/src/condition_variable.cpp', 18 | '../../deps/libcxx/src/debug.cpp', 19 | '../../deps/libcxx/src/exception.cpp', 20 | '../../deps/libcxx/src/future.cpp', 21 | '../../deps/libcxx/src/hash.cpp', 22 | '../../deps/libcxx/src/ios.cpp', 23 | '../../deps/libcxx/src/iostream.cpp', 24 | '../../deps/libcxx/src/locale.cpp', 25 | '../../deps/libcxx/src/memory.cpp', 26 | '../../deps/libcxx/src/mutex.cpp', 27 | '../../deps/libcxx/src/new.cpp', 28 | '../../deps/libcxx/src/optional.cpp', 29 | '../../deps/libcxx/src/random.cpp', 30 | '../../deps/libcxx/src/regex.cpp', 31 | '../../deps/libcxx/src/shared_mutex.cpp', 32 | '../../deps/libcxx/src/stdexcept.cpp', 33 | '../../deps/libcxx/src/string.cpp', 34 | '../../deps/libcxx/src/strstream.cpp', 35 | '../../deps/libcxx/src/system_error.cpp', 36 | '../../deps/libcxx/src/thread.cpp', 37 | '../../deps/libcxx/src/typeinfo.cpp', 38 | '../../deps/libcxx/src/utility.cpp', 39 | '../../deps/libcxx/src/valarray.cpp', 40 | ], 41 | 'cflags': [ 42 | '-fPIC', 43 | '-fstrict-aliasing', 44 | '-Wall', 45 | '-Wextra', 46 | '-Wshadow', 47 | '-Wconversion', 48 | '-Wnewline-eof', 49 | '-Wpadded', 50 | '-Wmissing-prototypes', 51 | '-Wstrict-aliasing=2', 52 | '-Wstrict-overflow=4', 53 | ], 54 | 'cflags_cc': [ 55 | '-nostdinc++', 56 | ], 57 | 'include_dirs': [ 58 | '../../deps/libcxx/include', 59 | '../../deps/libcxxabi/include', 60 | ], 61 | 'ldflags': [ 62 | '-nodefaultlibs', 63 | ], 64 | 'link_settings': { 65 | 'libraries': [ 66 | '-lpthread', 67 | '-lc', 68 | '-lm', 69 | ], 70 | }, 71 | 'direct_dependent_settings': { 72 | 'include_dirs': [ 73 | '../../deps/libcxx/include', 74 | ], 75 | }, 76 | 'all_dependent_settings': { 77 | 'cflags_cc': [ 78 | '-nostdinc++', 79 | ], 80 | 'ldflags': [ 81 | '-nodefaultlibs', 82 | ], 83 | }, 84 | }, 85 | ], 86 | } 87 | -------------------------------------------------------------------------------- /src/deps/libcxxabi.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'targets': [ 6 | { 7 | 'target_name': 'libc++abi', 8 | 'type': '<(library)', 9 | 'dependencies=': [], 10 | 'sources': [ 11 | '../../deps/libcxxabi/src/abort_message.cpp', 12 | '../../deps/libcxxabi/src/cxa_aux_runtime.cpp', 13 | '../../deps/libcxxabi/src/cxa_default_handlers.cpp', 14 | '../../deps/libcxxabi/src/cxa_demangle.cpp', 15 | '../../deps/libcxxabi/src/cxa_exception.cpp', 16 | '../../deps/libcxxabi/src/cxa_exception_storage.cpp', 17 | '../../deps/libcxxabi/src/cxa_guard.cpp', 18 | '../../deps/libcxxabi/src/cxa_handlers.cpp', 19 | '../../deps/libcxxabi/src/cxa_new_delete.cpp', 20 | '../../deps/libcxxabi/src/cxa_personality.cpp', 21 | '../../deps/libcxxabi/src/cxa_thread_atexit.cpp', 22 | '../../deps/libcxxabi/src/cxa_unexpected.cpp', 23 | '../../deps/libcxxabi/src/cxa_vector.cpp', 24 | '../../deps/libcxxabi/src/cxa_virtual.cpp', 25 | '../../deps/libcxxabi/src/exception.cpp', 26 | '../../deps/libcxxabi/src/private_typeinfo.cpp', 27 | '../../deps/libcxxabi/src/stdexcept.cpp', 28 | '../../deps/libcxxabi/src/typeinfo.cpp', 29 | ], 30 | 'cflags': [ 31 | '-fPIC', 32 | '-fstrict-aliasing', 33 | '-Wno-unused-function', 34 | ], 35 | 'cflags_cc': [ 36 | '-nostdinc++', 37 | '-std=c++11', 38 | ], 39 | 'include_dirs': [ 40 | '../../deps/libcxx/include', 41 | '../../deps/libcxxabi/include', 42 | ], 43 | 'ldflags': [ 44 | '-nodefaultlibs', 45 | ], 46 | 'link_settings': { 47 | 'libraries': [ 48 | '-lpthread', 49 | '-lc', 50 | ], 51 | 'conditions': [ 52 | ['OS!="mac"', { 53 | 'libraries': [ 54 | '-lgcc', 55 | '-lgcc_s', 56 | ], 57 | }], 58 | ], 59 | }, 60 | 'direct_dependent_settings': { 61 | 'include_dirs': [ 62 | '../../deps/libcxxabi/include', 63 | ], 64 | }, 65 | 'all_dependent_settings': { 66 | 'cflags_cc': [ 67 | '-nostdinc++', 68 | ], 69 | 'ldflags': [ 70 | '-nodefaultlibs', 71 | ], 72 | }, 73 | }, 74 | ], 75 | } 76 | -------------------------------------------------------------------------------- /src/main.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | 'common.gypi', 4 | ], 5 | 'targets': [ 6 | { 7 | 'target_name': 'tests', 8 | 'type': 'none', 9 | 'dependencies': [ 10 | 'bare/bare.gyp:bare_tests', 11 | 'crypto/crypto.gyp:crypto_tests', 12 | 'monitor/monitor.gyp:monitor_tests', 13 | ], 14 | }, 15 | { 16 | 'target_name': 'libs', 17 | 'type': 'none', 18 | 'dependencies': [ 19 | 'bare/bare.gyp:bare', 20 | 'crypto/crypto.gyp:crypto', 21 | #'monitor/monitor.gyp:monitor', 22 | ], 23 | } 24 | ], 25 | } 26 | -------------------------------------------------------------------------------- /src/monitor/api_compile_check.cc: -------------------------------------------------------------------------------- 1 | // This file is here to ensure that the public/api.h header compiles without 2 | // any dependencies. 3 | 4 | #include "bare/base_types.h" 5 | 6 | using size_t = sanctum::bare::size_t; 7 | using uintptr_t = sanctum::bare::uintptr_t; 8 | using uint8_t = sanctum::bare::uint8_t; 9 | 10 | #include "public/api.h" 11 | -------------------------------------------------------------------------------- /src/monitor/boot_init.cc: -------------------------------------------------------------------------------- 1 | #include "boot_init.h" 2 | 3 | #include "bare/bit_masking.h" 4 | #include "bare/memory.h" 5 | #include "cpu_core.h" 6 | #include "dram_regions.h" 7 | #include "dram_regions_inl.h" 8 | #include "enclave.h" 9 | #include "metadata_inl.h" 10 | 11 | using sanctum::api::null_enclave_id; 12 | using sanctum::api::os::dram_region_owned; 13 | using sanctum::bare::atomic_flag_clear; 14 | using sanctum::bare::atomic_init; 15 | using sanctum::bare::address_bits_for; 16 | using sanctum::bare::ceil_power_of_two; 17 | using sanctum::bare::is_shared_cache; 18 | using sanctum::bare::page_shift; 19 | using sanctum::bare::page_size; 20 | using sanctum::bare::pages_needed_for; 21 | using sanctum::bare::read_cache_levels; 22 | using sanctum::bare::read_cache_line_size; 23 | using sanctum::bare::read_cache_set_count; 24 | using sanctum::bare::read_core_count; 25 | using sanctum::bare::read_dram_size; 26 | using sanctum::bare::read_min_cache_index_shift; 27 | using sanctum::bare::read_max_cache_index_shift; 28 | using sanctum::bare::set_cache_index_shift; 29 | using sanctum::bare::set_drb_map; 30 | using sanctum::bare::set_dmar_base; 31 | using sanctum::bare::set_dmar_mask; 32 | using sanctum::bare::set_par_base; 33 | using sanctum::bare::set_par_mask; 34 | using sanctum::bare::size_t; 35 | using sanctum::bare::uintptr_t; 36 | 37 | namespace sanctum { 38 | namespace internal { // sanctum::internal 39 | 40 | uintptr_t g_monitor_top; 41 | 42 | void boot_panic() { 43 | // TODO: come up with a better way to signal panic 44 | while(true) { } 45 | } 46 | 47 | void boot_init_monitor_top() { 48 | // TODO: implement this by reading some linker symbol 49 | g_monitor_top = 0; 50 | } 51 | 52 | void boot_init_dram_regions() { 53 | constexpr size_t bits_in_size_t = sizeof(size_t) * 8; 54 | 55 | g_dram_size = read_dram_size(); 56 | size_t dram_address_bits = address_bits_for(g_dram_size); 57 | 58 | size_t cache_levels = read_cache_levels(); 59 | for (size_t i = 0; i < cache_levels; ++i) { 60 | if (is_shared_cache(i) && i != cache_levels - 1) 61 | boot_panic(); // Sanctum assumes that only the LLC is shared. 62 | } 63 | size_t llc = cache_levels - 1; 64 | 65 | size_t line_size = read_cache_line_size(llc); 66 | size_t line_bits = address_bits_for(line_size); 67 | if (line_size != (1 << line_bits)) 68 | boot_panic(); // Sanctum assumes power-of-two cache line sizes. 69 | 70 | size_t set_count = read_cache_set_count(llc); 71 | size_t set_bits = address_bits_for(set_count); 72 | if (set_count != (1 << set_bits)) 73 | boot_panic(); // Sanctum assumes power-of-two cache set counts. 74 | 75 | size_t cache_bits = set_bits + line_bits; 76 | if (cache_bits <= page_shift()) 77 | boot_panic(); // Address translation doesn't touch any cache indexing bit. 78 | size_t region_bits = cache_bits - page_shift(); 79 | 80 | size_t stripe_page_bits = dram_address_bits - cache_bits; 81 | size_t max_shift = read_max_cache_index_shift(); 82 | if (stripe_page_bits > max_shift) { 83 | // DRAM regions will not be continuous. 84 | stripe_page_bits = max_shift; 85 | } 86 | size_t min_shift = read_min_cache_index_shift(); 87 | if (stripe_page_bits < min_shift) { 88 | // DRAM can't use the entire cache and some regions are invalid. 89 | // 90 | // It's not worth dealing with this case, not taking advantage of the 91 | // entire cache is pretty insane from a performance standpoint. 92 | boot_panic(); // Sanctum assumes that DRAM can use the entire cache. 93 | } 94 | 95 | // TODO: figure out if this requires coordination between cores. 96 | set_cache_index_shift(stripe_page_bits); 97 | 98 | g_dram_region_shift = page_shift() + stripe_page_bits; 99 | g_dram_stripe_shift = g_dram_region_shift + region_bits; 100 | 101 | g_dram_stripe_size = 1 << g_dram_region_shift; 102 | g_dram_stripe_pages = 1 << stripe_page_bits; 103 | g_dram_region_count = 1 << region_bits; 104 | 105 | g_dram_stripe_page_mask = ((1 << stripe_page_bits) - 1) << page_shift(); 106 | g_dram_region_mask = (g_dram_region_count - 1) << g_dram_region_shift; 107 | g_dram_stripe_mask = 108 | (g_dram_size - 1) >> g_dram_stripe_shift << g_dram_stripe_shift; 109 | 110 | // NOTE: relying on the compiler to optimize division to bitwise shift 111 | g_dram_region_bitmap_words = 112 | (g_dram_region_count + bits_in_size_t - 1) / bits_in_size_t; 113 | } 114 | 115 | void boot_init_metadata() { 116 | g_metadata_region_pages = g_dram_size >> 117 | (g_dram_stripe_shift - g_dram_region_shift + page_shift()); 118 | 119 | // NOTE: relying on the compiler to optimize multiplication to bitwise shift 120 | size_t metadata_map_size = 121 | g_metadata_region_pages * sizeof(metadata_page_info_t); 122 | 123 | // This monitor implementation assumes that the metadata map fits into a 124 | // single DRAM stripe. In the unlikely instance of huge DRAM regions with 125 | // tiny stripes, we give up some of the metadata region capacity in order to 126 | // make the invariant hold. 127 | if (metadata_map_size > g_dram_stripe_size) { 128 | metadata_map_size = g_dram_stripe_size; 129 | // NOTE: relying on the compiler to optimize division to bitwise shift 130 | g_metadata_region_pages = metadata_map_size / sizeof(metadata_page_info_t); 131 | } 132 | 133 | g_metadata_region_start = pages_needed_for(metadata_map_size); 134 | } 135 | 136 | void boot_init_dynamic_arrays() { 137 | g_core_count = read_core_count(); 138 | g_core = phys_ptr{g_monitor_top}; 139 | g_monitor_top = static_cast(g_core + g_core_count); 140 | 141 | g_dram_region = phys_ptr{g_monitor_top}; 142 | g_monitor_top = static_cast(g_dram_region + g_dram_region_count); 143 | for (size_t i = 0; i < g_dram_region_count; ++i) { 144 | phys_ptr region{g_dram_region + i}; 145 | atomic_flag_clear(&(region->*(&dram_region_info_t::lock))); 146 | region->*(&dram_region_info_t::owner) = null_enclave_id; 147 | region->*(&dram_region_info_t::previous_owner) = null_enclave_id; 148 | region->*(&dram_region_info_t::pinned_pages) = 0; 149 | region->*(&dram_region_info_t::blocked_at) = 0; 150 | } 151 | 152 | g_dram_regions = phys_ptr{g_monitor_top}; 153 | g_monitor_top = static_cast(g_dram_regions + 1); 154 | atomic_init(&(g_dram_regions->*(&dram_regions_info_t::block_clock)), 155 | static_cast(0)); 156 | 157 | g_os_region_bitmap = phys_ptr{g_monitor_top}; 158 | g_monitor_top = static_cast( 159 | g_os_region_bitmap + g_dram_region_bitmap_words); 160 | for (size_t i = 0; i < g_dram_region_count; ++i) 161 | set_bitmap_bit(g_os_region_bitmap, i, 1); 162 | } 163 | 164 | void boot_init_protection() { 165 | // The monitor's code and data will be covered by a range in the address 166 | // translation, so we must round it up to a power of two. 167 | g_monitor_top = ceil_power_of_two(g_monitor_top); 168 | set_par_base(static_cast(0)); 169 | set_par_mask(~(static_cast(g_monitor_top - 1))); 170 | set_drb_map(uintptr_t(g_os_region_bitmap)); 171 | 172 | if (g_monitor_top > g_dram_stripe_size) 173 | boot_panic(); // Sanctum assumes that the monitor fits into a DRAM stripe. 174 | 175 | // NOTE: we're allowing DMA transfers for 1 byte at the top of the monitor. 176 | g_dma_range_start = g_monitor_top; 177 | g_dma_range_end = g_dma_range_start + 1; 178 | set_dmar_base(g_dma_range_start); 179 | set_dmar_mask(~(static_cast(0))); 180 | } 181 | 182 | }; // namespace sanctum::internal 183 | }; // namespace sanctum 184 | -------------------------------------------------------------------------------- /src/monitor/boot_init.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_BOOT_INIT_H_INCLUDED) 2 | #define MONITOR_BOOT_INIT_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | 6 | namespace sanctum { 7 | namespace internal { 8 | 9 | using sanctum::bare::uintptr_t; 10 | 11 | // The top of the memory used by the security monitor. 12 | // 13 | // This is used to track memory allocation during boot. It remains constant 14 | // after the boot initialization. 15 | extern uintptr_t g_monitor_top; 16 | 17 | // Computes the initial top of monitor memory, based on the monitor's header. 18 | void boot_init_monitor_top(); 19 | 20 | // Sets up DRAM regions. 21 | // 22 | // This computes the constants related to DRAM regions and sets the cache shift 23 | // index. 24 | void boot_init_dram_regions(); 25 | 26 | // Computes the constants related to enclave metadata DRAM regions. 27 | // 28 | // This must be called after the DRAM region constants are set. 29 | void boot_init_metadata(); 30 | 31 | // Allocates the monitor arrays whose sizes depend on system parameters. 32 | // 33 | // This must be called after the top of monitor memory and the DRAM region 34 | // constants are set. 35 | void boot_init_dynamic_arrays(); 36 | 37 | // Sets up the hardware registers needed to protect the monitor from the OS. 38 | // 39 | // This must be called after all the monitor's memory is allocated. 40 | void boot_init_protection(); 41 | 42 | // Stops the monitor's boot process. 43 | // 44 | // This is called when the monitor concludes that the platform violates a core 45 | // assumption built into the monitor. 46 | void boot_panic(); 47 | 48 | }; // namespace sanctum::internal 49 | }; // namespace sanctum 50 | #endif // !defined(MONITOR_BOOT_INIT_H_INCLUDED) 51 | -------------------------------------------------------------------------------- /src/monitor/boot_init_test.cc: -------------------------------------------------------------------------------- 1 | #include "boot_init.h" 2 | 3 | #include "bare/bit_masking.h" 4 | #include "bare/cpu_context.h" 5 | #include "bare/memory.h" 6 | #include "bare/page_tables.h" 7 | #include "cpu_core.h" 8 | #include "dram_regions.h" 9 | #include "enclave.h" 10 | #include "metadata.h" 11 | 12 | #include "gtest/gtest.h" 13 | 14 | using sanctum::bare::current_core; 15 | using sanctum::bare::is_power_of_two; 16 | using sanctum::bare::page_size; 17 | using sanctum::bare::page_shift; 18 | using sanctum::bare::phys_ptr; 19 | using sanctum::internal::boot_init_dram_regions; 20 | using sanctum::internal::boot_init_dynamic_arrays; 21 | using sanctum::internal::boot_init_metadata; 22 | using sanctum::internal::boot_init_protection; 23 | using sanctum::internal::core_info_t; 24 | using sanctum::internal::dram_region_info_t; 25 | using sanctum::internal::g_core; 26 | using sanctum::internal::g_core_count; 27 | using sanctum::internal::g_dma_range_end; 28 | using sanctum::internal::g_dma_range_start; 29 | using sanctum::internal::g_dram_region; 30 | using sanctum::internal::g_dram_regions; 31 | using sanctum::internal::g_dram_region_bitmap_words; 32 | using sanctum::internal::g_dram_region_count; 33 | using sanctum::internal::g_dram_region_mask; 34 | using sanctum::internal::g_dram_region_shift; 35 | using sanctum::internal::g_dram_size; 36 | using sanctum::internal::g_dram_stripe_mask; 37 | using sanctum::internal::g_dram_stripe_pages; 38 | using sanctum::internal::g_dram_stripe_page_mask; 39 | using sanctum::internal::g_dram_stripe_shift; 40 | using sanctum::internal::g_dram_stripe_size; 41 | using sanctum::internal::g_metadata_region_pages; 42 | using sanctum::internal::g_metadata_region_start; 43 | using sanctum::internal::g_monitor_top; 44 | using sanctum::internal::g_os_region_bitmap; 45 | 46 | namespace { 47 | 48 | // Sets up the test rig with the toy memory parameters from the Sanctum paper. 49 | void set_up_paper_memory_model() { 50 | sanctum::testing::dram_size = 1 << 18; 51 | ASSERT_GE(sanctum::testing::phys_buffer_size, sanctum::testing::dram_size); 52 | sanctum::testing::cache_levels = 3; 53 | 54 | sanctum::testing::is_shared_cache[0] = false; 55 | sanctum::testing::is_shared_cache[1] = false; 56 | sanctum::testing::is_shared_cache[2] = true; 57 | 58 | sanctum::testing::cache_line_size[0] = 1 << 6; // irrelevant to tests 59 | sanctum::testing::cache_line_size[1] = 1 << 6; // irrelevant to tests 60 | sanctum::testing::cache_line_size[2] = 1 << 6; // must be a power of 2 61 | 62 | sanctum::testing::cache_set_count[0] = 1 << 6; // irrelevant to tests 63 | sanctum::testing::cache_set_count[1] = 1 << 8; // irrelevant to tests 64 | sanctum::testing::cache_set_count[2] = 1 << 9; // must be a power of 2 65 | 66 | sanctum::testing::min_cache_index_shift = 0; 67 | sanctum::testing::max_cache_index_shift = 16; 68 | 69 | sanctum::testing::set_core_count(4); 70 | } 71 | 72 | void check_dram_geometry_invariants() { 73 | constexpr size_t page_mask = page_size() - 1; 74 | 75 | // The masks must be disjoint. 76 | ASSERT_EQ(page_mask & g_dram_stripe_page_mask, 0); 77 | if (g_dram_stripe_page_mask != 0) 78 | ASSERT_LT(page_mask, g_dram_stripe_page_mask); 79 | ASSERT_EQ(g_dram_stripe_page_mask & g_dram_region_mask, 0); 80 | if (g_dram_stripe_page_mask != 0) 81 | ASSERT_LT(g_dram_stripe_page_mask, g_dram_region_mask); 82 | ASSERT_EQ(g_dram_region_mask & g_dram_stripe_mask, 0); 83 | if (g_dram_stripe_mask != 0) 84 | ASSERT_LT(g_dram_region_mask, g_dram_stripe_mask); 85 | 86 | // The shifts must match the masks. 87 | if (g_dram_stripe_page_mask != 0) { 88 | ASSERT_EQ(g_dram_stripe_page_mask & (1 << page_shift()), 89 | 1 << page_shift()); 90 | ASSERT_GE(g_dram_stripe_page_mask, 1 << page_shift()); 91 | } 92 | ASSERT_EQ(g_dram_region_mask & (1 << g_dram_region_shift), 93 | 1 << g_dram_region_shift); 94 | ASSERT_GE(g_dram_region_mask, 1 << g_dram_region_shift); 95 | if (g_dram_stripe_mask != 0) { 96 | ASSERT_EQ(g_dram_stripe_mask & (1 << g_dram_stripe_shift), 97 | 1 << g_dram_stripe_shift); 98 | ASSERT_GE(g_dram_stripe_mask, 1 << g_dram_stripe_shift); 99 | } 100 | 101 | // The counts must match the masks. 102 | ASSERT_EQ(is_power_of_two(g_dram_region_count), true); 103 | ASSERT_EQ((g_dram_region_mask >> g_dram_region_shift) + 1, 104 | g_dram_region_count); 105 | 106 | // The masks must combine to cover all the DRAM address bits. 107 | ASSERT_EQ(page_mask | g_dram_stripe_page_mask | g_dram_region_mask | 108 | g_dram_stripe_mask, g_dram_size - 1); 109 | 110 | // The sizes must match the shifts. 111 | ASSERT_EQ(is_power_of_two(g_dram_stripe_size), true); 112 | ASSERT_EQ(is_power_of_two(g_dram_stripe_pages), true); 113 | ASSERT_EQ(g_dram_stripe_size, 1 << g_dram_region_shift); 114 | ASSERT_EQ(g_dram_stripe_size >> page_shift(), g_dram_stripe_pages); 115 | } 116 | 117 | }; // anonymous namespace 118 | 119 | TEST(BootInitTest, DramRegionsHappyPath) { 120 | set_up_paper_memory_model(); 121 | sanctum::testing::max_cache_index_shift = 3; 122 | boot_init_dram_regions(); 123 | 124 | ASSERT_EQ(g_dram_region_count, 8); 125 | ASSERT_EQ(g_dram_region_shift, 15); 126 | ASSERT_EQ(g_dram_stripe_shift, 18); 127 | ASSERT_EQ(g_dram_region_bitmap_words, 1); 128 | check_dram_geometry_invariants(); 129 | } 130 | 131 | TEST(BootInitTest, DramRegionsNoShift) { 132 | set_up_paper_memory_model(); 133 | sanctum::testing::max_cache_index_shift = 0; 134 | boot_init_dram_regions(); 135 | 136 | ASSERT_EQ(g_dram_region_count, 8); 137 | ASSERT_EQ(g_dram_region_shift, 12); 138 | ASSERT_EQ(g_dram_stripe_shift, 15); 139 | check_dram_geometry_invariants(); 140 | } 141 | 142 | TEST(BootInitTest, DramRegionsShiftOne) { 143 | set_up_paper_memory_model(); 144 | sanctum::testing::max_cache_index_shift = 1; 145 | boot_init_dram_regions(); 146 | 147 | ASSERT_EQ(g_dram_region_count, 8); 148 | ASSERT_EQ(g_dram_region_shift, 13); 149 | ASSERT_EQ(g_dram_stripe_shift, 16); 150 | check_dram_geometry_invariants(); 151 | } 152 | 153 | TEST(BootInitTest, DramRegionsShiftTwo) { 154 | set_up_paper_memory_model(); 155 | sanctum::testing::max_cache_index_shift = 2; 156 | boot_init_dram_regions(); 157 | 158 | ASSERT_EQ(g_dram_region_count, 8); 159 | ASSERT_EQ(g_dram_region_shift, 14); 160 | ASSERT_EQ(g_dram_stripe_shift, 17); 161 | check_dram_geometry_invariants(); 162 | } 163 | 164 | TEST(BootInitTest, MetadataHappyPath) { 165 | set_up_paper_memory_model(); 166 | boot_init_dram_regions(); 167 | boot_init_metadata(); 168 | 169 | ASSERT_EQ(g_metadata_region_pages, 8); 170 | ASSERT_EQ(g_metadata_region_start, 1); 171 | } 172 | 173 | TEST(BootInitTest, MetadataNoShift) { 174 | set_up_paper_memory_model(); 175 | sanctum::testing::max_cache_index_shift = 0; 176 | boot_init_dram_regions(); 177 | boot_init_metadata(); 178 | 179 | ASSERT_EQ(g_metadata_region_pages, 8); 180 | ASSERT_EQ(g_metadata_region_start, 1); 181 | } 182 | 183 | TEST(BootInitTest, MetadataShiftOne) { 184 | set_up_paper_memory_model(); 185 | sanctum::testing::max_cache_index_shift = 1; 186 | boot_init_dram_regions(); 187 | boot_init_metadata(); 188 | 189 | ASSERT_EQ(g_metadata_region_pages, 8); 190 | ASSERT_EQ(g_metadata_region_start, 1); 191 | } 192 | 193 | TEST(BootInitTest, MetadataShiftTwo) { 194 | set_up_paper_memory_model(); 195 | sanctum::testing::max_cache_index_shift = 2; 196 | boot_init_dram_regions(); 197 | boot_init_metadata(); 198 | 199 | ASSERT_EQ(g_metadata_region_pages, 8); 200 | ASSERT_EQ(g_metadata_region_start, 1); 201 | } 202 | 203 | TEST(BootInitTest, MetadataNoShiftMediumStripes) { 204 | set_up_paper_memory_model(); 205 | sanctum::testing::dram_size = 1 << 22; 206 | sanctum::testing::max_cache_index_shift = 0; 207 | 208 | boot_init_dram_regions(); 209 | ASSERT_EQ(g_dram_region_count, 8); 210 | ASSERT_EQ(g_dram_region_shift, 12); 211 | ASSERT_EQ(g_dram_stripe_shift, 15); 212 | 213 | boot_init_metadata(); 214 | ASSERT_EQ(g_metadata_region_pages, 128); 215 | ASSERT_EQ(g_metadata_region_start, 1); 216 | } 217 | 218 | TEST(BootInitTest, MetadataNoShiftLargeStripes) { 219 | set_up_paper_memory_model(); 220 | // NOTE: The DRAM size below is the minimum required to get the metadata 221 | // region size to the point where the metadata map barely fits into 222 | // a DRAM region stripe. 223 | sanctum::testing::dram_size = 1 << 24; 224 | sanctum::testing::max_cache_index_shift = 0; 225 | 226 | boot_init_dram_regions(); 227 | ASSERT_EQ(g_dram_region_count, 8); 228 | ASSERT_EQ(g_dram_region_shift, 12); 229 | ASSERT_EQ(g_dram_stripe_shift, 15); 230 | 231 | boot_init_metadata(); 232 | // NOTE: The expected page count here should match the expected count in 233 | // the MetadataNoShiftHugeStripes test case. 234 | ASSERT_EQ(g_metadata_region_pages, 512); 235 | ASSERT_EQ(g_metadata_region_start, 1); 236 | } 237 | 238 | TEST(BootInitTest, MetadataNoShiftHugeStripes) { 239 | set_up_paper_memory_model(); 240 | // NOTE: The DRAM size below should be bigger than the size used by the 241 | // MetadataNoShiftLargeStripe test. 242 | sanctum::testing::dram_size = 1 << 28; 243 | sanctum::testing::max_cache_index_shift = 0; 244 | 245 | boot_init_dram_regions(); 246 | ASSERT_EQ(g_dram_region_count, 8); 247 | ASSERT_EQ(g_dram_region_shift, 12); 248 | ASSERT_EQ(g_dram_stripe_shift, 15); 249 | 250 | boot_init_metadata(); 251 | // NOTE: The expected page count here should match the expected count in 252 | // the MetadataNoShiftLargeStripes test case. 253 | ASSERT_EQ(g_metadata_region_pages, 512); 254 | ASSERT_EQ(g_metadata_region_start, 1); 255 | } 256 | 257 | TEST(BootInitTest, DynamicArrays) { 258 | set_up_paper_memory_model(); 259 | 260 | boot_init_dram_regions(); 261 | boot_init_metadata(); 262 | 263 | g_monitor_top = 0x800; 264 | boot_init_dynamic_arrays(); 265 | 266 | ASSERT_EQ(g_core_count, 4); 267 | ASSERT_EQ(static_cast(g_core), static_cast(0x800)); 268 | ASSERT_EQ(static_cast(g_core + 4), 269 | static_cast(g_dram_region)); 270 | ASSERT_EQ(g_dram_region_count, 8); 271 | ASSERT_EQ(static_cast(g_dram_region + 8), 272 | static_cast(g_dram_regions)); 273 | ASSERT_EQ(static_cast(g_dram_regions + 1), 274 | static_cast(g_os_region_bitmap)); 275 | ASSERT_EQ(static_cast(g_os_region_bitmap + 1), g_monitor_top); 276 | } 277 | 278 | TEST(BootInitTest, Protection) { 279 | set_up_paper_memory_model(); 280 | 281 | boot_init_dram_regions(); 282 | boot_init_metadata(); 283 | 284 | g_monitor_top = 0x7363; 285 | boot_init_protection(); 286 | 287 | ASSERT_EQ(current_core(), 0); 288 | ASSERT_EQ(g_monitor_top, 0x8000); 289 | ASSERT_EQ(sanctum::testing::core_par_base[0], 0); 290 | ASSERT_EQ(~sanctum::testing::core_par_mask[0], 0x7fff); 291 | 292 | ASSERT_EQ(g_dma_range_start, 0x8000); 293 | ASSERT_EQ(g_dma_range_end, 0x8001); 294 | ASSERT_EQ(sanctum::testing::dmar_base, 0x8000); 295 | ASSERT_EQ(~sanctum::testing::dmar_mask, 0); 296 | } 297 | -------------------------------------------------------------------------------- /src/monitor/cpu_core.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_core.h" 2 | 3 | #include "cpu_core_inl.h" 4 | 5 | using sanctum::bare::phys_ptr; 6 | using sanctum::bare::size_t; 7 | 8 | namespace sanctum { 9 | namespace internal { 10 | 11 | phys_ptr g_core{0}; 12 | 13 | size_t g_core_count; 14 | 15 | }; // namespace sanctum::internal 16 | }; // namespace sanctum 17 | -------------------------------------------------------------------------------- /src/monitor/cpu_core.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_CPU_CORE_H_INCLUDED) 2 | #define MONITOR_CPU_CORE_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/cpu_context.h" 6 | #include "bare/phys_atomics.h" 7 | #include "public/api.h" 8 | 9 | namespace sanctum { 10 | namespace internal { 11 | 12 | using sanctum::api::enclave_id_t; 13 | using sanctum::api::thread_id_t; 14 | using sanctum::bare::atomic; 15 | using sanctum::bare::current_core; 16 | using sanctum::bare::phys_ptr; 17 | using sanctum::bare::size_t; 18 | using sanctum::bare::uintptr_t; 19 | 20 | struct thread_info_t; 21 | 22 | // Per-core accounting information. 23 | // 24 | // Most information is only modified on the core that it corresponds to, so we 25 | // don't need atomics or locking. The notable exception is the flushed_at 26 | // timestamp. 27 | struct core_info_t { 28 | enclave_id_t enclave_id; // 0 if the core isn't executing enclave code 29 | thread_id_t thread_id; 30 | 31 | // The DRAM backing the thread_info_t is guaranteed to be pinned 32 | // while the thread is executing on a core. 33 | phys_ptr thread; 34 | 35 | // The value of block_clock when this core's TLB was last flushed. 36 | // This is read on other cores, 37 | atomic flushed_at; 38 | }; 39 | 40 | // Core costants. 41 | // 42 | // These values are computed during the boot process. Once computed, the values 43 | // never change. 44 | 45 | extern phys_ptr g_core; 46 | 47 | extern size_t g_core_count; 48 | 49 | }; // namespace sanctum::internal 50 | }; // namespace sanctum 51 | #endif // !defined(MONITOR_CPU_CORE_H_INCLUDED) 52 | -------------------------------------------------------------------------------- /src/monitor/cpu_core_inl.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_CPU_CORE_INL_H_INCLUDED) 2 | #define MONITOR_CPU_CORE_INL_H_INCLUDED 3 | 4 | #include "cpu_core.h" 5 | 6 | namespace sanctum { 7 | namespace internal { 8 | 9 | using sanctum::api::enclave_id_t; 10 | using sanctum::bare::current_core; 11 | using sanctum::bare::phys_ptr; 12 | 13 | // The physical address of the core_info_t for the current core. 14 | inline phys_ptr current_core_info() { 15 | return &g_core[current_core()]; 16 | } 17 | 18 | // The enclave running on the current core. 19 | // 20 | // Returns null_enclave_id if no enclave is running on the current core. This 21 | // implies that the caller is the OS. 22 | inline enclave_id_t current_enclave() { 23 | phys_ptr core_info{current_core_info()}; 24 | return core_info->*(&core_info_t::enclave_id); 25 | } 26 | 27 | }; // namespace sanctum::internal 28 | }; // namespace sanctum 29 | #endif // !defined(MONITOR_CPU_CORE_INL_H_INCLUDED) 30 | -------------------------------------------------------------------------------- /src/monitor/cpu_core_inl_test.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_core_inl.h" 2 | 3 | #include "gtest/gtest.h" 4 | -------------------------------------------------------------------------------- /src/monitor/cpu_core_test.cc: -------------------------------------------------------------------------------- 1 | #include "cpu_core.h" 2 | 3 | #include "gtest/gtest.h" 4 | -------------------------------------------------------------------------------- /src/monitor/dram_regions.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_DRAM_REGIONS_H_INCLUDED) 2 | #define MONITOR_DRAM_REGIONS_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/phys_atomics.h" 6 | #include "public/api.h" 7 | 8 | // The computer's DRAM is split up into regions that map to different LLC sets. 9 | // 10 | // In order words, if two memory locations belong to different DRAM regions, 11 | // they can never map to the same LLC line. 12 | // 13 | // Stripes are continuous ranges of DRAM. A region is made up of multiple 14 | // stripes. If the cache address shift is optimal, each DRAM region is 15 | // contiguous and has exactly one stripe. Otherwise, the stripes alternate in 16 | // DRAM as follows. 17 | // 18 | // R1S1 R2S1 ... RmS1 R1S2 R2S2 ... RmS2 ... R1Sn R2Sn ... RmSn 19 | // 20 | // After accounting for the above, a DRAM address can be broken down into the 21 | // following components. 22 | // 23 | // | unused | bits used to address installed DRAM memory | 24 | // | zero | stripe index | region index | stripe page index | page offset | 25 | // 26 | // With an optimal cache address shift, the stripe index field has zero bits, 27 | // because the region index is at the top of the DRAM address bits. 28 | 29 | namespace sanctum { 30 | namespace internal { 31 | 32 | using sanctum::api::enclave_id_t; 33 | using sanctum::api::os::dram_region_state_t; 34 | using sanctum::bare::atomic; 35 | using sanctum::bare::atomic_flag; 36 | using sanctum::bare::phys_ptr; 37 | using sanctum::bare::size_t; 38 | using sanctum::bare::uintptr_t; 39 | 40 | // Per-DRAM region accounting information. 41 | struct dram_region_info_t { 42 | atomic_flag lock; // lock for all the DRAM region's state 43 | enclave_id_t owner; // nullptr if not owned by enclave 44 | enclave_id_t previous_owner; // nullptr if previously owned by OS 45 | size_t pinned_pages; // pages that can't be removed from DRAM 46 | size_t blocked_at; // only valid for blocked regions 47 | }; 48 | 49 | // Accounting information for all DRAM regions. 50 | struct dram_regions_info_t { 51 | // NOTE: The lock generation counter is NOT protected by a lock because it 52 | // must be accessible in flush_cached_dram_regions(), which must be 53 | // lock-free. 54 | atomic block_clock; 55 | }; 56 | 57 | // The regions are allocated at boot time, so the physical pointers never 58 | // change. 59 | 60 | extern phys_ptr g_dram_region; 61 | extern phys_ptr g_dram_regions; 62 | 63 | // The fields below are set by boot_init_dram_regions() and never change 64 | // afterwards. Therefore, they do not require locking. 65 | 66 | // Amount of DRAM installed on the system. 67 | extern size_t g_dram_size; 68 | 69 | // NOTE: There is no g_dram_stripe_page_shift -- that is simply page_shift(). 70 | 71 | // The position of the least significant 1 bit in the DRAM region mask. 72 | extern size_t g_dram_region_shift; 73 | // The position of the least significant 1 bit in the DRAM stripe mask. 74 | extern size_t g_dram_stripe_shift; 75 | 76 | // The bits in a physical address that indicate the DRAM region stripe page. 77 | // 78 | // Without any cache address shifting, the mask will be zero, because each DRAM 79 | // stripe will be exactly one page long. 80 | extern size_t g_dram_stripe_page_mask; 81 | // The bits in a physical address that indicate the DRAM region. 82 | // 83 | // DRAM regions may not be contiguous in DRAM. 84 | extern size_t g_dram_region_mask; 85 | // The bits in a physical address that indicate the DRAM region stripe. 86 | // 87 | // If the cache address shift is optimal, the mask will be zero, because each 88 | // DRAM region will have exactly one stripe. 89 | extern size_t g_dram_stripe_mask; 90 | 91 | // The number of DRAM regions indexed by the mask. 92 | // 93 | // This is always 1 + (g_dram_region_mask >> g_dram_region_shift). 94 | extern size_t g_dram_region_count; 95 | 96 | // The size of a DRAM stripe. 97 | // 98 | // The stripe size is always (1 << g_dram_region_shift). 99 | extern size_t g_dram_stripe_size; 100 | 101 | // The number of pages in a DRAM stripe. 102 | // 103 | // This number is always g_dram_stripe_size >> page_shift(), which is 104 | // equivalent to (1 << (g_dram_region_shift - page_shift())). 105 | extern size_t g_dram_stripe_pages; 106 | 107 | // The size of a DRAM region bitmap, in units of sizeof(size_t). 108 | // 109 | // This is ceil(g_dram_region_count / (sizeof(size_t) * 8)). 110 | extern size_t g_dram_region_bitmap_words; 111 | 112 | // The first byte of the allowed DMA transfers memory range. 113 | // 114 | // Accesses to this must have acquired the lock of DRAM region 0. 115 | extern size_t g_dma_range_start; 116 | 117 | // The first byte past the allowed DMA transfers memory range. 118 | // 119 | // Accesses to this must have acquired the lock of DRAM region 0. 120 | extern size_t g_dma_range_end; 121 | 122 | // The special enclave ID values below are used to make it possible to infer a 123 | // DRAM region's state by reading its owner field. The values will not be 124 | // validated by is_valid_enclave_id() because it will extract DRAM region 0 from 125 | // the ID, and the owner of DRAM region 0 is always 0 (the OS). 126 | 127 | // The enclave ID used as the owner of a blocked DRAM region. 128 | constexpr enclave_id_t blocked_enclave_id = 1; 129 | 130 | // The enclave ID used as the owner of a metadata DRAM region. 131 | constexpr enclave_id_t metadata_enclave_id = 2; 132 | 133 | // The enclave ID used as the owner of a free DRAM region. 134 | constexpr enclave_id_t free_enclave_id = 3; 135 | 136 | }; // namespace sanctum::internal 137 | }; // namespace sanctum 138 | #endif // !defined(MONITOR_DRAM_REGIONS_H_INCLUDED) 139 | -------------------------------------------------------------------------------- /src/monitor/dram_regions_inl.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_DRAM_REGIONS_INL_H_INCLUDED) 2 | #define MONITOR_DRAM_REGIONS_INL_H_INCLUDED 3 | 4 | #include "bare/bit_masking.h" 5 | #include "bare/cpu_context.h" 6 | #include "bare/memory.h" 7 | #include "cpu_core.h" 8 | #include "dram_regions.h" 9 | 10 | namespace sanctum { 11 | namespace internal { // sanctum::internal 12 | 13 | using sanctum::api::enclave_id_t; 14 | using sanctum::bare::atomic_flag; 15 | using sanctum::bare::bzero; 16 | using sanctum::bare::page_shift; 17 | using sanctum::bare::phys_ptr; 18 | using sanctum::bare::size_t; 19 | using sanctum::bare::uintptr_t; 20 | 21 | // Verifies that a physical address belongs in DRAM. 22 | // 23 | // Physical addresses can also point to space belonging to memory-mapped 24 | // devices, or to invalid physical addresses. 25 | inline bool is_dram_address(uintptr_t address) { 26 | return address < g_dram_size; 27 | } 28 | 29 | // True for valid DRAM region indices. 30 | // 31 | // This can be called without holding locks, as it relies on constant state. 32 | inline bool is_valid_dram_region(size_t dram_region) { 33 | return dram_region < g_dram_region_count; 34 | } 35 | // True for DRAM regions that can be freed and re-assigned. 36 | // 37 | // The first DRAM region contains monitor code, so it can only be assigned to 38 | // the OS. 39 | inline bool is_dynamic_dram_region(size_t dram_region) { 40 | return dram_region != 0 && is_valid_dram_region(dram_region); 41 | } 42 | 43 | // Computes the physical start address of a DRAM region. 44 | // 45 | // Invalid DRAM region indices will yield invalid pointers. 46 | inline uintptr_t dram_region_start(size_t dram_region) { 47 | return static_cast(dram_region) << g_dram_region_shift; 48 | } 49 | 50 | // Computes the DRAM region index for a pointer. 51 | // 52 | // Pointers outside DRAM will yield valid but meaningless region indices. 53 | inline size_t dram_region_for(uintptr_t address) { 54 | return (address & g_dram_region_mask) >> g_dram_region_shift; 55 | } 56 | 57 | // Computes the DRAM region index for a pointer. 58 | // 59 | // Region index 0 will be returned for pointers outside DRAM. 60 | inline size_t clamped_dram_region_for(uintptr_t address) { 61 | return is_dram_address(address) ? dram_region_for(address) : 0; 62 | } 63 | 64 | // Computes the DRAM region stripe page index for a pointer. 65 | // 66 | // Pointers outside DRAM will yield valid but meaningless stripe indices. 67 | inline size_t dram_stripe_page_for(uintptr_t address) { 68 | return (address & g_dram_stripe_page_mask) >> page_shift(); 69 | } 70 | 71 | // Computes the DRAM region stripe index for a pointer. 72 | // 73 | // Pointers outside DRAM will yield invalid page indices. 74 | inline size_t dram_stripe_for(uintptr_t address) { 75 | return address >> g_dram_stripe_shift; 76 | } 77 | 78 | // Computes the DRAM region page index for a pointer. 79 | // 80 | // The DRAM region page index is unique for a page within a DRAM region, 81 | // whereas the stripe page index is only unique within a DRAM region stripe. 82 | // 83 | // Pointers outside DRAM will yield invalid page indices. 84 | inline size_t dram_region_page_for(uintptr_t address) { 85 | return dram_stripe_page_for(address) | (dram_stripe_for(address) << 86 | (g_dram_region_shift - g_dram_stripe_shift)); 87 | } 88 | 89 | // Acquires the lock for a DRAM region. 90 | // 91 | // Invalid DRAM region indices will cause memory thrashing. 92 | // 93 | // Returns false if the lock was acquired, and true if it was already held by 94 | // someone else. 95 | inline bool test_and_set_dram_region_lock(size_t dram_region) { 96 | phys_ptr region = &g_dram_region[dram_region]; 97 | return atomic_flag_test_and_set(&(region->*(&dram_region_info_t::lock))); 98 | } 99 | 100 | // Releases the lock for a DRAM region. 101 | // 102 | // Invalid DRAM region indices will cause memory thrashing. 103 | // 104 | // Clear a lock that was not explicitly acquired is a security vulnerability, 105 | // because another piece of code might have acquired the lock. 106 | inline void clear_dram_region_lock(size_t dram_region) { 107 | phys_ptr region = &g_dram_region[dram_region]; 108 | atomic_flag_clear(&(region->*(&dram_region_info_t::lock))); 109 | } 110 | 111 | // Reads the owner from a DRAM region. 112 | // 113 | // Invalid DRAM region indices will cause memory reads outside the DRAM space. 114 | inline enclave_id_t read_dram_region_owner(size_t dram_region) { 115 | const phys_ptr region = &g_dram_region[dram_region]; 116 | return region->*(&dram_region_info_t::owner); 117 | } 118 | 119 | // Wipes the data in a DRAM region. 120 | // 121 | // Invalid DRAM region indices will cause memory trashing. 122 | inline void bzero_dram_region(size_t dram_region) { 123 | // The address diff between two stripes belonging to the same DRAM region. 124 | const uintptr_t stripe_step = g_dram_region_count << g_dram_region_shift; 125 | 126 | const uintptr_t region_start = dram_region << g_dram_region_shift; 127 | for (uintptr_t stripe = 0; stripe < g_dram_size; stripe += stripe_step) { 128 | const uintptr_t stripe_start = stripe | region_start; 129 | bzero(phys_ptr{stripe_start}, g_dram_stripe_size); 130 | } 131 | } 132 | 133 | // Flushes the core's TLBs and updates the relevant flush generation counter. 134 | // 135 | // This code is guaranteed to be lock-free, as it is used in enclave exits. 136 | inline void dram_region_tlb_flush() { 137 | sanctum::bare::flush_tlbs(); 138 | 139 | // NOTE: it is safer to increment the atomic counter AFTER the TLBs are 140 | // flushed; the moment the counter is incremented, some DRAM region may 141 | // be freed and reallocated; this sequence is 142 | 143 | const phys_ptr core_info{&g_core[current_core()]}; 144 | const size_t block_clock = atomic_load( 145 | &(g_dram_regions->*(&dram_regions_info_t::block_clock))); 146 | atomic_store(&(core_info->*(&core_info_t::flushed_at)), block_clock); 147 | } 148 | 149 | }; // namespace sanctum::internal 150 | }; // namespace sanctum 151 | #endif // !defined(MONITOR_DRAM_REGIONS_INL_H_INCLUDED) 152 | -------------------------------------------------------------------------------- /src/monitor/dram_regions_inl_test.cc: -------------------------------------------------------------------------------- 1 | #include "dram_regions_inl.h" 2 | 3 | #include "boot_init.h" 4 | 5 | #include "gtest/gtest.h" 6 | 7 | using sanctum::bare::atomic_load; 8 | using sanctum::bare::atomic_store; 9 | using sanctum::bare::phys_ptr; 10 | using sanctum::internal::boot_init_dram_regions; 11 | using sanctum::internal::boot_init_metadata; 12 | using sanctum::internal::boot_init_dynamic_arrays; 13 | using sanctum::internal::bzero_dram_region; 14 | using sanctum::internal::clamped_dram_region_for; 15 | using sanctum::internal::clear_dram_region_lock; 16 | using sanctum::internal::core_info_t; 17 | using sanctum::internal::dram_region_for; 18 | using sanctum::internal::dram_region_info_t; 19 | using sanctum::internal::dram_regions_info_t; 20 | using sanctum::internal::dram_region_page_for; 21 | using sanctum::internal::dram_region_start; 22 | using sanctum::internal::dram_region_tlb_flush; 23 | using sanctum::internal::dram_stripe_for; 24 | using sanctum::internal::dram_stripe_page_for; 25 | using sanctum::internal::g_core; 26 | using sanctum::internal::g_dram_region; 27 | using sanctum::internal::g_dram_regions; 28 | using sanctum::internal::is_dram_address; 29 | using sanctum::internal::is_dynamic_dram_region; 30 | using sanctum::internal::is_valid_dram_region; 31 | using sanctum::internal::read_dram_region_owner; 32 | using sanctum::internal::test_and_set_dram_region_lock; 33 | 34 | namespace { 35 | 36 | // Sets up the test rig with the toy memory parameters from the Sanctum paper. 37 | void set_up_paper_memory_model() { 38 | sanctum::testing::dram_size = 1 << 18; 39 | sanctum::testing::cache_levels = 3; 40 | 41 | sanctum::testing::is_shared_cache[0] = false; 42 | sanctum::testing::is_shared_cache[1] = false; 43 | sanctum::testing::is_shared_cache[2] = true; 44 | 45 | sanctum::testing::cache_line_size[0] = 1 << 6; // irrelevant to tests 46 | sanctum::testing::cache_line_size[1] = 1 << 6; // irrelevant to tests 47 | sanctum::testing::cache_line_size[2] = 1 << 6; // must be a power of 2 48 | 49 | sanctum::testing::cache_set_count[0] = 1 << 6; // irrelevant to tests 50 | sanctum::testing::cache_set_count[1] = 1 << 8; // irrelevant to tests 51 | sanctum::testing::cache_set_count[2] = 1 << 9; // must be a power of 2 52 | 53 | sanctum::testing::min_cache_index_shift = 0; 54 | sanctum::testing::max_cache_index_shift = 16; 55 | 56 | sanctum::testing::set_core_count(4); 57 | } 58 | 59 | } 60 | 61 | class DramRegionInlTest : public ::testing::Test { 62 | protected: 63 | virtual void SetUp() { 64 | set_up_paper_memory_model(); 65 | boot_init_dram_regions(); 66 | boot_init_metadata(); 67 | boot_init_dynamic_arrays(); 68 | } 69 | }; 70 | 71 | TEST_F(DramRegionInlTest, IsDramAddress) { 72 | EXPECT_EQ(is_dram_address(0), true); 73 | EXPECT_EQ(is_dram_address(1), true); 74 | EXPECT_EQ(is_dram_address(0x1000), true); 75 | EXPECT_EQ(is_dram_address(0x3ffff), true); 76 | EXPECT_EQ(is_dram_address(0x40000), false); 77 | EXPECT_EQ(is_dram_address(0x40001), false); 78 | EXPECT_EQ(is_dram_address(~0), false); 79 | } 80 | 81 | TEST_F(DramRegionInlTest, IsValidDramRegion) { 82 | EXPECT_EQ(is_valid_dram_region(0), true); 83 | EXPECT_EQ(is_valid_dram_region(1), true); 84 | EXPECT_EQ(is_valid_dram_region(2), true); 85 | EXPECT_EQ(is_valid_dram_region(6), true); 86 | EXPECT_EQ(is_valid_dram_region(7), true); 87 | EXPECT_EQ(is_valid_dram_region(8), false); 88 | EXPECT_EQ(is_valid_dram_region(16), false); 89 | EXPECT_EQ(is_valid_dram_region(~0), false); 90 | } 91 | 92 | TEST_F(DramRegionInlTest, IsDynamicDramRegion) { 93 | EXPECT_EQ(is_dynamic_dram_region(0), false); 94 | EXPECT_EQ(is_dynamic_dram_region(1), true); 95 | EXPECT_EQ(is_dynamic_dram_region(2), true); 96 | EXPECT_EQ(is_dynamic_dram_region(6), true); 97 | EXPECT_EQ(is_dynamic_dram_region(7), true); 98 | EXPECT_EQ(is_dynamic_dram_region(8), false); 99 | EXPECT_EQ(is_dynamic_dram_region(16), false); 100 | EXPECT_EQ(is_dynamic_dram_region(~0), false); 101 | } 102 | 103 | TEST_F(DramRegionInlTest, DramRegionStart) { 104 | EXPECT_EQ(dram_region_start(0), 0); 105 | EXPECT_EQ(dram_region_start(1), 0x8000); 106 | EXPECT_EQ(dram_region_start(2), 0x10000); 107 | EXPECT_EQ(dram_region_start(32), 0x100000); 108 | } 109 | 110 | TEST_F(DramRegionInlTest, DramRegionFor) { 111 | EXPECT_EQ(dram_region_for(0), 0); 112 | EXPECT_EQ(dram_region_for(1), 0); 113 | EXPECT_EQ(dram_region_for(0x7fff), 0); 114 | EXPECT_EQ(dram_region_for(0x8000), 1); 115 | EXPECT_EQ(dram_region_for(0x8001), 1); 116 | EXPECT_EQ(dram_region_for(0x9000), 1); 117 | EXPECT_EQ(dram_region_for(0xa000), 1); 118 | EXPECT_EQ(dram_region_for(0xe000), 1); 119 | EXPECT_EQ(dram_region_for(0xffff), 1); 120 | EXPECT_EQ(dram_region_for(0x10000), 2); 121 | EXPECT_EQ(dram_region_for(0x10001), 2); 122 | EXPECT_EQ(dram_region_for(0x3ffff), 7); 123 | } 124 | 125 | TEST_F(DramRegionInlTest, ClampedDramRegionFor) { 126 | EXPECT_EQ(clamped_dram_region_for(0), 0); 127 | EXPECT_EQ(clamped_dram_region_for(1), 0); 128 | EXPECT_EQ(clamped_dram_region_for(0x7fff), 0); 129 | EXPECT_EQ(clamped_dram_region_for(0x8000), 1); 130 | EXPECT_EQ(clamped_dram_region_for(0x8001), 1); 131 | EXPECT_EQ(clamped_dram_region_for(0x9000), 1); 132 | EXPECT_EQ(clamped_dram_region_for(0xa000), 1); 133 | EXPECT_EQ(clamped_dram_region_for(0xe000), 1); 134 | EXPECT_EQ(clamped_dram_region_for(0xffff), 1); 135 | EXPECT_EQ(clamped_dram_region_for(0x10000), 2); 136 | EXPECT_EQ(clamped_dram_region_for(0x10001), 2); 137 | EXPECT_EQ(clamped_dram_region_for(0x3ffff), 7); 138 | 139 | ASSERT_EQ(is_dram_address(0x40000), false); 140 | EXPECT_EQ(clamped_dram_region_for(0x40000), 0); 141 | EXPECT_EQ(clamped_dram_region_for(0x40001), 0); 142 | EXPECT_EQ(clamped_dram_region_for(0x48000), 0); 143 | EXPECT_EQ(clamped_dram_region_for(0x7ffff), 0); 144 | } 145 | 146 | TEST_F(DramRegionInlTest, DramStripePageFor) { 147 | EXPECT_EQ(dram_stripe_page_for(0), 0); 148 | EXPECT_EQ(dram_stripe_page_for(1), 0); 149 | EXPECT_EQ(dram_stripe_page_for(0xfff), 0); 150 | EXPECT_EQ(dram_stripe_page_for(0x1000), 1); 151 | EXPECT_EQ(dram_stripe_page_for(0x1001), 1); 152 | EXPECT_EQ(dram_stripe_page_for(0x1fff), 1); 153 | EXPECT_EQ(dram_stripe_page_for(0x2000), 2); 154 | EXPECT_EQ(dram_stripe_page_for(0x3000), 3); 155 | EXPECT_EQ(dram_stripe_page_for(0x4000), 4); 156 | EXPECT_EQ(dram_stripe_page_for(0x6000), 6); 157 | EXPECT_EQ(dram_stripe_page_for(0x7fff), 7); 158 | EXPECT_EQ(dram_stripe_page_for(0x8000), 0); 159 | EXPECT_EQ(dram_stripe_page_for(0x8001), 0); 160 | EXPECT_EQ(dram_stripe_page_for(0x9000), 1); 161 | EXPECT_EQ(dram_stripe_page_for(0xa000), 2); 162 | EXPECT_EQ(dram_stripe_page_for(0xe000), 6); 163 | EXPECT_EQ(dram_stripe_page_for(0xffff), 7); 164 | EXPECT_EQ(dram_stripe_page_for(0x10000), 0); 165 | EXPECT_EQ(dram_stripe_page_for(0x10001), 0); 166 | EXPECT_EQ(dram_stripe_page_for(0x3ffff), 7); 167 | } 168 | 169 | TEST_F(DramRegionInlTest, DramStripeFor) { 170 | EXPECT_EQ(dram_stripe_for(0), 0); 171 | EXPECT_EQ(dram_stripe_for(1), 0); 172 | EXPECT_EQ(dram_stripe_for(0xfff), 0); 173 | EXPECT_EQ(dram_stripe_for(0x1000), 0); 174 | EXPECT_EQ(dram_stripe_for(0x1001), 0); 175 | EXPECT_EQ(dram_stripe_for(0x1fff), 0); 176 | EXPECT_EQ(dram_stripe_for(0x2000), 0); 177 | EXPECT_EQ(dram_stripe_for(0x3000), 0); 178 | EXPECT_EQ(dram_stripe_for(0x4000), 0); 179 | EXPECT_EQ(dram_stripe_for(0x6000), 0); 180 | EXPECT_EQ(dram_stripe_for(0x7fff), 0); 181 | EXPECT_EQ(dram_stripe_for(0x8000), 0); 182 | EXPECT_EQ(dram_stripe_for(0x8001), 0); 183 | EXPECT_EQ(dram_stripe_for(0x9000), 0); 184 | EXPECT_EQ(dram_stripe_for(0xa000), 0); 185 | EXPECT_EQ(dram_stripe_for(0xe000), 0); 186 | EXPECT_EQ(dram_stripe_for(0xffff), 0); 187 | EXPECT_EQ(dram_stripe_for(0x10000), 0); 188 | EXPECT_EQ(dram_stripe_for(0x10001), 0); 189 | EXPECT_EQ(dram_stripe_for(0x3ffff), 0); 190 | } 191 | 192 | TEST_F(DramRegionInlTest, DramRegionPageFor) { 193 | EXPECT_EQ(dram_region_page_for(0), 0); 194 | EXPECT_EQ(dram_region_page_for(1), 0); 195 | EXPECT_EQ(dram_region_page_for(0xfff), 0); 196 | EXPECT_EQ(dram_region_page_for(0x1000), 1); 197 | EXPECT_EQ(dram_region_page_for(0x1001), 1); 198 | EXPECT_EQ(dram_region_page_for(0x1fff), 1); 199 | EXPECT_EQ(dram_region_page_for(0x2000), 2); 200 | EXPECT_EQ(dram_region_page_for(0x3000), 3); 201 | EXPECT_EQ(dram_region_page_for(0x4000), 4); 202 | EXPECT_EQ(dram_region_page_for(0x6000), 6); 203 | EXPECT_EQ(dram_region_page_for(0x7fff), 7); 204 | EXPECT_EQ(dram_region_page_for(0x8000), 0); 205 | EXPECT_EQ(dram_region_page_for(0x8001), 0); 206 | EXPECT_EQ(dram_region_page_for(0x9000), 1); 207 | EXPECT_EQ(dram_region_page_for(0xa000), 2); 208 | EXPECT_EQ(dram_region_page_for(0xe000), 6); 209 | EXPECT_EQ(dram_region_page_for(0xffff), 7); 210 | EXPECT_EQ(dram_region_page_for(0x10000), 0); 211 | EXPECT_EQ(dram_region_page_for(0x10001), 0); 212 | EXPECT_EQ(dram_region_page_for(0x3ffff), 7); 213 | } 214 | 215 | TEST_F(DramRegionInlTest, DramRegionLocks) { 216 | clear_dram_region_lock(5); 217 | test_and_set_dram_region_lock(5); 218 | ASSERT_EQ(atomic_flag_test_and_set( 219 | &((g_dram_region + 5)->*(&dram_region_info_t::lock))), 1); 220 | clear_dram_region_lock(5); 221 | ASSERT_EQ(atomic_flag_test_and_set( 222 | &((g_dram_region + 5)->*(&dram_region_info_t::lock))), 0); 223 | clear_dram_region_lock(5); 224 | 225 | clear_dram_region_lock(0); 226 | clear_dram_region_lock(1); 227 | clear_dram_region_lock(2); 228 | ASSERT_EQ(test_and_set_dram_region_lock(0), 0); 229 | ASSERT_EQ(test_and_set_dram_region_lock(0), 1); 230 | ASSERT_EQ(test_and_set_dram_region_lock(1), 0); 231 | ASSERT_EQ(test_and_set_dram_region_lock(1), 1); 232 | ASSERT_EQ(test_and_set_dram_region_lock(2), 0); 233 | ASSERT_EQ(test_and_set_dram_region_lock(2), 1); 234 | clear_dram_region_lock(1); 235 | ASSERT_EQ(test_and_set_dram_region_lock(0), 1); 236 | ASSERT_EQ(test_and_set_dram_region_lock(1), 0); 237 | ASSERT_EQ(test_and_set_dram_region_lock(2), 1); 238 | } 239 | 240 | TEST_F(DramRegionInlTest, ReadDramRegionOwner) { 241 | (g_dram_region + 0)->*(&dram_region_info_t::owner) = 0x42424242; 242 | (g_dram_region + 1)->*(&dram_region_info_t::owner) = 0xabababab; 243 | (g_dram_region + 2)->*(&dram_region_info_t::owner) = 0x98765432; 244 | (g_dram_region + 5)->*(&dram_region_info_t::owner) = 0x12345678; 245 | (g_dram_region + 7)->*(&dram_region_info_t::owner) = 0xfccffccf; 246 | ASSERT_EQ(read_dram_region_owner(0), 0x42424242); 247 | ASSERT_EQ(read_dram_region_owner(1), 0xabababab); 248 | ASSERT_EQ(read_dram_region_owner(2), 0x98765432); 249 | ASSERT_EQ(read_dram_region_owner(5), 0x12345678); 250 | ASSERT_EQ(read_dram_region_owner(7), 0xfccffccf); 251 | } 252 | 253 | TEST_F(DramRegionInlTest, BzeroDramRegion) { 254 | for (size_t i = 0; i < 256 * 1024; i += sizeof(uintptr_t)) 255 | *(phys_ptr{i}) = ~0; 256 | bzero_dram_region(1); 257 | 258 | for (size_t i = 0; i < 32 * 1024; i += sizeof(uintptr_t)) 259 | ASSERT_EQ(*(phys_ptr{i}), ~0); 260 | for (size_t i = 32 * 1024; i < 64 * 1024; i += sizeof(uintptr_t)) 261 | ASSERT_EQ(*(phys_ptr{i}), 0); 262 | for (size_t i = 64 * 1024; i < 256 * 1024; i += sizeof(uintptr_t)) 263 | ASSERT_EQ(*(phys_ptr{i}), ~0); 264 | } 265 | 266 | TEST_F(DramRegionInlTest, DramRegionTlbFlush) { 267 | sanctum::testing::core_tlb_flush_count[0] = 16; 268 | sanctum::testing::core_tlb_flush_count[1] = 32; 269 | sanctum::testing::core_tlb_flush_count[2] = 64; 270 | sanctum::testing::core_tlb_flush_count[3] = 128; 271 | sanctum::testing::set_current_core(2); 272 | atomic_store(&(g_dram_regions->*(&dram_regions_info_t::block_clock)), 273 | static_cast(0x12345678)); 274 | atomic_store(&((g_core + 2)->*(&core_info_t::flushed_at)), 275 | static_cast(0)); 276 | 277 | dram_region_tlb_flush(); 278 | 279 | ASSERT_EQ(sanctum::testing::core_tlb_flush_count[0], 16); 280 | ASSERT_EQ(sanctum::testing::core_tlb_flush_count[1], 32); 281 | ASSERT_EQ(sanctum::testing::core_tlb_flush_count[2], 65); 282 | ASSERT_EQ(sanctum::testing::core_tlb_flush_count[3], 128); 283 | ASSERT_EQ(atomic_load(&((g_core + 2)->*(&core_info_t::flushed_at))), 284 | static_cast(0x12345678)); 285 | } 286 | 287 | // TODO: test dram_stripe_for in multi-stripe memory setting 288 | // TODO: test dram_region_page_for in multi-stripe memory setting 289 | // TODO: test bzero_dram_region in multi-stripe memory setting 290 | -------------------------------------------------------------------------------- /src/monitor/enclave.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_ENCLAVE_H_INCLUDED) 2 | #define MONITOR_ENCLAVE_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/cpu_context.h" 6 | #include "bare/phys_atomics.h" 7 | #include "crypto/hash.h" 8 | #include "public/api.h" 9 | 10 | namespace sanctum { 11 | namespace internal { 12 | 13 | using sanctum::api::enclave_id_t; 14 | using sanctum::api::enclave::thread_init_info_t; 15 | using sanctum::bare::atomic; 16 | using sanctum::bare::atomic_flag; 17 | using sanctum::bare::phys_ptr; 18 | using sanctum::bare::register_state_t; 19 | using sanctum::bare::size_t; 20 | using sanctum::bare::uintptr_t; 21 | using sanctum::crypto::hash_block_size; 22 | using sanctum::crypto::hash_state_t; 23 | 24 | // The per-thread information stored in metadata regions. 25 | struct thread_info_t { 26 | // Protects this structure from data races. 27 | // 28 | // This lock should be acquired using lock_thread_metadata(), which 29 | // guarantees that the thread_info_t is valid at lock acquisition time by 30 | // holding the metadata region's lock while acquiring the thread's lock. 31 | atomic_flag lock; 32 | 33 | // The fields below get initialized from thread_init_info_t. 34 | 35 | // The virtual address of the thread's entry point. 36 | uintptr_t entry_pc; 37 | // The virtual address of the thread's stack top. 38 | uintptr_t entry_stack; 39 | // The virtual address of the thread's fault handler. 40 | uintptr_t fault_pc; 41 | // The virtual address of the fault handler's stack top. 42 | uintptr_t fault_stack; 43 | 44 | // The EPTBR value to be loaded for this thread. 45 | // 46 | // The EPTBR contains the physical address of the enclave's page table base. 47 | uintptr_t eptbr; 48 | 49 | register_state_t exit_state; // enter_enclave caller state 50 | register_state_t aex_state; // enclave state saved on AEX 51 | size_t can_resume; // true if the AEX state is valid 52 | }; 53 | 54 | // Per-enclave accounting information. 55 | // 56 | // This structure is stored at the beginning of an enclave's main DRAM region, 57 | // followed by the enclave's DRAM region bitmap and mailboxes. The monitor 58 | // ensures that the pages holding the structure are not evicted while the 59 | // enclave is alive. 60 | struct enclave_info_t { 61 | // Protects this structure from data races. 62 | // 63 | // This lock should be acquired using lock_enclave_info(), which guarantees 64 | // that the enclave_info_t is valid at lock acquisition time by holding the 65 | // metadata region's lock while acquiring the enclave's lock. 66 | atomic_flag lock; 67 | 68 | // Number of mailbox_t structures following the thread_slot_t structures. 69 | size_t mailbox_count; 70 | 71 | // non-zero when the enclave was initialized and can execute threads. 72 | // NOTE: this isn't bool because we don't want to specialize phys_ptr. 73 | size_t is_initialized; 74 | 75 | // non-zero for debug enclaves. 76 | size_t is_debug; 77 | 78 | // Number of thread metadata structures assigned to the enclave. 79 | // 80 | // This must be zero for the enclave to be killed. 81 | size_t thread_count; 82 | 83 | // Number of DRAM regions assigned to the enclave. 84 | // 85 | // This must be zero for the enclave's metadata to be removed. 86 | size_t dram_region_count; 87 | 88 | // The base of the enclave's virtual address range. 89 | uintptr_t ev_base; 90 | 91 | // The mask of the enclave's virtual address range. 92 | uintptr_t ev_mask; 93 | 94 | // Physical address of the enclave's page table base during loading. 95 | // 96 | // This is set by the first load_page_table() call, and forced as the 97 | // EPTBR value for enclave threads created by load_thread. 98 | uintptr_t load_eptbr; 99 | 100 | // The phyiscal address of the last page loaded into the enclave by the OS. 101 | uintptr_t last_load_addr; 102 | 103 | // The enclave's measurement hash. 104 | // 105 | // This is updated by the enclave loading API calls, and finalized by 106 | // enclave_init(). It does not change afterwards. 107 | hash_state_t hash; 108 | 109 | // Working area for the enclave measurement process. 110 | uint32_t hash_block[hash_block_size / sizeof(uint32_t)]; 111 | }; 112 | 113 | // The DRAM region bitmap for the OS. 114 | // 115 | // This is synchronized by the lock of DRAM region 0, which must belong to the 116 | // OS. The pointer itself is allocated at boot time, so it never changes. 117 | extern phys_ptr g_os_region_bitmap; 118 | 119 | 120 | }; // namespace sanctum::internal 121 | }; // namespace sanctum 122 | #endif // !defined(MONITOR_ENCLAVE_H_INCLUDED) 123 | -------------------------------------------------------------------------------- /src/monitor/enclave_init.cc: -------------------------------------------------------------------------------- 1 | #include "enclave.h" 2 | 3 | #include "bare/memory.h" 4 | #include "bare/page_tables.h" 5 | #include "dram_regions_inl.h" 6 | #include "enclave_inl.h" 7 | #include "measure_inl.h" 8 | #include "metadata_inl.h" 9 | 10 | using sanctum::bare::atomic_fetch_add; 11 | using sanctum::bare::bcopy; 12 | using sanctum::bare::bzero; 13 | using sanctum::bare::ceil_power_of_two; 14 | using sanctum::bare::is_page_aligned; 15 | using sanctum::bare::is_valid_page_table_entry; 16 | using sanctum::bare::is_valid_range; 17 | using sanctum::bare::page_size; 18 | using sanctum::bare::page_shift; 19 | using sanctum::bare::page_table_levels; 20 | using sanctum::bare::page_table_size; 21 | using sanctum::bare::phys_ptr; 22 | using sanctum::bare::size_t; 23 | using sanctum::bare::uintptr_t; 24 | using sanctum::bare::write_page_table_entry; 25 | using sanctum::internal::clamped_dram_region_for; 26 | using sanctum::internal::clear_dram_region_lock; 27 | using sanctum::internal::dram_region_for; 28 | using sanctum::internal::dram_region_info_t; 29 | using sanctum::internal::dram_region_start; 30 | using sanctum::internal::enclave_info_t; 31 | using sanctum::internal::enclave_info_pages; 32 | using sanctum::internal::enclave_info_size; 33 | using sanctum::internal::enclave_region_bitmap; 34 | using sanctum::internal::extend_enclave_hash_with_page; 35 | using sanctum::internal::extend_enclave_hash_with_page_table; 36 | using sanctum::internal::extend_enclave_hash_with_thread; 37 | using sanctum::internal::finalize_enclave_hash; 38 | using sanctum::internal::free_enclave_id; 39 | using sanctum::internal::g_dram_region; 40 | using sanctum::internal::g_dram_stripe_size; 41 | using sanctum::internal::init_enclave_hash; 42 | using sanctum::internal::is_dram_address; 43 | using sanctum::internal::is_enclave_virtual_address; 44 | using sanctum::internal::is_valid_dram_region; 45 | using sanctum::internal::is_valid_enclave_id; 46 | using sanctum::internal::read_dram_region_owner; 47 | using sanctum::internal::read_enclave_region_bitmap_bit; 48 | using sanctum::internal::test_and_set_dram_region_lock; 49 | using sanctum::internal::thread_metadata_pages; 50 | using sanctum::internal::thread_metadata_size; 51 | using sanctum::internal::thread_info_t; 52 | using sanctum::internal::walk_page_tables; 53 | using sanctum::internal::walk_page_tables_to_entry; 54 | 55 | namespace sanctum { 56 | namespace api { // sanctum::api 57 | namespace os { // sancum::api::os 58 | 59 | 60 | api_result_t load_page_table(enclave_id_t enclave_id, 61 | uintptr_t phys_addr, uintptr_t virtual_addr, size_t level, size_t acl) { 62 | if (!is_dram_address(phys_addr)) 63 | return monitor_invalid_value; 64 | if (!is_page_aligned(phys_addr)) 65 | return monitor_invalid_value; 66 | // NOTE: we need to check the level to avoid an infinite loop; we don't do 67 | // any unnecessary checking on measured arguments 68 | if (level >= page_table_levels()) 69 | return monitor_invalid_value; 70 | 71 | size_t dram_region = clamped_dram_region_for(enclave_id); 72 | if (test_and_set_dram_region_lock(dram_region)) 73 | return monitor_concurrent_call; 74 | 75 | // NOTE: null_enclave_id is accepted by is_valid_enclave_id, but does not 76 | // have a useful meaning here 77 | if (enclave_id == null_enclave_id || !is_valid_enclave_id(enclave_id)) { 78 | clear_dram_region_lock(dram_region); 79 | return monitor_invalid_value; 80 | } 81 | 82 | phys_ptr enclave_info{enclave_id}; 83 | if (enclave_info->*(&enclave_info_t::is_initialized) != 0) { 84 | clear_dram_region_lock(dram_region); 85 | return monitor_invalid_state; 86 | } 87 | if (phys_addr <= enclave_info->*(&enclave_info_t::last_load_addr)) { 88 | clear_dram_region_lock(dram_region); 89 | return monitor_invalid_value; 90 | } 91 | if (level != page_table_levels() - 1 && 92 | !is_enclave_virtual_address(virtual_addr, enclave_id)) { 93 | clear_dram_region_lock(dram_region); 94 | return monitor_invalid_value; 95 | } 96 | /* 97 | if (is_enclave_metadata_address(phys_addr, enclave_id)) { 98 | clear_dram_region_lock(dram_region); 99 | return monitor_invalid_value; 100 | } 101 | 102 | // NOTE: We don't need to lock the DRAM regions of the page tables, because 103 | // an enclave cannot relinquish its DRAM regions until it is 104 | // initialized and running. Therefore, once the DRAM region is assigned 105 | // and its bit is set in the enclave's region bitmap, we know the DRAM 106 | // region will stay with the enclave until initialization completes. 107 | phys_ptr region_bitmap = enclave_region_bitmap(enclave_id); 108 | size_t table_size = page_table_size(level); 109 | size_t phys_end = phys_addr + table_size; 110 | for (size_t table_page_addr = phys_addr; table_page_addr < phys_end; 111 | table_page_addr += page_size()) { 112 | size_t table_dram_region = dram_region_for(table_page_addr); 113 | if (!read_bitmap_bit(region_bitmap, table_dram_region)) { 114 | clear_dram_region_lock(dram_region); 115 | return monitor_invalid_value; 116 | } 117 | } 118 | 119 | // Allocating a page table at level N means walking until level N + 1, and 120 | // then editing the level N + 1 table to point to our new table. 121 | size_t edit_level = level + 1; 122 | if (edit_level == page_table_levels()) { 123 | enclave_info->*(&enclave_info_t::load_eptbr) = phys_addr; 124 | // NOTE: we completely ignore virtual_addr here; we don't bother checking 125 | // that it's zero because the call gets measured 126 | } else { 127 | uintptr_t ptb = enclave_info->*(&enclave_info_t::load_eptbr); 128 | uintptr_t entry_addr = walk_page_tables_to_entry(ptb, virtual_addr, 129 | edit_level); 130 | if (entry_addr == 0 || is_valid_page_table_entry(entry_addr, edit_level)) { 131 | clear_dram_region_lock(dram_region); 132 | return monitor_invalid_state; 133 | } 134 | write_page_table_entry(entry_addr, edit_level, phys_addr, acl); 135 | } 136 | 137 | // NOTE: last_load_addr points to the last allocated physical page, so 138 | // we have to subtract a page from the page table's end address. 139 | enclave_info->*(&enclave_info_t::last_load_addr) = phys_end - page_size(); 140 | bzero(phys_ptr{phys_addr}, table_size); 141 | 142 | extend_enclave_hash_with_page_table(enclave_info, virtual_addr, level, acl); 143 | */ 144 | 145 | clear_dram_region_lock(dram_region); 146 | return monitor_ok; 147 | } 148 | 149 | api_result_t load_page(enclave_id_t enclave_id, uintptr_t phys_addr, 150 | uintptr_t virtual_addr, uintptr_t os_addr, uintptr_t acl) { 151 | if (!is_dram_address(phys_addr) || !is_dram_address(os_addr)) 152 | return monitor_invalid_value; 153 | if (!is_page_aligned(phys_addr) || !is_page_aligned(os_addr)) 154 | return monitor_invalid_value; 155 | 156 | size_t dram_region = clamped_dram_region_for(enclave_id); 157 | if (test_and_set_dram_region_lock(dram_region)) 158 | return monitor_concurrent_call; 159 | 160 | // NOTE: null_enclave_id is accepted by is_valid_enclave_id, but does not 161 | // have a useful meaning here 162 | if (enclave_id == null_enclave_id || !is_valid_enclave_id(enclave_id)) { 163 | clear_dram_region_lock(dram_region); 164 | return monitor_invalid_value; 165 | } 166 | 167 | phys_ptr enclave_info{enclave_id}; 168 | if (enclave_info->*(&enclave_info_t::is_initialized) != 0) { 169 | clear_dram_region_lock(dram_region); 170 | return monitor_invalid_state; 171 | } 172 | if (phys_addr <= enclave_info->*(&enclave_info_t::last_load_addr)) { 173 | clear_dram_region_lock(dram_region); 174 | return monitor_invalid_value; 175 | } 176 | if (!is_enclave_virtual_address(virtual_addr, enclave_id)) { 177 | clear_dram_region_lock(dram_region); 178 | return monitor_invalid_value; 179 | } 180 | /* 181 | if (is_enclave_metadata_address(phys_addr, enclave_id)) { 182 | clear_dram_region_lock(dram_region); 183 | return monitor_invalid_value; 184 | } 185 | 186 | // NOTE: See load_page_table for the explanation why we don't need to 187 | // lock phys_addr's DRAM region. 188 | size_t page_dram_region = dram_region_for(phys_addr); 189 | if (!read_bitmap_bit(enclave_region_bitmap(enclave_id), page_dram_region)) { 190 | clear_dram_region_lock(dram_region); 191 | return monitor_invalid_value; 192 | } 193 | 194 | uintptr_t ptb = enclave_info->*(&enclave_info_t::load_eptbr); 195 | uintptr_t entry_addr = walk_page_tables_to_entry(ptb, virtual_addr, 0); 196 | if (entry_addr == 0 || is_valid_page_table_entry(entry_addr, 0)) { 197 | clear_dram_region_lock(dram_region); 198 | return monitor_invalid_state; 199 | } 200 | 201 | // NOTE: We're performing the OS DRAM region checks last to minimize the 202 | // number of times we have two release two locks when bailing out due 203 | // to errors. 204 | 205 | // NOTE: We don't need to check if os_dram_region is the same as dram_region. 206 | // If that's the case, we'll simply fail to acquire the lock and return 207 | // concurrent_call. This is acceptable. Ideally, we'd return 208 | // invalid_value, but that'd increase code size. 209 | size_t os_dram_region = dram_region_for(os_addr); 210 | if (test_and_set_dram_region_lock(os_dram_region)) { 211 | clear_dram_region_lock(dram_region); 212 | return monitor_concurrent_call; 213 | } 214 | 215 | // NOTE: Even though we're reading the DRAM region ownership atomically, we 216 | // still need to lock the region to make sure that it doesn't go away 217 | // while we bcopy a page out of it. 218 | phys_ptr region = &g_dram_region[os_dram_region]; 219 | if (read_dram_region_owner(dram_region) != null_enclave_id) { 220 | clear_dram_region_lock(os_dram_region); 221 | clear_dram_region_lock(dram_region); 222 | return monitor_access_denied; 223 | } 224 | 225 | enclave_info->*(&enclave_info_t::last_load_addr) = phys_addr; 226 | bcopy(phys_ptr{phys_addr}, phys_ptr{os_addr}, page_size()); 227 | clear_dram_region_lock(os_dram_region); 228 | 229 | extend_enclave_hash_with_page(enclave_info, virtual_addr, acl, phys_addr); 230 | */ 231 | clear_dram_region_lock(dram_region); 232 | return monitor_ok; 233 | } 234 | 235 | api_result_t init_enclave(enclave_id_t enclave_id) { 236 | size_t dram_region = clamped_dram_region_for(enclave_id); 237 | if (test_and_set_dram_region_lock(dram_region)) 238 | return monitor_concurrent_call; 239 | 240 | // NOTE: null_enclave_id is accepted by is_valid_enclave_id, but does not 241 | // have a useful meaning here 242 | if (enclave_id == null_enclave_id || !is_valid_enclave_id(enclave_id)) { 243 | clear_dram_region_lock(dram_region); 244 | return monitor_invalid_value; 245 | } 246 | 247 | phys_ptr enclave_info{enclave_id}; 248 | if (enclave_info->*(&enclave_info_t::is_initialized) != 0) { 249 | clear_dram_region_lock(dram_region); 250 | return monitor_invalid_state; 251 | } 252 | 253 | finalize_enclave_hash(enclave_info); 254 | 255 | enclave_info->*(&enclave_info_t::is_initialized) = 1; 256 | clear_dram_region_lock(dram_region); 257 | return monitor_ok; 258 | } 259 | 260 | }; // namespace sanctum::api::os 261 | }; // namespace sanctum::api 262 | }; // namespace sanctum 263 | -------------------------------------------------------------------------------- /src/monitor/enclave_inl.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_ENCLAVE_INL_H_INCLUDED) 2 | #define MONITOR_ENCLAVE_INL_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/bit_masking.h" 6 | #include "bare/page_tables.h" 7 | #include "bare/phys_ptr.h" 8 | #include "dram_regions.h" 9 | #include "enclave.h" 10 | #include "measure_inl.h" 11 | 12 | namespace sanctum { 13 | namespace internal { 14 | 15 | using sanctum::bare::is_valid_page_table_entry; 16 | using sanctum::bare::page_size; 17 | using sanctum::bare::page_shift; 18 | using sanctum::bare::page_table_levels; 19 | using sanctum::bare::page_table_shift; 20 | using sanctum::bare::page_table_entry_shift; 21 | using sanctum::bare::page_table_entry_target; 22 | using sanctum::bare::page_table_translated_bits; 23 | using sanctum::bare::pages_needed_for; 24 | using sanctum::bare::size_t; 25 | using sanctum::bare::uintptr_t; 26 | 27 | // Checks if a given virtual address is a valid enclave virtual address. 28 | // 29 | // The caller should hold the lock of the enclave metadata's DRAM region. 30 | inline bool is_enclave_virtual_address(uintptr_t virtual_addr, 31 | enclave_id_t enclave_id) { 32 | phys_ptr enclave_info{enclave_id}; 33 | return (virtual_addr & enclave_info->*(&enclave_info_t::ev_mask)) == 34 | enclave_info->*(&enclave_info_t::ev_base); 35 | } 36 | 37 | // Walks a page table, stops at the entry at a given level. 38 | // 39 | // The level is assumed to be valid (between 0 and page_table_levels() - 1). 40 | // The ptb (page table base) and the page tables are all assumed to point to 41 | // accessible memory. This assumption only holds before an enclave is 42 | // initialized, when the monitor is in charge of its page tables. 43 | // 44 | // Returns 0 if the walk was interrupted due to a page table entry not being 45 | // valid / present. 46 | inline uintptr_t walk_page_tables_to_entry(uintptr_t ptb, 47 | uintptr_t virtual_addr, size_t level) { 48 | size_t addr_shift = page_table_translated_bits(); 49 | size_t table_addr = ptb; 50 | 51 | // NOTE: We're handling the special case of an unset PTB (page table base) 52 | // here because it's equivalent to the valid bit being unset on an 53 | // entry in the page tables. 54 | if (ptb == 0) 55 | return 0; 56 | 57 | size_t walk_level = page_table_levels(); 58 | while (true) { 59 | walk_level--; 60 | size_t level_addr_shift = page_table_shift(walk_level); 61 | addr_shift -= level_addr_shift; 62 | uintptr_t addr_mask = ((1 << level_addr_shift) - 1); 63 | uintptr_t entry_offset = (virtual_addr >> addr_shift) & addr_mask; 64 | 65 | uintptr_t entry_addr = table_addr + 66 | (entry_offset << page_table_entry_shift(walk_level)); 67 | if (walk_level == level) 68 | return entry_addr; 69 | if (!is_valid_page_table_entry(entry_addr, walk_level)) 70 | break; 71 | table_addr = page_table_entry_target(entry_addr, walk_level); 72 | } 73 | return 0; 74 | } 75 | 76 | // Performs a software virtual address translation. 77 | // 78 | // The level is assumed to be valid (between 0 and page_table_levels() - 1). 79 | // The ptb (page table base) and the page tables are all assumed to point to 80 | // accessible memory. This assumption only holds before an enclave is 81 | // initialized, when the monitor is in charge of its page tables. 82 | // 83 | // Returns 0 if the walk was interrupted due to a page table entry not being 84 | // valid / present. 85 | inline uintptr_t walk_page_tables(uintptr_t ptb, uintptr_t virtual_addr) { 86 | uintptr_t entry_addr = walk_page_tables_to_entry(ptb, virtual_addr, 0); 87 | if (entry_addr == 0) 88 | return 0; 89 | if (!is_valid_page_table_entry(entry_addr, 0)) 90 | return 0; 91 | return page_table_entry_target(entry_addr, 0); 92 | } 93 | 94 | // Initializes an enclave's metadata structure. 95 | // 96 | // The caller is responsible for validating all input parameters. The caller 97 | // must also hold the lock for the metadata region of the enclave metadata. 98 | inline void init_enclave_info(phys_ptr enclave_info, 99 | uintptr_t ev_base, uintptr_t ev_mask, size_t mailbox_count, bool debug) { 100 | atomic_flag_clear(&(enclave_info->*(&enclave_info_t::lock))); 101 | enclave_info->*(&enclave_info_t::mailbox_count) = mailbox_count; 102 | enclave_info->*(&enclave_info_t::is_initialized) = 0; 103 | enclave_info->*(&enclave_info_t::is_debug) = debug; 104 | enclave_info->*(&enclave_info_t::ev_base) = ev_base; 105 | enclave_info->*(&enclave_info_t::ev_mask) = ev_mask; 106 | enclave_info->*(&enclave_info_t::load_eptbr) = 0; 107 | enclave_info->*(&enclave_info_t::last_load_addr) = 0; 108 | enclave_info->*(&enclave_info_t::thread_count) = 0; 109 | enclave_info->*(&enclave_info_t::dram_region_count) = 0; 110 | init_enclave_hash(enclave_info, ev_base, ev_mask, mailbox_count, debug); 111 | } 112 | 113 | }; // namespace sanctum::internal 114 | }; // namespace sanctum 115 | #endif // !defined(MONITOR_ENCLAVE_INL_H_INCLUDED) 116 | -------------------------------------------------------------------------------- /src/monitor/enclave_inl_test.cc: -------------------------------------------------------------------------------- 1 | #include "enclave_inl.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | -------------------------------------------------------------------------------- /src/monitor/mailbox.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwnall/sanctum/ae0650c8d5048b968a6795db0fc152f81eb8480a/src/monitor/mailbox.cc -------------------------------------------------------------------------------- /src/monitor/mailbox.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_MAILBOX_H_INCLUDED) 2 | #define MONITOR_MAILBOX_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "crypto/hash.h" 6 | #include "public/api.h" 7 | 8 | namespace sanctum { 9 | namespace internal { 10 | 11 | using sanctum::api::enclave_id_t; 12 | using sanctum::api::enclave::mailbox_message_size; 13 | 14 | // Metadata for one mailbox. 15 | struct mailbox_t { 16 | size_t state; 17 | 18 | // The OS-assigned enclave ID of the expected sender. 19 | // 20 | // This is necessary to prevent a malicious enclave from DoSing other 21 | // enclaves in the system by spamming their mailboxes. This enclave ID should 22 | // not be trusted to identify the software inside the sender. 23 | enclave_id_t sender_id; 24 | 25 | // The measurement of the expected sender. 26 | // 27 | // This is a secure identifier for the software inside the sender enclave. 28 | hash_state_t sender_hash; 29 | 30 | // The message held by the mailbox. 31 | uintptr_t message[mailbox_message_size / sizeof(uintptr_t)]; 32 | }; 33 | 34 | }; // namespace sanctum::internal 35 | }; // namespace sanctum 36 | #endif // !defined(MONITOR_MAILBOX_H_INCLUDED) 37 | -------------------------------------------------------------------------------- /src/monitor/mailbox_test.cc: -------------------------------------------------------------------------------- 1 | #include "mailbox.h" 2 | 3 | #include "gtest/gtest.h" 4 | -------------------------------------------------------------------------------- /src/monitor/measure_inl.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_MEASURE_INL_H_INCLUDED) 2 | #define MONITOR_MEASURE_INL_H_INCLUDED 3 | 4 | #include "bare/memory.h" 5 | #include "bare/page_tables.h" 6 | #include "crypto/hash.h" 7 | #include "enclave.h" 8 | #include "public/api.h" 9 | 10 | namespace sanctum { 11 | namespace internal { 12 | 13 | using sanctum::api::thread_id_t; 14 | using sanctum::bare::page_size; 15 | using sanctum::crypto::extend_hash; 16 | using sanctum::crypto::finalize_hash; 17 | using sanctum::crypto::hash_block_size; 18 | using sanctum::crypto::hash_state_t; 19 | using sanctum::crypto::init_hash; 20 | 21 | // The layout of a hash block used to measure an enclave operation. 22 | // 23 | // The structure may not overlap the entire hash block, so init_enclave_hash() 24 | // zeroes the entire hash block. Every other function that uses the structure 25 | // is responsible for zeroing out the arguments that it sets after it's done 26 | // extending the enclave hash. The opcode field does not need to be zeroed out, 27 | // because every operation needs to set it. 28 | struct measurement_block_t { 29 | size_t opcode; 30 | uintptr_t ptr1, ptr2, ptr3, ptr4; 31 | size_t size1, size2; 32 | }; 33 | static_assert(sizeof(measurement_block_t) <= hash_block_size, 34 | "measurement_block_t does not fit in a hash block"); 35 | 36 | // Opcodes for enclave operations. 37 | constexpr size_t enclave_init_opcode = 0xAAAAAAAA; 38 | constexpr size_t load_page_table_opcode = 0xBBBBBBBB; 39 | constexpr size_t load_page_opcode = 0xCCCCCCCC; 40 | constexpr size_t load_thread_opcode = 0xDDDDDDDD; 41 | constexpr size_t finalize_enclave_opcode = 0xEEEEEEEE; 42 | 43 | // Computes the address of an enclave's buffer for measurement hashing. 44 | // 45 | // The buffer is used to put together 46 | inline phys_ptr enclave_measurement_block( 47 | phys_ptr enclave_info) { 48 | phys_ptr block_ptr = enclave_info->*(&enclave_info_t::hash_block); 49 | return phys_ptr{uintptr_t(block_ptr)}; 50 | } 51 | 52 | // Initializes an enclave's measurement hash. 53 | // 54 | // The caller must hold the lock of the enclave's main DRAM region. 55 | inline void init_enclave_hash(phys_ptr enclave_info, 56 | uintptr_t ev_base, uintptr_t ev_mask, size_t mailbox_count, bool debug) { 57 | // NOTE: 32-bit operations may be slow on 64-bit architectures, so we convert 58 | // the pointer to an architecture-native type before instantiating the 59 | // bzero template 60 | phys_ptr fast_hash_block = phys_ptr{uintptr_t( 61 | enclave_info->*(&enclave_info_t::hash_block))}; 62 | bzero(fast_hash_block, hash_block_size); 63 | init_hash(&(enclave_info->*(&enclave_info_t::hash))); 64 | 65 | phys_ptr block = 66 | enclave_measurement_block(enclave_info); 67 | block->*(&measurement_block_t::opcode) = enclave_init_opcode; 68 | block->*(&measurement_block_t::ptr1) = ev_base; 69 | block->*(&measurement_block_t::ptr2) = ev_mask; 70 | block->*(&measurement_block_t::size1) = mailbox_count; 71 | block->*(&measurement_block_t::size2) = debug; 72 | 73 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), 74 | enclave_info->*(&enclave_info_t::hash_block)); 75 | block->*(&measurement_block_t::ptr1) = 0; 76 | block->*(&measurement_block_t::ptr2) = 0; 77 | block->*(&measurement_block_t::size1) = 0; 78 | block->*(&measurement_block_t::size2) = 0; 79 | } 80 | 81 | // Adds a page table creation operation to an enclave's measurement hash. 82 | // 83 | // The caller must hold the lock of the encalve's main DRAM region. 84 | inline void extend_enclave_hash_with_page_table( 85 | phys_ptr enclave_info, uintptr_t virtual_addr, 86 | size_t level, uintptr_t acl) { 87 | phys_ptr block = 88 | enclave_measurement_block(enclave_info); 89 | block->*(&measurement_block_t::opcode) = load_page_table_opcode; 90 | block->*(&measurement_block_t::ptr1) = virtual_addr; 91 | block->*(&measurement_block_t::ptr2) = acl; 92 | block->*(&measurement_block_t::size1) = level; 93 | 94 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), 95 | enclave_info->*(&enclave_info_t::hash_block)); 96 | block->*(&measurement_block_t::ptr1) = 0; 97 | block->*(&measurement_block_t::ptr2) = 0; 98 | block->*(&measurement_block_t::size1) = 0; 99 | } 100 | 101 | // Adds a page creation operation to an enclave's measurement hash. 102 | // 103 | // The caller must hold the lock of the encalve's main DRAM region. 104 | // 105 | // `phys_addr` is not included in the measurement. It's used to read in the 106 | // page and hash its contents. 107 | inline void extend_enclave_hash_with_page( 108 | phys_ptr enclave_info, uintptr_t virtual_addr, 109 | uintptr_t acl, uintptr_t phys_addr) { 110 | phys_ptr block = 111 | enclave_measurement_block(enclave_info); 112 | block->*(&measurement_block_t::opcode) = load_page_opcode; 113 | block->*(&measurement_block_t::ptr1) = virtual_addr; 114 | block->*(&measurement_block_t::ptr2) = acl; 115 | 116 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), 117 | enclave_info->*(&enclave_info_t::hash_block)); 118 | block->*(&measurement_block_t::ptr1) = 0; 119 | block->*(&measurement_block_t::ptr2) = 0; 120 | 121 | phys_ptr page_end{phys_addr + page_size()}; 122 | for(phys_ptr page_ptr{phys_addr}; page_ptr != page_end; 123 | page_ptr += hash_block_size / sizeof(uint32_t)) { 124 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), page_ptr); 125 | } 126 | } 127 | 128 | // Adds a thread creation operation to an enclave's measurement hash. 129 | // 130 | // The caller must hold the lock of the encalve's main DRAM region. 131 | inline void extend_enclave_hash_with_thread( 132 | phys_ptr enclave_info, uintptr_t entry_pc, 133 | uintptr_t entry_stack, uintptr_t fault_pc, uintptr_t fault_stack) { 134 | phys_ptr block = 135 | enclave_measurement_block(enclave_info); 136 | block->*(&measurement_block_t::opcode) = load_thread_opcode; 137 | block->*(&measurement_block_t::ptr1) = entry_pc; 138 | block->*(&measurement_block_t::ptr2) = entry_stack; 139 | block->*(&measurement_block_t::ptr3) = fault_pc; 140 | block->*(&measurement_block_t::ptr4) = fault_stack; 141 | 142 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), 143 | enclave_info->*(&enclave_info_t::hash_block)); 144 | block->*(&measurement_block_t::ptr1) = 0; 145 | block->*(&measurement_block_t::ptr2) = 0; 146 | block->*(&measurement_block_t::ptr3) = 0; 147 | block->*(&measurement_block_t::ptr4) = 0; 148 | } 149 | 150 | // Finalizes the enclave's measurement hash. 151 | inline void finalize_enclave_hash(phys_ptr enclave_info) { 152 | phys_ptr block = 153 | enclave_measurement_block(enclave_info); 154 | block->*(&measurement_block_t::opcode) = finalize_enclave_opcode; 155 | 156 | extend_hash(&(enclave_info->*(&enclave_info_t::hash)), 157 | enclave_info->*(&enclave_info_t::hash_block)); 158 | finalize_hash(&(enclave_info->*(&enclave_info_t::hash))); 159 | } 160 | 161 | }; // namespace sanctum::internal 162 | }; // namespace sanctum 163 | #endif // !defined(MONITOR_MEASURE_INL_H_INCLUDED) 164 | -------------------------------------------------------------------------------- /src/monitor/measure_inl_test.cc: -------------------------------------------------------------------------------- 1 | #include "measure_inl.h" 2 | -------------------------------------------------------------------------------- /src/monitor/metadata.cc: -------------------------------------------------------------------------------- 1 | #include "metadata.h" 2 | 3 | #include "bare/page_tables.h" 4 | #include "bare/phys_ptr.h" 5 | #include "cpu_core_inl.h" 6 | #include "dram_regions_inl.h" 7 | #include "enclave_inl.h" 8 | #include "measure_inl.h" 9 | #include "metadata_inl.h" 10 | 11 | namespace sanctum { 12 | namespace internal { // sanctum::internal 13 | 14 | size_t g_metadata_region_pages; 15 | size_t g_metadata_region_start; 16 | 17 | }; // namespace sanctum::internal 18 | }; // namespace sanctum 19 | 20 | using sanctum::bare::is_page_aligned; 21 | using sanctum::bare::is_valid_range; 22 | using sanctum::bare::page_shift; 23 | using sanctum::bare::page_size; 24 | using sanctum::bare::phys_ptr; 25 | using sanctum::internal::accept_metadata_pages; 26 | using sanctum::internal::clear_dram_region_lock; 27 | using sanctum::internal::current_enclave; 28 | using sanctum::internal::dram_region_for; 29 | using sanctum::internal::dram_region_info_t; 30 | using sanctum::internal::dram_region_start; 31 | using sanctum::internal::enclave_info_pages; 32 | using sanctum::internal::enclave_info_t; 33 | using sanctum::internal::enclave_metadata_page_type; 34 | using sanctum::internal::g_dram_region; 35 | using sanctum::internal::g_metadata_region_pages; 36 | using sanctum::internal::g_metadata_region_start; 37 | using sanctum::internal::free_enclave_id; 38 | using sanctum::internal::is_dram_address; 39 | using sanctum::internal::init_enclave_info; 40 | using sanctum::internal::lock_enclave; 41 | using sanctum::internal::lock_metadata_region_for; 42 | using sanctum::internal::metadata_enclave_id; 43 | using sanctum::internal::metadata_page_info_t; 44 | using sanctum::internal::read_dram_region_owner; 45 | using sanctum::internal::reserve_metadata_pages; 46 | using sanctum::internal::test_and_set_dram_region_lock; 47 | using sanctum::internal::thread_metadata_page_type; 48 | using sanctum::internal::thread_info_t; 49 | using sanctum::internal::unlock_enclave; 50 | 51 | namespace sanctum { 52 | namespace api { // sanctum::api 53 | namespace os { // sancum::api::os 54 | 55 | size_t metadata_region_pages() { 56 | return g_metadata_region_pages; 57 | } 58 | 59 | size_t metadata_region_start() { 60 | return g_metadata_region_start; 61 | } 62 | 63 | size_t enclave_metadata_pages(size_t mailbox_count) { 64 | return enclave_info_pages(mailbox_count); 65 | } 66 | 67 | size_t thread_metadata_pages() { 68 | return sanctum::internal::thread_metadata_pages(); 69 | } 70 | 71 | api_result_t create_enclave(enclave_id_t enclave_id, uintptr_t ev_base, 72 | uintptr_t ev_mask, size_t mailbox_count, bool debug) { 73 | if (!is_valid_range(ev_base, ev_mask)) 74 | return monitor_invalid_value; 75 | if (ev_mask + 1 < page_size()) 76 | return monitor_invalid_value; 77 | 78 | size_t dram_region; 79 | api_result_t result = lock_metadata_region_for(enclave_id, dram_region); 80 | if (result != monitor_ok) 81 | return result; 82 | 83 | phys_ptr region = &g_dram_region[dram_region]; 84 | if (region->*(&dram_region_info_t::owner) != metadata_enclave_id) { 85 | clear_dram_region_lock(dram_region); 86 | return monitor_invalid_value; 87 | } 88 | 89 | result = reserve_metadata_pages(enclave_id, 90 | enclave_info_pages(mailbox_count), enclave_id, 91 | enclave_metadata_page_type); 92 | if (result != monitor_ok) { 93 | clear_dram_region_lock(dram_region); 94 | return result; 95 | } 96 | 97 | init_enclave_info(phys_ptr{enclave_id}, ev_base, ev_mask, 98 | mailbox_count, debug); 99 | clear_dram_region_lock(dram_region); 100 | return monitor_ok; 101 | } 102 | 103 | api_result_t assign_thread(enclave_id_t enclave_id, thread_id_t thread_id) { 104 | api_result_t result = lock_enclave(enclave_id); 105 | if (result != monitor_ok) 106 | return result; 107 | 108 | size_t dram_region; 109 | result = lock_metadata_region_for(thread_id, dram_region); 110 | if (result != monitor_ok) { 111 | unlock_enclave(enclave_id); 112 | return result; 113 | } 114 | 115 | phys_ptr enclave_info{enclave_id}; 116 | if (enclave_info->*(&enclave_info_t::is_initialized)) { 117 | result = reserve_metadata_pages(enclave_id, thread_metadata_pages(), 118 | enclave_id, thread_metadata_page_type); 119 | } else { 120 | result = monitor_invalid_state; 121 | } 122 | 123 | if (result != monitor_ok) { 124 | clear_dram_region_lock(dram_region); 125 | unlock_enclave(enclave_id); 126 | return result; 127 | } 128 | 129 | enclave_info->*(&enclave_info_t::thread_count) += 1; 130 | 131 | clear_dram_region_lock(dram_region); 132 | unlock_enclave(enclave_id); 133 | return monitor_ok; 134 | } 135 | 136 | api_result_t load_thread(enclave_id_t enclave_id, 137 | thread_id_t thread_id, uintptr_t entry_pc, uintptr_t entry_stack, 138 | uintptr_t fault_pc, uintptr_t fault_stack) { 139 | api_result_t result = lock_enclave(enclave_id); 140 | if (result != monitor_ok) 141 | return result; 142 | 143 | size_t thread_dram_region; 144 | result = lock_metadata_region_for(thread_id, thread_dram_region); 145 | if (result != monitor_ok) { 146 | unlock_enclave(enclave_id); 147 | return result; 148 | } 149 | 150 | phys_ptr enclave_info{enclave_id}; 151 | if (enclave_info->*(&enclave_info_t::is_initialized) || 152 | enclave_info->*(&enclave_info_t::load_eptbr) == 0) { 153 | result = monitor_invalid_state; 154 | } else { 155 | result = reserve_metadata_pages(enclave_id, thread_metadata_pages(), 156 | enclave_id, thread_metadata_page_type); 157 | } 158 | 159 | if (result != monitor_ok) { 160 | clear_dram_region_lock(thread_dram_region); 161 | unlock_enclave(enclave_id); 162 | return result; 163 | } 164 | 165 | enclave_info->*(&enclave_info_t::thread_count) += 1; 166 | 167 | phys_ptr thread_metadata{thread_id}; 168 | atomic_flag_clear(&(thread_metadata->*(&thread_info_t::lock))); 169 | thread_metadata->*(&thread_info_t::entry_pc) = entry_pc; 170 | thread_metadata->*(&thread_info_t::entry_stack) = entry_stack; 171 | thread_metadata->*(&thread_info_t::fault_pc) = fault_pc; 172 | thread_metadata->*(&thread_info_t::fault_stack) = fault_stack; 173 | thread_metadata->*(&thread_info_t::eptbr) = 174 | enclave_info->*(&enclave_info_t::load_eptbr); 175 | 176 | extend_enclave_hash_with_thread(enclave_info, entry_pc, entry_stack, 177 | fault_pc, fault_stack); 178 | 179 | clear_dram_region_lock(thread_dram_region); 180 | unlock_enclave(enclave_id); 181 | return monitor_ok; 182 | } 183 | 184 | }; // namespace sanctum::api::os 185 | 186 | namespace enclave { // namespace sanctum::api::enclave 187 | 188 | static_assert(sizeof(thread_init_info_t) <= page_size(), 189 | "accept_thread assumes that thread_init_info_t fits into one page"); 190 | 191 | api_result_t accept_thread(thread_id_t thread_id, uintptr_t thread_info_addr) { 192 | if (!is_dram_address(thread_info_addr) || !is_page_aligned(thread_info_addr)) 193 | return monitor_invalid_value; 194 | 195 | enclave_id_t enclave_id = current_enclave(); 196 | 197 | size_t thread_dram_region = dram_region_for(thread_info_addr); 198 | if (test_and_set_dram_region_lock(thread_info_addr)) 199 | return monitor_concurrent_call; 200 | 201 | if (read_dram_region_owner(thread_dram_region) != enclave_id) { 202 | clear_dram_region_lock(thread_dram_region); 203 | return monitor_invalid_value; 204 | } 205 | 206 | // NOTE: This enclave_id is known to be correct, so we can use a faster path 207 | // to lock the enclave's metadata region. 208 | size_t enclave_dram_region = dram_region_for(enclave_id); 209 | if (test_and_set_dram_region_lock(enclave_dram_region)) { 210 | clear_dram_region_lock(thread_dram_region); 211 | return monitor_concurrent_call; 212 | } 213 | 214 | api_result_t result = accept_metadata_pages(thread_id, 215 | sanctum::internal::thread_metadata_pages(), enclave_id, 216 | thread_metadata_page_type); 217 | if (result != monitor_ok) { 218 | clear_dram_region_lock(enclave_dram_region); 219 | clear_dram_region_lock(thread_dram_region); 220 | return result; 221 | } 222 | 223 | // NOTE: The enclave's thread_count is NOT incremented here, because this 224 | // thread was already accounted for in assign_thread(). 225 | 226 | phys_ptr thread_metadata{thread_id}; 227 | phys_ptr thread_info{thread_info_addr}; 228 | 229 | atomic_flag_clear(&(thread_metadata->*(&thread_info_t::lock))); 230 | 231 | uintptr_t entry_pc = *phys_ptr{ 232 | uintptr_t{&(thread_info->*(&thread_init_info_t::entry_pc))}}; 233 | uintptr_t entry_stack = *phys_ptr{ 234 | uintptr_t{&(thread_info->*(&thread_init_info_t::entry_stack))}}; 235 | uintptr_t fault_pc = *phys_ptr{ 236 | uintptr_t{&(thread_info->*(&thread_init_info_t::fault_pc))}}; 237 | uintptr_t fault_stack = *phys_ptr{ 238 | uintptr_t{&(thread_info->*(&thread_init_info_t::fault_stack))}}; 239 | uintptr_t eptbr = thread_info->*(&thread_init_info_t::eptbr); 240 | 241 | thread_metadata->*(&thread_info_t::entry_pc) = entry_pc; 242 | thread_metadata->*(&thread_info_t::entry_stack) = entry_stack; 243 | thread_metadata->*(&thread_info_t::fault_pc) = fault_pc; 244 | thread_metadata->*(&thread_info_t::fault_stack) = fault_stack; 245 | thread_metadata->*(&thread_info_t::eptbr) = eptbr; 246 | 247 | clear_dram_region_lock(enclave_dram_region); 248 | clear_dram_region_lock(thread_dram_region); 249 | return monitor_ok; 250 | } 251 | 252 | }; // namespace sanctum::api::enclave 253 | }; // namespace sanctum::api 254 | }; // namespace sanctum 255 | -------------------------------------------------------------------------------- /src/monitor/metadata.h: -------------------------------------------------------------------------------- 1 | #if !defined(MONITOR_METADATA_H_INCLUDED) 2 | #define MONITOR_METADATA_H_INCLUDED 3 | 4 | #include "bare/base_types.h" 5 | #include "bare/phys_atomics.h" 6 | #include "public/api.h" 7 | 8 | // Metadata regions are DRAM regions that are dedicated to storing the metadata 9 | // used by the monitor to manage enclaves. The metadata is not stored inside 10 | // enclave regions so that the monitor can read or modify the metadata without 11 | // impacting an enclave's LLC lines. 12 | // 13 | // Each metadata region is managed as a collection of pages. The first few 14 | // pages store an array of metadata_page_info_t elements, and the other pages 15 | // are usable for metadata storage. Each metadata_page_info_t element indicates 16 | // the ownership and data type of its corresponding metadata page. 17 | // 18 | // For simplicity, the monitor implementation assumes that the 19 | // metadata_page_info_t array fits into a single DRAM regon stripe. The boot 20 | // initialization sequence ensures that the invariant holds. 21 | 22 | namespace sanctum { 23 | namespace internal { 24 | 25 | using sanctum::api::enclave_id_t; 26 | using sanctum::bare::phys_ptr; 27 | using sanctum::bare::uintptr_t; 28 | 29 | // The page map entry for a metadata page is packed in a single pointer. 30 | // 31 | // This is possible because an enclave ID points to the enclave's metadata 32 | // page, so each enclave ID is page-aligned. This makes the bottom bits 33 | // available for storing accounting information. 34 | typedef uintptr_t metadata_page_info_t; 35 | 36 | // Mask that selects the metadata page type bits. 37 | constexpr metadata_page_info_t metadata_page_type_mask = 3; 38 | 39 | // Type for metadata pages that have been assigned to enclaves but not used. 40 | // 41 | // These pages are waiting to be turned into threat_info_t pages by the enclave 42 | // software. 43 | constexpr metadata_page_info_t empty_metadata_page_type = 0; 44 | 45 | // Type for metadata pages that store inner pages for data structures. 46 | // 47 | // The first page of each data structure has a type that identifies the data 48 | // structure. The other pages use the inner type. This way, we can check that 49 | // an address points to the beginning of a metadata structure by comparing the 50 | // page's type against the expected structure type. 51 | constexpr metadata_page_info_t inner_metadata_page_type = 1; 52 | 53 | // Type for metadata pages that hold an enclave's enclave_info_t. 54 | constexpr metadata_page_info_t enclave_metadata_page_type = 2; 55 | 56 | // Type for metadata pages that hold a thread_info_t for an enclave. 57 | constexpr metadata_page_info_t thread_metadata_page_type = 3; 58 | 59 | // Total number of metadata pages in a DRAM region dedicated to metadata. 60 | extern size_t g_metadata_region_pages; 61 | 62 | // The first usable metadata page in a DRAM region dedicated to metadata. 63 | // 64 | // Unusable pages contain a map of all the pages, ensuring that metadata pages 65 | // aren't double-allocated. 66 | extern size_t g_metadata_region_start; 67 | 68 | }; // namespace sanctum::internal 69 | }; // namespace sanctum 70 | #endif // !defined(MONITOR_METADATA_H_INCLUDED) 71 | -------------------------------------------------------------------------------- /src/monitor/metadata_inl_test.cc: -------------------------------------------------------------------------------- 1 | #include "metadata_inl.h" 2 | 3 | #include "bare/bit_masking.h" 4 | 5 | #include "gtest/gtest.h" 6 | 7 | using sanctum::api::enclave_id_t; 8 | using sanctum::bare::is_power_of_two; 9 | using sanctum::bare::page_size; 10 | using sanctum::internal::empty_metadata_page_info; 11 | using sanctum::internal::empty_metadata_page_type; 12 | using sanctum::internal::enclave_metadata_page_type; 13 | using sanctum::internal::inner_metadata_page_type; 14 | using sanctum::internal::metadata_page_info; 15 | using sanctum::internal::metadata_page_info_t; 16 | using sanctum::internal::metadata_page_info_type; 17 | using sanctum::internal::metadata_page_type_mask; 18 | using sanctum::internal::thread_metadata_page_type; 19 | 20 | TEST(MetadataPageInfo, SizeAndMasks) { 21 | static_assert(is_power_of_two(sizeof(metadata_page_info_t)), 22 | "metadata_page_info_t's size must be a power of two"); 23 | static_assert(sizeof(metadata_page_info_t) >= sizeof(enclave_id_t), 24 | "metadata_page_info_t must be able to hold an enclave_id_t"); 25 | 26 | static_assert(metadata_page_type_mask < (page_size() - 1), 27 | "metadata_page_type_mask must fit into untranslated address bits"); 28 | static_assert((empty_metadata_page_type & metadata_page_type_mask) 29 | == empty_metadata_page_type, 30 | "metadata_page_type_mask must cover empty_metadata_page_type"); 31 | static_assert((inner_metadata_page_type & metadata_page_type_mask) 32 | == inner_metadata_page_type, 33 | "metadata_page_type_mask must cover inner_metadata_page_type"); 34 | static_assert((enclave_metadata_page_type & metadata_page_type_mask) 35 | == enclave_metadata_page_type, 36 | "metadata_page_type_mask must cover enclave_metadata_page_type"); 37 | static_assert((thread_metadata_page_type & metadata_page_type_mask) 38 | == thread_metadata_page_type, 39 | "metadata_page_type_mask must cover thread_metadata_page_type"); 40 | } 41 | 42 | TEST(MetadataPageInfo, MetadataPageInfo) { 43 | static_assert(metadata_page_info(0xAA0000, 0x3) == 0xAA0003, 44 | "incorrect metadata_page_info implementation"); 45 | } 46 | 47 | TEST(MetadataPageInfo, EmptyMetadataPageInfo) { 48 | static_assert(empty_metadata_page_info == 0, 49 | "empty_metadata_page_info must be zero"); 50 | } 51 | 52 | TEST(MetadataPageInfo, MetadataPageInfoType) { 53 | static_assert(metadata_page_info_type(0xFFF000) == 0, 54 | "incorrect metadata_page_info_type implementation"); 55 | static_assert(metadata_page_info_type(0xEFE001) == 1, 56 | "incorrect metadata_page_info_type implementation"); 57 | static_assert(metadata_page_info_type(0xEEE0002) == 2, 58 | "incorrect metadata_page_info_type implementation"); 59 | static_assert(metadata_page_info_type(0xAA0003) == 3, 60 | "incorrect metadata_page_info_type implementation"); 61 | } 62 | -------------------------------------------------------------------------------- /src/monitor/monitor.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'includes': [ 3 | '../common.gypi', 4 | ], 5 | 'variables': { 6 | 'monitor_sources': [ 7 | 'api_compile_check.cc', 8 | 'boot_init.cc', 9 | 'boot_init.h', 10 | 'cpu_core.cc', 11 | 'cpu_core.h', 12 | 'cpu_core_inl.h', 13 | 'dram_regions.cc', 14 | 'dram_regions.h', 15 | 'dram_regions_inl.h', 16 | 'enclave.cc', 17 | 'enclave.h', 18 | 'enclave_init.cc', 19 | 'enclave_inl.h', 20 | 'mailbox.h', 21 | 'mailbox.cc', 22 | 'metadata.cc', 23 | 'metadata.h', 24 | 'metadata_inl.h', 25 | 'public/api.h', 26 | ], 27 | }, 28 | 'targets': [ 29 | { 30 | # The security monitor. 31 | 'target_name': 'monitor', 32 | 'type': '<(library)', 33 | 'sources': [ 34 | '<@(monitor_sources)', 35 | ], 36 | 'target_defaults': { 37 | 'cflags_cc+': [ 38 | ], 39 | 'xcode_settings': { 40 | }, 41 | }, 42 | 'direct_dependent_settings': { 43 | 'include_dirs': [ 44 | '..', 45 | ], 46 | }, 47 | 'dependencies': [ 48 | '../bare/bare.gyp:bare', 49 | '../crypto/crypto.gyp:crypto', 50 | ], 51 | }, 52 | { 53 | # Unit tests for the monitor. 54 | 'target_name': 'monitor_tests', 55 | 'type': 'executable', 56 | 'sources': [ 57 | '<@(monitor_sources)', 58 | 'boot_init_test.cc', 59 | 'cpu_core_inl_test.cc', 60 | 'cpu_core_test.cc', 61 | 'dram_regions_inl_test.cc', 62 | 'enclave_inl_test.cc', 63 | 'mailbox_test.cc', 64 | 'measure_inl_test.cc', 65 | 'metadata_inl_test.cc', 66 | ], 67 | 'dependencies': [ 68 | '../bare/bare.gyp:bare_testing', 69 | '../crypto/crypto.gyp:crypto_testing', 70 | '../deps/gtest.gyp:gtest', 71 | '../deps/libcxx.gyp:libc++', 72 | ], 73 | }, 74 | ], 75 | } 76 | --------------------------------------------------------------------------------