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