diff --git a/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp index e7c7074f..ead3d412 100644 --- a/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/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/libexosphere/include/exosphere/pmc.hpp b/libexosphere/include/exosphere/pmc.hpp index 207307eb..d8034a14 100644 --- a/libexosphere/include/exosphere/pmc.hpp +++ b/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/libexosphere/include/exosphere/se/se_aes.hpp b/libexosphere/include/exosphere/se/se_aes.hpp index 942f9155..bd8c46ce 100644 --- a/libexosphere/include/exosphere/se/se_aes.hpp +++ b/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/libexosphere/include/exosphere/se/se_management.hpp b/libexosphere/include/exosphere/se/se_management.hpp index 05369d1f..41469f59 100644 --- a/libexosphere/include/exosphere/se/se_management.hpp +++ b/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/libexosphere/include/exosphere/se/se_suspend.hpp b/libexosphere/include/exosphere/se/se_suspend.hpp index 9f16924b..33cc7a15 100644 --- a/libexosphere/include/exosphere/se/se_suspend.hpp +++ b/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/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp index 8bc203ff..eebdcc40 100644 --- a/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/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/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index c6ede5f0..98d3743d 100644 --- a/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/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/libexosphere/source/se/se_aes.cpp b/libexosphere/source/se/se_aes.cpp index c46e0217..5ded61a1 100644 --- a/libexosphere/source/se/se_aes.cpp +++ b/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/libexosphere/source/se/se_registers.hpp b/libexosphere/source/se/se_registers.hpp index 2a5e7035..f66360a8 100644 --- a/libexosphere/source/se/se_registers.hpp +++ b/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/libexosphere/source/se/se_suspend.cpp b/libexosphere/source/se/se_suspend.cpp index ca5134a3..98d49c95 100644 --- a/libexosphere/source/se/se_suspend.cpp +++ b/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))); + } + }