From 2fb363dcf01bd7a0069d5100f5637f05641d9db9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Jun 2020 00:41:27 -0700 Subject: [PATCH] exo2: implement the rest of cpu suspend (security checks TODO) --- exosphere2/program/source/secmon_setup.cpp | 2 +- .../smc/secmon_smc_power_management.cpp | 129 +++++++++++- .../exosphere/pkg1/pkg1_se_key_slots.hpp | 3 +- .../libexosphere/include/exosphere/pmc.hpp | 4 +- .../include/exosphere/se/se_aes.hpp | 3 + .../include/exosphere/se/se_management.hpp | 3 +- .../include/exosphere/se/se_suspend.hpp | 3 +- .../exosphere/tegra/tegra_flow_ctlr.hpp | 2 +- .../include/exosphere/tegra/tegra_pmc.hpp | 2 + libraries/libexosphere/source/se/se_aes.cpp | 39 ++++ .../libexosphere/source/se/se_registers.hpp | 10 + .../libexosphere/source/se/se_suspend.cpp | 192 ++++++++++++++++++ 12 files changed, 384 insertions(+), 8 deletions(-) diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index a6a9ce914..f81642763 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -1007,7 +1007,7 @@ namespace ams::secmon { se::InitializeRandom(); se::SetRandomKey(pkg1::AesKeySlot_Temporary); se::GenerateSrk(); - se::SetRandomKey(pkg1::AesKeySlot_TzramSave); + se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); /* Initialize pmc secure scratch. */ pmc::InitializeRandomScratch(); diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 4398a6961..bc6c8f580 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -17,6 +17,7 @@ #include "../secmon_cache.hpp" #include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" +#include "../secmon_misc.hpp" #include "secmon_smc_power_management.hpp" #include "secmon_smc_se_lock.hpp" @@ -152,11 +153,137 @@ namespace ams::secmon::smc { } void ValidateSocStateForSuspend() { + /* Validate that all other cores are off. */ + AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0))); + + /* Validate that the bpmp is appropriately halted. */ + AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) != reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED))); + /* TODO */ } + void GenerateCryptographicallyRandomBytes(void * const dst, int size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, size); + hw::DataSynchronizationBarrierInnerShareable(); + } + void SaveSecureContextForErista() { - /* TODO */ + /* Generate a random key source. */ + util::AlignedBuffer key_source; + GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize); + + const u32 * const key_source_32 = reinterpret_cast(static_cast(key_source)); + + /* Ensure that the key source registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked); + + /* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]); + pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked); + + /* Generate a random kek into keyslot 2. */ + se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek); + + /* Verify that the se is in a validate state, context save, and validate again. */ + { + se::ValidateErrStatus(); + ON_SCOPE_EXIT { se::ValidateErrStatus(); }; + + { + /* Transition to non-secure mode for the duration of the context save operation. */ + se::SetSecure(false); + ON_SCOPE_EXIT { se::SetSecure(true); }; + + /* Get a pointer to the context storage. */ + se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer(); + static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context)); + + /* Save the context. */ + se::SaveContext(context); + + /* Ensure that the cpu sees consistent data. */ + hw::FlushDataCache(context, sizeof(*context)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */ + reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress()); + } + } + + /* Clear keyslot 3, and then derive the save key. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + + /* Declare a temporary block to be used as both iv and mac. */ + u32 temp_block[se::AesBlockSize / sizeof(u32)] = {}; + + /* Ensure that the SE sees consistent data for tzram. */ + const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize(); + void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer(); + constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize(); + + hw::FlushDataCache(tzram_save_src, TzramSaveSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Encrypt tzram using our random key. */ + se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize); + hw::FlushDataCache(tzram_save_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Copy the data from work space to the secure storage destination. */ + void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer(); + std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize); + hw::FlushDataCache(tzram_store_dst, TzramSaveSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Compute cmac of tzram into our temporary block. */ + se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize); + + /* Ensure that the cmac registers are not locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked); + + /* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite); + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked); + + /* Verify the key source is correct in registers, and read-lock the key source registers. */ + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]); + AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]); + pmc::LockSecureRegister(pmc::SecureRegister_CmacRead); + + /* Ensure that the key source registers are locked. */ + AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked); } void SaveSecureContextForMariko() { diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp index e7c7074f8..ead3d4124 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -21,7 +21,8 @@ namespace ams::pkg1 { enum AesKeySlot { AesKeySlot_UserStart = 0, - AesKeySlot_TzramSave = 2, + AesKeySlot_TzramSaveKek = 2, + AesKeySlot_TzramSaveKey = 3, AesKeySlot_UserLast = 5, AesKeySlot_UserEnd = AesKeySlot_UserLast + 1, diff --git a/libraries/libexosphere/include/exosphere/pmc.hpp b/libraries/libexosphere/include/exosphere/pmc.hpp index 207307ebc..d8034a146 100644 --- a/libraries/libexosphere/include/exosphere/pmc.hpp +++ b/libraries/libexosphere/include/exosphere/pmc.hpp @@ -42,8 +42,8 @@ namespace ams::pmc { void LockSecureRegister(SecureRegister reg); enum class LockState { - Locked = 0, - NotLocked = 1, + Locked = 0, + NotLocked = 1, PartiallyLocked = 2, }; diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index 942f9155d..bd8c46ced 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -38,6 +38,9 @@ namespace ams::se { void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void EncryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void EncryptAes256Cbc(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/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp index 05369d1fa..41469f594 100644 --- a/libraries/libexosphere/include/exosphere/se/se_management.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -30,6 +30,7 @@ namespace ams::se { void HandleInterrupt(); + void ValidateErrStatus(); void ValidateAesOperationResult(); -} \ No newline at end of file +} diff --git a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp index 9f16924b7..33cc7a15a 100644 --- a/libraries/libexosphere/include/exosphere/se/se_suspend.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_suspend.hpp @@ -51,5 +51,6 @@ namespace ams::se { static_assert(util::is_pod::value); bool ValidateStickyBits(const StickyBits &bits); + void SaveContext(Context *dst); -} \ No newline at end of file +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index 8bc203ff3..eebdcc400 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp @@ -61,7 +61,7 @@ DEFINE_FLOW_REG_BIT_ENUM(HALT_CPUN_EVENTS_LIC_IRQN, 11, DISABLE, ENABLE); DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_CPUN_EVENTS_FLOW_MODE, 29, NONE, RUN_AND_INT, WAITEVENT, WAITEVENT_AND_INT, STOP_UNTIL_IRQ, STOP_UNTIL_EVENT_AND_IRQ, RESERVED6, RESERVED7); -DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, ENABLED, DISABLED); +DEFINE_FLOW_REG_BIT_ENUM(HALT_COP_EVENTS_JTAG, 28, DISABLED, ENABLED); DEFINE_FLOW_REG_THREE_BIT_ENUM(HALT_COP_EVENTS_MODE, 29, FLOW_MODE_NONE, FLOW_MODE_RUN_AND_INT, FLOW_MODE_STOP, FLOW_MODE_STOP_AND_INT, FLOW_MODE_STOP_UNTIL_IRQ, FLOW_MODE_STOP_UNTIL_IRQ_AND_INT, FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ, RESERVED7); DEFINE_FLOW_REG_BIT_ENUM(FLOW_DBG_QUAL_FIQ2CCPLEX_ENABLE, 28, DISABLE, ENABLE); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index c6ede5f01..98d3743d8 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -185,6 +185,8 @@ DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_AUD, 27, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_DFD, 28, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON); +DEFINE_PMC_REG(PWRGATE_STATUS_CE123, 9, 3); + DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1); DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index c46e02171..5ded61a13 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -277,6 +277,37 @@ namespace ams::se { GetCmacResult(SE, dst, dst_size); } + void EncryptAesCbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size, AesMode mode) { + /* If nothing to encrypt, succeed. */ + if (src_size == 0) { return; } + + /* Validate input. */ + AMS_ABORT_UNLESS(iv_size == AesBlockSize); + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine extents. */ + const size_t num_blocks = src_size / AesBlockSize; + const size_t aligned_size = num_blocks * AesBlockSize; + AMS_ABORT_UNLESS(src_size == aligned_size); + + /* Configure for aes-cbc encryption. */ + SetConfig(SE, true, SE_CONFIG_DST_MEMORY); + SetAesConfig(SE, slot, true, AesConfigCbcEncrypt); + UpdateAesMode(SE, mode); + + /* Set the iv. */ + SetAesKeyIv(SE, slot, iv, iv_size); + + /* Set the block count. */ + SetBlockCount(SE, num_blocks); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_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; } @@ -448,6 +479,14 @@ namespace ams::se { return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes256); } + void EncryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes128); + } + + void EncryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) { + return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256); + } + 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); diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp index 2a5e70352..f66360a88 100644 --- a/libraries/libexosphere/source/se/se_registers.hpp +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -158,6 +158,16 @@ namespace ams::se { SE_CONFIG_ENC_MODE_SHA512 = 7, }; + /* SE_CTX_SAVE_CONFIG */ + DEFINE_SE_REG_TWO_BIT_ENUM(CTX_SAVE_CONFIG_AES_WORD_QUAD, 0, KEYS_0_3, KEYS_4_7, ORIGINAL_IVS, UPDATED_IVS); + DEFINE_SE_REG(CTX_SAVE_CONFIG_PKA1_WORD_QUAD_L, 0, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_AES_KEY_INDEX, 8, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_RSA_WORD_QUAD, 12, 4); + DEFINE_SE_REG(CTX_SAVE_CONFIG_PKA1_WORD_QUAD_H, 12, 4); + DEFINE_SE_REG_TWO_BIT_ENUM(CTX_SAVE_CONFIG_RSA_KEY_INDEX, 16, SLOT0_EXPONENT, SLOT0_MODULUS, SLOT1_EXPONENT, SLOT1_MODULUS); + DEFINE_SE_REG_BIT_ENUM(CTX_SAVE_CONFIG_STICKY_WORD_QUAD, 24, WORDS_0_3, WORDS_4_7); + DEFINE_SE_REG_THREE_BIT_ENUM(CTX_SAVE_CONFIG_SRC, 29, STICKY_BITS, RSA_KEYTABLE, AES_KEYTABLE, PKA1_STICKY_BITS, MEM, RESERVED5, SRK, PKA1_KEYTABLE); + /* SE_SHA_CONFIG */ DEFINE_SE_REG(SHA_CONFIG_HW_INIT_HASH, 0, 1); diff --git a/libraries/libexosphere/source/se/se_suspend.cpp b/libraries/libexosphere/source/se/se_suspend.cpp index ca5134a35..98d49c958 100644 --- a/libraries/libexosphere/source/se/se_suspend.cpp +++ b/libraries/libexosphere/source/se/se_suspend.cpp @@ -20,10 +20,50 @@ namespace ams::se { namespace { + constinit const u8 FixedPattern[AesBlockSize] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F + }; + bool TestRegister(volatile u32 &r, u16 v) { return (static_cast(reg::Read(r))) == v; } + void ExecuteContextSaveOperation(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Save the output to a temporary buffer. */ + util::AlignedBuffer temp; + AMS_ABORT_UNLESS(dst_size <= AesBlockSize); + + /* Ensure that the cpu and SE see consistent data. */ + if (src_size > 0) { + hw::FlushDataCache(src, src_size); + hw::DataSynchronizationBarrierInnerShareable(); + } + if (dst_size > 0) { + hw::FlushDataCache(temp, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + } + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_CTX_SAVE, dst, dst_size, src, src_size); + + /* Copy output from the operation, if any. */ + if (dst_size > 0) { + hw::DataSynchronizationBarrierInnerShareable(); + hw::FlushDataCache(temp, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + std::memcpy(dst, temp, dst_size); + } + } + + void SaveContextBlock(volatile SecurityEngineRegisters *SE, void *dst) { + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst, AesBlockSize, nullptr, 0); + } + } bool ValidateStickyBits(const StickyBits &bits) { @@ -56,4 +96,156 @@ namespace ams::se { return true; } + void SaveContext(Context *dst) { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Generate a random srk. */ + GenerateSrk(); + + /* Save a randomly-generated block. */ + { + util::AlignedBuffer random_block; + + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Generate random bytes. */ + GenerateRandomBytes(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(random_block, AesBlockSize); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure to encrypt the random block to memory. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, AES_ENC), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, MEMORY)); + + /* Configure to context save using memory as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, MEM)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst->random, AesBlockSize, random_block, AesBlockSize); + } + + /* Save the sticky bits. */ + for (size_t i = 0; i < util::size(dst->sticky_bits); ++i) { + /* Configure to encrypt the sticky bits block. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, STICKY_BITS), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_STICKY_WORD_QUAD, i)); + + /* Save the block. */ + SaveContextBlock(SE, dst->sticky_bits[i]); + } + + /* Save the aes keytable. */ + { + for (size_t key = 0; key < util::size(dst->aes_key); ++key) { + for (auto part = 0; part < AesKeySlotPartCount; ++part) { + /* Configure to encrypt the part of the key. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_WORD_QUAD, part)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_key[key][part]); + } + } + + for (size_t key = 0; key < util::size(dst->aes_oiv); ++key) { + /* Configure to encrypt the original iv. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_AES_WORD_QUAD, ORIGINAL_IVS)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_oiv[key]); + } + + for (size_t key = 0; key < util::size(dst->aes_uiv); ++key) { + /* Configure to encrypt the updated iv. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, AES_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_AES_KEY_INDEX, key), + SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_AES_WORD_QUAD, UPDATED_IVS)); + + /* Save the block. */ + SaveContextBlock(SE, dst->aes_uiv[key]); + } + } + + /* Save the rsa keytable. */ + for (size_t key = 0; key < util::size(dst->rsa_key); ++key) { + for (auto part = 0; part < RsaKeySlotPartCount; ++part) { + /* Note that the parts are done in reverse order. */ + const auto part_index = RsaKeySlotPartCount - 1 - part; + + /* Determine a total key index. */ + const auto key_index = key * util::size(dst->rsa_key) + part_index; + + for (size_t block = 0; block < RsaSize / AesBlockSize; ++block) { + /* Configure to encrypt the part of the key. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM (CTX_SAVE_CONFIG_SRC, RSA_KEYTABLE), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_RSA_KEY_INDEX, key_index), + SE_REG_BITS_VALUE(CTX_SAVE_CONFIG_RSA_WORD_QUAD, block)); + + /* Save the block. */ + SaveContextBlock(SE, dst->rsa_key[key][part][block]); + } + } + } + + /* Save the fixed pattern. */ + { + /* Configure to context save using memory as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, MEM)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, dst->fixed_pattern, AesBlockSize, FixedPattern, AesBlockSize); + } + + /* Save the srk. */ + { + /* Configure to context save using srk as source. */ + reg::Write(SE->SE_CTX_SAVE_CONFIG, SE_REG_BITS_ENUM(CTX_SAVE_CONFIG_SRC, SRK)); + + /* Configure to encrypt a single block. */ + reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, nullptr, 0, nullptr, 0); + } + + /* Perform a no-op context save operation. */ + { + /* Configure to perform no-op. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP)); + + /* Execute the operation. */ + ExecuteContextSaveOperation(SE, nullptr, 0, nullptr, 0); + } + } + + void ValidateErrStatus() { + /* Get the registers. */ + auto *SE = GetRegisters(); + + /* Ensure there is no error status. */ + AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0); + + /* Ensure no error occurred. */ + AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR))); + } + }