From 8b80e0ec238e4430484fb50c0456441716c99a82 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 15 May 2020 14:58:45 -0700 Subject: [PATCH] exo2: implement SmcComputeAes, SmcGetResult, SmcGetResultData --- libexosphere/include/exosphere/gic.hpp | 2 + libexosphere/include/exosphere/se/se_aes.hpp | 4 + .../include/exosphere/se/se_management.hpp | 2 + .../exosphere/secmon/secmon_memory_layout.hpp | 1 + libexosphere/source/gic/gic_api.cpp | 4 + libexosphere/source/se/se_aes.cpp | 137 +++++++++++++++--- libexosphere/source/se/se_execute.cpp | 25 +++- libexosphere/source/se/se_execute.hpp | 3 + libexosphere/source/se/se_management.cpp | 8 + 9 files changed, 168 insertions(+), 18 deletions(-) diff --git a/libexosphere/include/exosphere/gic.hpp b/libexosphere/include/exosphere/gic.hpp index 72e30432..4b866d4e 100644 --- a/libexosphere/include/exosphere/gic.hpp +++ b/libexosphere/include/exosphere/gic.hpp @@ -36,6 +36,8 @@ namespace ams::gic { void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask); void SetSpiMode(int interrupt_id, InterruptMode mode); + void SetPending(int interrupt_id); + int GetInterruptRequestId(); void SetEndOfInterrupt(int interrupt_id); diff --git a/libexosphere/include/exosphere/se/se_aes.hpp b/libexosphere/include/exosphere/se/se_aes.hpp index a9bf3e67..b7882041 100644 --- a/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libexosphere/include/exosphere/se/se_aes.hpp @@ -35,4 +35,8 @@ namespace ams::se { void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); + } diff --git a/libexosphere/include/exosphere/se/se_management.hpp b/libexosphere/include/exosphere/se/se_management.hpp index f5a8e6be..05369d1f 100644 --- a/libexosphere/include/exosphere/se/se_management.hpp +++ b/libexosphere/include/exosphere/se/se_management.hpp @@ -30,4 +30,6 @@ namespace ams::se { void HandleInterrupt(); + void ValidateAesOperationResult(); + } \ No newline at end of file diff --git a/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index a7818c11..38e6452b 100644 --- a/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -73,6 +73,7 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionVirtual = MemoryRegion(UINT64_C(0x1F0000000), 2_MB); constexpr inline const MemoryRegion MemoryRegionPhysical = MemoryRegion(UINT64_C( 0x40000000), 1_GB); constexpr inline const MemoryRegion MemoryRegionDram = MemoryRegion(UINT64_C( 0x80000000), 2_GB); + constexpr inline const MemoryRegion MemoryRegionDramHigh = MemoryRegion(MemoryRegionDram.GetEndAddress(), 2_GB); constexpr inline const MemoryRegion MemoryRegionDramGpuCarveout = MemoryRegion(UINT64_C(0x80020000), UINT64_C(0x40000)); static_assert(MemoryRegionDram.Contains(MemoryRegionDramGpuCarveout)); diff --git a/libexosphere/source/gic/gic_api.cpp b/libexosphere/source/gic/gic_api.cpp index 70e36002..220713e7 100644 --- a/libexosphere/source/gic/gic_api.cpp +++ b/libexosphere/source/gic/gic_api.cpp @@ -206,6 +206,10 @@ namespace ams::gic { ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast(mode) << 1); } + void SetPending(int interrupt_id) { + Write(g_distributor_address + offsetof(GicDistributor, ispendr), 1, interrupt_id, 1); + } + int GetInterruptRequestId() { return reg::Read(GetCpuInterface()->iar); } diff --git a/libexosphere/source/se/se_aes.cpp b/libexosphere/source/se/se_aes.cpp index 138e18cf..ed0ec0e9 100644 --- a/libexosphere/source/se/se_aes.cpp +++ b/libexosphere/source/se/se_aes.cpp @@ -33,21 +33,37 @@ namespace ams::se { MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF, }; - constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); - constexpr inline u32 AesConfigCtr = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 1), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, LINEAR_CTR), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), - SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigCtr = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 1), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, LINEAR_CTR), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + constexpr inline u32 AesConfigCbcEncrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_AESOUT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, TOP), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + + constexpr inline u32 AesConfigCbcDecrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_PREV_MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) { reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128), @@ -73,9 +89,9 @@ namespace ams::se { reg::ReadWrite(SE->SE_CONFIG, REG_BITS_VALUE(16, 16, mode)); } - // void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { - // reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); - // } + void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) { + reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif)); + } void SetCounter(volatile SecurityEngineRegisters *SE, const void *ctr) { const u32 *ctr_32 = reinterpret_cast(ctr); @@ -87,6 +103,25 @@ namespace ams::se { reg::Write(SE->SE_CRYPTO_LINEAR_CTR[3], util::LoadLittleEndian(ctr_32 + 3)); } + void SetAesKeyIv(volatile SecurityEngineRegisters *SE, int slot, const void *iv, size_t iv_size) { + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + AMS_ABORT_UNLESS(iv_size <= AesBlockSize); + + /* Set each iv word in order. */ + const u32 *iv_u32 = static_cast(iv); + const int num_words = iv_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the key word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = *(iv_u32++); + } + } + void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) { AMS_ABORT_UNLESS(key_size <= AesKeySizeMax); AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount); @@ -133,6 +168,29 @@ namespace ams::se { ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { + /* If nothing to decrypt, succeed. */ + if (size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Configure for the specific operation. */ + SetConfig(SE, encrypt, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, encrypt, config); + UpdateMemoryInterface(SE, MemoryInterface_Mc); + + /* Configure the number of blocks. */ + const int num_blocks = size / AesBlockSize; + SetBlockCount(SE, num_blocks); + + /* Set the done handler. */ + SetDoneHandler(SE, handler); + + /* Start the raw operation. */ + StartOperationRaw(SE, SE_OPERATION_OP_START, out_ll_address, in_ll_address); + } + } void ClearAesKeySlot(int slot) { @@ -270,4 +328,49 @@ namespace ams::se { } } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCbcEncrypt, true, SE); + } + + void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCbcDecrypt, false, SE); + } + + void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { + /* Validate the iv. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + + /* Get the registers. */ + volatile auto *SE = GetRegisters(); + + /* Here Nintendo writes 1 to SE_SPARE. It's unclear why they do this, but we will do so as well. */ + SE->SE_SPARE = 0x1; + + /* Set the counter. */ + SetCounter(SE, iv); + + /* Perform the asynchronous aes operation. */ + ComputeAes128Async(out_ll_address, slot, in_ll_address, size, handler, AesConfigCtr, true, SE); + } + } diff --git a/libexosphere/source/se/se_execute.cpp b/libexosphere/source/se/se_execute.cpp index 35b18fcd..47252e77 100644 --- a/libexosphere/source/se/se_execute.cpp +++ b/libexosphere/source/se/se_execute.cpp @@ -64,6 +64,13 @@ namespace ams::se { reg::Write(SE->SE_OPERATION, SE_REG_BITS_VALUE(OPERATION_OP, op)); } + void EnsureOperationStarted(volatile SecurityEngineRegisters *SE) { + /* Read the operation register to make sure our write takes. */ + reg::Read(SE->SE_OPERATION); + + hw::DataSynchronizationBarrierInnerShareable(); + } + void WaitForOperationComplete(volatile SecurityEngineRegisters *SE) { /* Spin until the operation is done. */ while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_SE_OP_DONE, CLEAR))) { /* ... */ } @@ -124,6 +131,18 @@ namespace ams::se { std::memcpy(dst, aligned, dst_size); } + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) { + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, in_ll_address); + reg::Write(SE->SE_OUT_LL_ADDR, out_ll_address); + + /* Start the operation. */ + StartOperation(SE, op); + + /* Ensure the operation is started. */ + EnsureOperationStarted(SE); + } + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE) { /* Ensure no error occurred. */ AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); @@ -132,7 +151,11 @@ namespace ams::se { AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE))); /* Ensure there is no error status. */ - AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + } + + void ValidateAesOperationResult() { + return ValidateAesOperationResult(GetRegisters()); } } diff --git a/libexosphere/source/se/se_execute.hpp b/libexosphere/source/se/se_execute.hpp index 76b0a34a..5b242c59 100644 --- a/libexosphere/source/se/se_execute.hpp +++ b/libexosphere/source/se/se_execute.hpp @@ -23,6 +23,9 @@ namespace ams::se { void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address); + void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler); + void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE); } diff --git a/libexosphere/source/se/se_management.cpp b/libexosphere/source/se/se_management.cpp index 8c145fd5..0c577a01 100644 --- a/libexosphere/source/se/se_management.cpp +++ b/libexosphere/source/se/se_management.cpp @@ -105,4 +105,12 @@ namespace ams::se { } } + void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler) { + /* Set the done handler. */ + g_done_handler = handler; + + /* Configure to trigger an interrupt when done. */ + reg::Write(SE->SE_INT_ENABLE, SE_REG_BITS_ENUM(INT_ENABLE_SE_OP_DONE, ENABLE)); + } + }