From e0d08dd6dec91a250cec695f045aeef7d6a962c7 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 27 Aug 2021 23:20:46 -0700 Subject: [PATCH] fusee_cpp: implement tsec_keygen firmware execution --- libexosphere/arm.mk | 2 +- libexosphere/source/kfuse/kfuse_registers.hpp | 32 ++++ libexosphere/source/tsec/tsec_api.cpp | 156 +++++++++++++++--- libexosphere/source/tsec/tsec_registers.hpp | 49 ++++++ 4 files changed, 214 insertions(+), 25 deletions(-) create mode 100644 libexosphere/source/kfuse/kfuse_registers.hpp create mode 100644 libexosphere/source/tsec/tsec_registers.hpp diff --git a/libexosphere/arm.mk b/libexosphere/arm.mk index 6a42c37c..0414f3de 100644 --- a/libexosphere/arm.mk +++ b/libexosphere/arm.mk @@ -149,7 +149,7 @@ $(OFILES) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) libc.o: CFLAGS += -fno-builtin -fno-lto -libgcc_division.arch.arm.o: CFLAGS += -fno-builtin -fno-lto +util_api.o: CXXFLAGS += -fno-lto #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin diff --git a/libexosphere/source/kfuse/kfuse_registers.hpp b/libexosphere/source/kfuse/kfuse_registers.hpp new file mode 100644 index 00000000..4e6f1c6f --- /dev/null +++ b/libexosphere/source/kfuse/kfuse_registers.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public LicenKFUSE, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be uKFUSEful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOKFUSE. KFUSEe the GNU General Public LicenKFUSE for + * more details. + * + * You should have received a copy of the GNU General Public LicenKFUSE + * along with this program. If not, KFUSEe . + */ +#include + +#define KFUSE_STATE (0x080) + +#define KFUSE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (KFUSE, NAME) +#define KFUSE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (KFUSE, NAME, VALUE) +#define KFUSE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (KFUSE, NAME, ENUM) +#define KFUSE_REG_BITS_ENUM_KFUSEL(NAME, __COND__, TRUE_ENUM, FALKFUSE_ENUM) REG_NAMED_BITS_ENUM_KFUSEL(KFUSE, NAME, __COND__, TRUE_ENUM, FALKFUSE_ENUM) + +#define DEFINE_KFUSE_REG(NAME, __OFFKFUSET__, __WIDTH__) REG_DEFINE_NAMED_REG (KFUSE, NAME, __OFFKFUSET__, __WIDTH__) +#define DEFINE_KFUSE_REG_BIT_ENUM(NAME, __OFFKFUSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (KFUSE, NAME, __OFFKFUSET__, ZERO, ONE) +#define DEFINE_KFUSE_REG_TWO_BIT_ENUM(NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (KFUSE, NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_KFUSE_REG_THREE_BIT_ENUM(NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, KFUSEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(KFUSE, NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, KFUSEVEN) +#define DEFINE_KFUSE_REG_FOUR_BIT_ENUM(NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, KFUSEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (KFUSE, NAME, __OFFKFUSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, KFUSEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_KFUSE_REG_BIT_ENUM(STATE_DONE, 16, NOT_DONE, DONE); +DEFINE_KFUSE_REG_BIT_ENUM(STATE_CRCPASS, 17, FAIL, PASS); diff --git a/libexosphere/source/tsec/tsec_api.cpp b/libexosphere/source/tsec/tsec_api.cpp index a251b91f..56f6a708 100644 --- a/libexosphere/source/tsec/tsec_api.cpp +++ b/libexosphere/source/tsec/tsec_api.cpp @@ -14,46 +14,154 @@ * along with this program. If not, see . */ #include +#include "tsec_registers.hpp" +#include "../kfuse/kfuse_registers.hpp" namespace ams::tsec { namespace { - enum TsecResult { + constexpr inline const uintptr_t KFUSE = 0x7000FC00; + constexpr inline const uintptr_t TSEC = 0x54500000; + + enum TsecResult : u32 { TsecResult_Success = 0xB0B0B0B0, TsecResult_Failure = 0xD0D0D0D0, }; - bool RunFirmwareImpl(const void *fw, size_t fw_size) { - /* Enable relevant clocks. */ - clkrst::EnableHost1xClock(); - clkrst::EnableTsecClock(); - clkrst::EnableSorSafeClock(); - clkrst::EnableSor0Clock(); - clkrst::EnableSor1Clock(); - clkrst::EnableKfuseClock(); + enum TsecMemory { + TsecMemory_Imem, + TsecMemory_Dmem, + }; - /* Disable clocks once we're done. */ - ON_SCOPE_EXIT { - clkrst::DisableHost1xClock(); - clkrst::DisableTsecClock(); - clkrst::DisableSorSafeClock(); - clkrst::DisableSor0Clock(); - clkrst::DisableSor1Clock(); - clkrst::DisableKfuseClock(); - }; + bool WaitForKfuseReady() { + constexpr auto KfuseTimeout = 10 * 1000; /* 10 ms. */ - /* TODO */ - AMS_UNUSED(fw, fw_size); - return true; + const u32 end_time = util::GetMicroSeconds() + KfuseTimeout; + + /* Wait for STATE_DONE. */ + while (!reg::HasValue(KFUSE + KFUSE_STATE, KFUSE_REG_BITS_ENUM(STATE_DONE, DONE))) { + if (util::GetMicroSeconds() >= end_time) { + return false; + } + } + + /* Check for STATE_CRCPASS. */ + return reg::HasValue(KFUSE + KFUSE_STATE, KFUSE_REG_BITS_ENUM(STATE_CRCPASS, PASS)); + } + + void WaitForDmaIdle() { + constexpr auto DmaTimeout = 10 * 1000 * 1000; /* 10 Seconds. */ + + u32 cur_time = util::GetMicroSeconds(); + const u32 end_time = cur_time + DmaTimeout; + + while (cur_time <= end_time) { + if (reg::HasValue(TSEC + TSEC_FALCON_DMATRFCMD, TSEC_REG_BITS_ENUM(FALCON_DMATRFCMD_BUSY, IDLE))) { + return; + } + + cur_time = util::GetMicroSeconds(); + } + + AMS_ABORT("tsec dma timeout"); + } + + void WaitForTsecIdle() { + constexpr auto TsecTimeout = 2 * 1000 * 1000; /* 2 Seconds. */ + + u32 cur_time = util::GetMicroSeconds(); + const u32 end_time = cur_time + TsecTimeout; + + while (cur_time <= end_time) { + if (reg::HasValue(TSEC + TSEC_FALCON_CPUCTL, TSEC_REG_BITS_ENUM(FALCON_CPUCTL_HALTED, TRUE))) { + return; + } + + cur_time = util::GetMicroSeconds(); + } + + AMS_ABORT("tsec timeout"); + } + + void DoDma256(TsecMemory memory, u32 dst_offset, u32 src_offset) { + reg::Write(TSEC + TSEC_FALCON_DMATRFMOFFS, TSEC_REG_BITS_VALUE(FALCON_DMATRFMOFFS_OFFSET, dst_offset)); + reg::Write(TSEC + TSEC_FALCON_DMATRFFBOFFS, src_offset); + + if (memory == TsecMemory_Imem) { + reg::Write(TSEC + TSEC_FALCON_DMATRFCMD, TSEC_REG_BITS_ENUM(FALCON_DMATRFCMD_TO, IMEM), + TSEC_REG_BITS_ENUM(FALCON_DMATRFCMD_SIZE, 4B)); + } else { + reg::Write(TSEC + TSEC_FALCON_DMATRFCMD, TSEC_REG_BITS_ENUM(FALCON_DMATRFCMD_TO, DMEM), + TSEC_REG_BITS_ENUM(FALCON_DMATRFCMD_SIZE, 256B)); + } + + WaitForDmaIdle(); } } bool RunTsecFirmware(const void *fw, size_t fw_size) { - /* TODO */ - AMS_UNUSED(fw, fw_size); - return RunFirmwareImpl(fw, fw_size); + /* Enable relevant clocks. */ + clkrst::EnableHost1xClock(); + clkrst::EnableTsecClock(); + clkrst::EnableSorSafeClock(); + clkrst::EnableSor0Clock(); + clkrst::EnableSor1Clock(); + clkrst::EnableKfuseClock(); + + /* Disable clocks once we're done. */ + ON_SCOPE_EXIT { + clkrst::DisableHost1xClock(); + clkrst::DisableTsecClock(); + clkrst::DisableSorSafeClock(); + clkrst::DisableSor0Clock(); + clkrst::DisableSor1Clock(); + clkrst::DisableKfuseClock(); + }; + + /* Wait for kfuse to be ready. */ + if (!WaitForKfuseReady()) { + return false; + } + + /* Configure falcon. */ + reg::Write(TSEC + TSEC_FALCON_DMACTL, 0); + reg::Write(TSEC + TSEC_FALCON_IRQMSET, 0xFFF2); + reg::Write(TSEC + TSEC_FALCON_IRQDEST, 0xFFF0); + reg::Write(TSEC + TSEC_FALCON_ITFEN, 0x3); + + /* Wait for TSEC dma to be idle. */ + WaitForDmaIdle(); + + /* Set the base address for transfers. */ + reg::Write(TSEC + TSEC_FALCON_DMATRFBASE, reinterpret_cast(fw) >> 8); + + /* Transfer all data to TSEC imem. */ + for (size_t i = 0; i < fw_size; i += 0x100) { + DoDma256(TsecMemory_Imem, i, i); + } + + /* Write the magic value to host1x syncpoint 160. */ + reg::Write(0x50003300, 0x34C2E1DA); + + /* Execute the firmware. */ + reg::Write(TSEC + TSEC_FALCON_MAILBOX0, 0); + reg::Write(TSEC + TSEC_FALCON_MAILBOX1, 0); + reg::Write(TSEC + TSEC_FALCON_BOOTVEC, 0); + reg::Write(TSEC + TSEC_FALCON_CPUCTL, TSEC_REG_BITS_ENUM(FALCON_CPUCTL_STARTCPU, TRUE)); + + /* Wait for TSEC dma to be idle. */ + WaitForDmaIdle(); + + /* Wait for TSEC to complete. */ + WaitForTsecIdle(); + + /* Clear magic value from host1x syncpoint 160. */ + reg::Write(0x50003300, 0); + + /* Return whether the tsec firmware succeeded. */ + return reg::Read(TSEC + TSEC_FALCON_MAILBOX1) == TsecResult_Success; } void Lock() { diff --git a/libexosphere/source/tsec/tsec_registers.hpp b/libexosphere/source/tsec/tsec_registers.hpp new file mode 100644 index 00000000..fb8e758a --- /dev/null +++ b/libexosphere/source/tsec/tsec_registers.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public LicenTSEC, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be uTSECful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOTSEC. TSECe the GNU General Public LicenTSEC for + * more details. + * + * You should have received a copy of the GNU General Public LicenTSEC + * along with this program. If not, TSECe . + */ +#include + +#define TSEC_FALCON_IRQMSET (0x1010) +#define TSEC_FALCON_IRQDEST (0x101C) +#define TSEC_FALCON_MAILBOX0 (0x1040) +#define TSEC_FALCON_MAILBOX1 (0x1044) +#define TSEC_FALCON_ITFEN (0x1048) +#define TSEC_FALCON_CPUCTL (0x1100) +#define TSEC_FALCON_BOOTVEC (0x1104) +#define TSEC_FALCON_DMACTL (0x110C) +#define TSEC_FALCON_DMATRFBASE (0x1110) +#define TSEC_FALCON_DMATRFMOFFS (0x1114) +#define TSEC_FALCON_DMATRFCMD (0x1118) +#define TSEC_FALCON_DMATRFFBOFFS (0x111C) + +#define TSEC_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (TSEC, NAME) +#define TSEC_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (TSEC, NAME, VALUE) +#define TSEC_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (TSEC, NAME, ENUM) +#define TSEC_REG_BITS_ENUM_TSECL(NAME, __COND__, TRUE_ENUM, FALTSEC_ENUM) REG_NAMED_BITS_ENUM_TSECL(TSEC, NAME, __COND__, TRUE_ENUM, FALTSEC_ENUM) + +#define DEFINE_TSEC_REG(NAME, __OFFTSECT__, __WIDTH__) REG_DEFINE_NAMED_REG (TSEC, NAME, __OFFTSECT__, __WIDTH__) +#define DEFINE_TSEC_REG_BIT_ENUM(NAME, __OFFTSECT__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (TSEC, NAME, __OFFTSECT__, ZERO, ONE) +#define DEFINE_TSEC_REG_TWO_BIT_ENUM(NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (TSEC, NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE) +#define DEFINE_TSEC_REG_THREE_BIT_ENUM(NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, TSECVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(TSEC, NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, TSECVEN) +#define DEFINE_TSEC_REG_FOUR_BIT_ENUM(NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, TSECVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (TSEC, NAME, __OFFTSECT__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, TSECVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_TSEC_REG_BIT_ENUM(FALCON_CPUCTL_STARTCPU, 1, FALSE, TRUE); +DEFINE_TSEC_REG_BIT_ENUM(FALCON_CPUCTL_HALTED, 4, FALSE, TRUE); + +DEFINE_TSEC_REG(FALCON_DMATRFMOFFS_OFFSET, 0, 16); + +DEFINE_TSEC_REG_BIT_ENUM(FALCON_DMATRFCMD_BUSY, 1, BUSY, IDLE); +DEFINE_TSEC_REG_BIT_ENUM(FALCON_DMATRFCMD_TO, 4, DMEM, IMEM); +DEFINE_TSEC_REG_THREE_BIT_ENUM(FALCON_DMATRFCMD_SIZE, 8, 4B, 8B, 16B, 32B, 64B, 128B, 256B, RSVD7); \ No newline at end of file