exo2: implement SmcComputeAes, SmcGetResult, SmcGetResultData

This commit is contained in:
Michael Scire 2020-05-15 14:58:45 -07:00 committed by SciresM
parent b6b114ec40
commit e0dbfc69a8
16 changed files with 486 additions and 24 deletions

View file

@ -206,6 +206,10 @@ namespace ams::gic {
ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast<u32>(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);
}

View file

@ -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<const u32 *>(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<const u32 *>(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);
}
}

View file

@ -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());
}
}

View file

@ -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);
}

View file

@ -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));
}
}