diff --git a/exosphere/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp
index d025d5675..40d851dd9 100644
--- a/exosphere/program/source/secmon_error.cpp
+++ b/exosphere/program/source/secmon_error.cpp
@@ -21,17 +21,6 @@ namespace ams {
namespace {
constexpr bool SaveSystemStateForDebug = false;
- constexpr bool LogSystemStateForDebug = false;
-
- void LogU64(u64 value) {
- char buffer[2 * sizeof(value)];
- for (size_t i = 0; i < sizeof(value); ++i) {
- buffer[sizeof(buffer) - 1 - (2 * i) - 0] = "0123456789ABCDEF"[(value >> 0) & 0xF];
- buffer[sizeof(buffer) - 1 - (2 * i) - 1] = "0123456789ABCDEF"[(value >> 4) & 0xF];
- value >>= 8;
- }
- log::SendText(buffer, sizeof(buffer));
- }
}
}
@@ -114,57 +103,6 @@ namespace ams::secmon {
util::WaitMicroSeconds(1000);
}
- ALWAYS_INLINE void LogSystemStateForDebugErrorReboot(u64 lr, u64 sp) {
- log::SendText("*** Error Reboot ***\n", 21);
- log::Flush();
-
- u64 temp_reg;
-
- __asm__ __volatile__("mrs %0, esr_el3" : "=r"(temp_reg) :: "memory");
- log::SendText("ESR_EL3: ", 9);
- LogU64(temp_reg);
- log::SendText("\n", 1);
- log::Flush();
-
- __asm__ __volatile__("mrs %0, elr_el3" : "=r"(temp_reg) :: "memory");
- log::SendText("ELR_EL3: ", 9);
- LogU64(temp_reg);
- log::SendText("\n", 1);
- log::Flush();
-
- __asm__ __volatile__("mrs %0, far_el3" : "=r"(temp_reg) :: "memory");
- log::SendText("FAR_EL3: ", 9);
- LogU64(temp_reg);
- log::SendText("\n", 1);
- log::Flush();
-
- log::SendText("LR: ", 9);
- LogU64(lr);
- log::SendText("\n", 1);
- log::Flush();
-
- log::SendText("SP: ", 9);
- LogU64(sp);
- log::SendText("\n", 1);
- log::Flush();
-
- log::SendText("Stack:\n", 7);
- log::Flush();
-
- char buf[2];
- for (int i = 0; i < 0x100; ++i) {
- const u8 byte = *(volatile u8 *)(sp + i);
- buf[0] = "0123456789ABCDEF"[(byte >> 4) & 0xF];
- buf[1] = "0123456789ABCDEF"[(byte >> 0) & 0xF];
- log::SendText(buf, 2);
- log::Flush();
- if (util::IsAligned(i + 1, 0x10)) {
- log::SendText("\n", 1);
- log::Flush();
- }
- }
- }
-
}
void SetError(pkg1::ErrorInfo info) {
@@ -181,14 +119,6 @@ namespace ams::secmon {
SaveSystemStateForDebugErrorReboot();
}
- if constexpr (LogSystemStateForDebug) {
- u64 lr, sp;
- __asm__ __volatile__("mov %0, lr" : "=r"(lr) :: "memory");
- __asm__ __volatile__("mov %0, sp" : "=r"(sp) :: "memory");
-
- LogSystemStateForDebugErrorReboot(lr, sp);
- }
-
/* Lockout the security engine. */
se::Lockout();
diff --git a/exosphere/program/source/secmon_exception_vectors.s b/exosphere/program/source/secmon_exception_vectors.s
index 88d7fe8a5..cb83adcfc 100644
--- a/exosphere/program/source/secmon_exception_vectors.s
+++ b/exosphere/program/source/secmon_exception_vectors.s
@@ -163,22 +163,22 @@ vector_entry irq_a64
check_vector_size irq_a64
vector_entry fiq_a64
- /* Save X29, X30. */
+ /* Save x18, x26-x30. */
stp x29, x30, [sp, #-0x10]!
-
- /* Get the current core ID, ensure it's core 3. */
- mrs x29, mpidr_el1
- and x29, x29, #3
- cmp x29, #3
- b.ne _ZN3ams6secmon26UnexpectedExceptionHandlerEv
-
- /* Save x26-x28, x18. */
stp x28, x18, [sp, #-0x10]!
stp x26, x27, [sp, #-0x10]!
/* Set x18 to the global data region. */
ldr x18, =0x1F01FA000
+ /* Get the current core. */
+ mrs x29, mpidr_el1
+ and x29, x29, #3
+
+ /* If we're not on core 3, take the core0-2 handler. */
+ cmp x29, #3
+ b.ne _ZN3ams6secmon25HandleFiqExceptionCore012Ev
+
/* Handle the fiq exception. */
bl _ZN3ams6secmon18HandleFiqExceptionEv
@@ -326,7 +326,41 @@ _ZN3ams6secmon18HandleFiqExceptionEv:
vector_entry serror_a32
/* An unexpected exception was taken. */
b _ZN3ams6secmon26UnexpectedExceptionHandlerEv
- check_vector_size serror_a32
+ .endfunc
+ .cfi_endproc
+_ZN3ams6secmon25HandleFiqExceptionCore012Ev:
+ /* Acquire exclusive access to the common smc stack. */
+ stp x4, x5, [sp, #-0x10]!
+ stp x2, x3, [sp, #-0x10]!
+ stp x0, x1, [sp, #-0x10]!
+ bl _ZN3ams6secmon25AcquireCommonSmcStackLockEv
+ ldp x0, x1, [sp], #0x10
+ ldp x2, x3, [sp], #0x10
+ ldp x4, x5, [sp], #0x10
+
+ /* Pivot to use the common smc stack. */
+ mov x30, sp
+ ldr x29, =0x1F01F6E80
+ mov sp, x29
+ stp x29, x30, [sp, #-0x10]!
+
+ /* Handle the fiq exception. */
+ bl _ZN3ams6secmon18HandleFiqExceptionEv
+
+ /* Restore our core-specific stack. */
+ ldp x29, x30, [sp], #0x10
+ mov sp, x30
+
+ /* Release our exclusive access to the common smc stack. */
+ stp x0, x1, [sp, #-0x10]!
+ bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
+ ldp x0, x1, [sp], #0x10
+
+ /* Return. */
+ ldp x26, x27, [sp], #0x10
+ ldp x28, x18, [sp], #0x10
+ ldp x29, x30, [sp], #0x10
+ eret
/* Instantiate the literal pool for the exception vectors. */
.ltorg
diff --git a/exosphere/program/source/secmon_interrupt_handler.cpp b/exosphere/program/source/secmon_interrupt_handler.cpp
index 41f466632..7f66e2f41 100644
--- a/exosphere/program/source/secmon_interrupt_handler.cpp
+++ b/exosphere/program/source/secmon_interrupt_handler.cpp
@@ -25,14 +25,16 @@ namespace ams::secmon {
constinit InterruptHandler g_handlers[InterruptHandlersMax] = {};
constinit int g_interrupt_ids[InterruptHandlersMax] = {};
+ constinit u8 g_interrupt_core_masks[InterruptHandlersMax] = {};
}
- void SetInterruptHandler(int interrupt_id, InterruptHandler handler) {
+ void SetInterruptHandler(int interrupt_id, u8 core_mask, InterruptHandler handler) {
for (int i = 0; i < InterruptHandlersMax; ++i) {
if (g_interrupt_ids[i] == 0) {
- g_interrupt_ids[i] = interrupt_id;
- g_handlers[i] = handler;
+ g_interrupt_ids[i] = interrupt_id;
+ g_handlers[i] = handler;
+ g_interrupt_core_masks[i] = core_mask;
return;
}
}
@@ -51,6 +53,9 @@ namespace ams::secmon {
/* Check each handler. */
for (int i = 0; i < InterruptHandlersMax; ++i) {
if (g_interrupt_ids[i] == interrupt_id) {
+ /* Validate that we can invoke the handler. */
+ AMS_ABORT_UNLESS((g_interrupt_core_masks[i] & (1u << hw::GetCurrentCoreId())) != 0);
+
/* Invoke the handler. */
g_handlers[i]();
gic::SetEndOfInterrupt(interrupt_id);
diff --git a/exosphere/program/source/secmon_interrupt_handler.hpp b/exosphere/program/source/secmon_interrupt_handler.hpp
index 136da0264..bf63ae75c 100644
--- a/exosphere/program/source/secmon_interrupt_handler.hpp
+++ b/exosphere/program/source/secmon_interrupt_handler.hpp
@@ -20,6 +20,6 @@ namespace ams::secmon {
using InterruptHandler = void (*)();
- void SetInterruptHandler(int interrupt_id, InterruptHandler handler);
+ void SetInterruptHandler(int interrupt_id, u8 core_mask, InterruptHandler handler);
}
diff --git a/exosphere/program/source/secmon_mariko_fatal_error.cpp b/exosphere/program/source/secmon_mariko_fatal_error.cpp
new file mode 100644
index 000000000..e4cd4b3ae
--- /dev/null
+++ b/exosphere/program/source/secmon_mariko_fatal_error.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "secmon_cpu_context.hpp"
+#include "secmon_map.hpp"
+#include "secmon_page_mapper.hpp"
+#include "secmon_mariko_fatal_error.hpp"
+#include "smc/secmon_smc_power_management.hpp"
+
+namespace ams::secmon {
+
+ namespace {
+
+ constinit u8 g_fatal_error_mask = 0;
+
+ }
+
+ void HandleMarikoFatalErrorInterrupt() {
+ /* This interrupt handler doesn't return, so mark that we're at end of interrupt. */
+ gic::SetEndOfInterrupt(MarikoFatalErrorInterruptId);
+
+ /* Get the current core id. */
+ const auto core_id = hw::GetCurrentCoreId();
+
+ /* Set that we received the fatal on the current core. */
+ g_fatal_error_mask |= (1u << core_id);
+ hw::FlushDataCache(std::addressof(g_fatal_error_mask), sizeof(g_fatal_error_mask));
+ hw::DataSynchronizationBarrier();
+
+ /* If not all cores have received the fatal, we need to trigger the interrupt on other cores. */
+ if (g_fatal_error_mask != (1u << NumCores) - 1) {
+
+ /* Configure and send the interrupt to the next core. */
+ const auto next_core = __builtin_ctz(~g_fatal_error_mask);
+ gic::SetSpiTargetCpu(MarikoFatalErrorInterruptId, (1u << next_core));
+ gic::SetPending(MarikoFatalErrorInterruptId);
+ }
+
+ /* If current core is not 3, kill ourselves. */
+ if (core_id != NumCores - 1) {
+ smc::PowerOffCpu();
+ } else {
+ /* Wait for all cores to kill themselves. */
+ while (g_fatal_error_mask != (1u << NumCores) - 1) {
+ util::WaitMicroSeconds(100);
+ }
+ }
+
+ /* Copy the fatal error context to mariko tzram. */
+ {
+ /* Map the iram page. */
+ constexpr uintptr_t FatalErrorPhysicalAddress = MemoryRegionPhysicalIramFatalErrorContext.GetAddress();
+ AtmosphereIramPageMapper mapper(FatalErrorPhysicalAddress);
+ if (mapper.Map()) {
+ /* Copy the fatal error context. */
+ void *dst = MemoryRegionVirtualTzramMarikoProgramFatalErrorContext.GetPointer();
+ const void *src = mapper.GetPointerTo(FatalErrorPhysicalAddress, sizeof(ams::impl::FatalErrorContext));
+ std::memcpy(dst, src, sizeof(ams::impl::FatalErrorContext));
+ }
+ }
+
+ /* Map Dram for the mariko program. */
+ MapDramForMarikoProgram();
+
+ AMS_SECMON_LOG("%s\n", "Jumping to Mariko Fatal.");
+ AMS_LOG_FLUSH();
+
+ /* Jump to the mariko fatal program. */
+ reinterpret_cast(secmon::MemoryRegionVirtualTzramMarikoProgram.GetAddress())();
+
+ /* The mariko fatal program never returns. */
+ __builtin_unreachable();
+
+ AMS_INFINITE_LOOP();
+ }
+
+}
diff --git a/exosphere/program/source/secmon_mariko_fatal_error.hpp b/exosphere/program/source/secmon_mariko_fatal_error.hpp
new file mode 100644
index 000000000..dbe2bbde1
--- /dev/null
+++ b/exosphere/program/source/secmon_mariko_fatal_error.hpp
@@ -0,0 +1,25 @@
+/*
+ * 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 License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::secmon {
+
+ constexpr inline int MarikoFatalErrorInterruptId = 198;
+
+ NORETURN void HandleMarikoFatalErrorInterrupt();
+
+}
diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp
index 0136a72d8..bf47eec7d 100644
--- a/exosphere/program/source/secmon_setup.cpp
+++ b/exosphere/program/source/secmon_setup.cpp
@@ -18,6 +18,7 @@
#include "secmon_error.hpp"
#include "secmon_map.hpp"
#include "secmon_cpu_context.hpp"
+#include "secmon_mariko_fatal_error.hpp"
#include "secmon_interrupt_handler.hpp"
#include "secmon_misc.hpp"
#include "smc/secmon_random_cache.hpp"
@@ -1082,25 +1083,36 @@ namespace ams::secmon {
/* Setup the security engine interrupt. */
constexpr int SecurityEngineInterruptId = 90;
- gic::SetPriority (SecurityEngineInterruptId, gic::HighestPriority);
- gic::SetInterruptGroup(SecurityEngineInterruptId, 0);
- gic::SetEnable (SecurityEngineInterruptId, true);
- gic::SetSpiTargetCpu (SecurityEngineInterruptId, (1 << 3));
- gic::SetSpiMode (SecurityEngineInterruptId, gic::InterruptMode_Level);
+ constexpr u8 SecurityEngineInterruptCoreMask = (1 << 3);
+ gic::SetPriority (SecurityEngineInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(SecurityEngineInterruptId, 0);
+ gic::SetEnable (SecurityEngineInterruptId, true);
+ gic::SetSpiTargetCpu (SecurityEngineInterruptId, SecurityEngineInterruptCoreMask);
+ gic::SetSpiMode (SecurityEngineInterruptId, gic::InterruptMode_Level);
/* Setup the activity monitor interrupt. */
constexpr int ActivityMonitorInterruptId = 77;
- gic::SetPriority (ActivityMonitorInterruptId, gic::HighestPriority);
- gic::SetInterruptGroup(ActivityMonitorInterruptId, 0);
- gic::SetEnable (ActivityMonitorInterruptId, true);
- gic::SetSpiTargetCpu (ActivityMonitorInterruptId, (1 << 3));
- gic::SetSpiMode (ActivityMonitorInterruptId, gic::InterruptMode_Level);
+ constexpr u8 ActivityMonitorInterruptCoreMask = (1 << 3);
+ gic::SetPriority (ActivityMonitorInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(ActivityMonitorInterruptId, 0);
+ gic::SetEnable (ActivityMonitorInterruptId, true);
+ gic::SetSpiTargetCpu (ActivityMonitorInterruptId, ActivityMonitorInterruptCoreMask);
+ gic::SetSpiMode (ActivityMonitorInterruptId, gic::InterruptMode_Level);
+
+ /* Setup the mariko fatal error interrupt. */
+ constexpr u8 MarikoFatalInterruptCoreMask = 0b1111;
+ gic::SetPriority (MarikoFatalErrorInterruptId, gic::HighestPriority);
+ gic::SetInterruptGroup(MarikoFatalErrorInterruptId, 0);
+ gic::SetEnable (MarikoFatalErrorInterruptId, true);
+ gic::SetSpiTargetCpu (MarikoFatalErrorInterruptId, 0);
+ gic::SetSpiMode (MarikoFatalErrorInterruptId, gic::InterruptMode_Level);
/* If we're coldboot, perform one-time setup. */
if (g_is_cold_boot) {
- /* Register both interrupt handlers. */
- SetInterruptHandler(SecurityEngineInterruptId, se::HandleInterrupt);
- SetInterruptHandler(ActivityMonitorInterruptId, actmon::HandleInterrupt);
+ /* Register all interrupt handlers. */
+ SetInterruptHandler(SecurityEngineInterruptId, SecurityEngineInterruptCoreMask, se::HandleInterrupt);
+ SetInterruptHandler(ActivityMonitorInterruptId, ActivityMonitorInterruptCoreMask, actmon::HandleInterrupt);
+ SetInterruptHandler(MarikoFatalErrorInterruptId, MarikoFatalInterruptCoreMask, secmon::HandleMarikoFatalErrorInterrupt);
/* We're expecting the other cores to come out of reset. */
for (int i = 1; i < NumCores; ++i) {
diff --git a/exosphere/program/source/secmon_user_power_management.cpp b/exosphere/program/source/secmon_user_power_management.cpp
index f1190905b..b17d6e98c 100644
--- a/exosphere/program/source/secmon_user_power_management.cpp
+++ b/exosphere/program/source/secmon_user_power_management.cpp
@@ -14,8 +14,9 @@
* along with this program. If not, see .
*/
#include
-#include "secmon_map.hpp"
+#include "secmon_cpu_context.hpp"
#include "secmon_page_mapper.hpp"
+#include "secmon_mariko_fatal_error.hpp"
#include "secmon_user_power_management.hpp"
#include "rebootstub_bin.h"
@@ -90,34 +91,11 @@ namespace ams::secmon {
/* On Erista, we reboot to fatal error by jumping to fusee primary's handler. */
return PerformUserRebootToPayload();
} else /* if (fuse::GetSocType() == fuse::SocType_Mariko) */ {
- /* TODO: Send a SGI FIQ to the other CPUs, so that user code stops executing. */
+ /* Call the fatal error handler. */
+ HandleMarikoFatalErrorInterrupt();
- /* TODO: On cores other than 3, halt/wfi. */
-
- /* Copy the fatal error context to mariko tzram. */
- {
- /* Map the iram page. */
- constexpr uintptr_t FatalErrorPhysicalAddress = MemoryRegionPhysicalIramFatalErrorContext.GetAddress();
- AtmosphereIramPageMapper mapper(FatalErrorPhysicalAddress);
- if (mapper.Map()) {
- /* Copy the fatal error context. */
- void *dst = MemoryRegionVirtualTzramMarikoProgramFatalErrorContext.GetPointer();
- const void *src = mapper.GetPointerTo(FatalErrorPhysicalAddress, sizeof(ams::impl::FatalErrorContext));
- std::memcpy(dst, src, sizeof(ams::impl::FatalErrorContext));
- }
- }
-
- /* Map Dram for the mariko program. */
- MapDramForMarikoProgram();
-
- AMS_SECMON_LOG("%s\n", "Jumping to Mariko Fatal.");
- AMS_LOG_FLUSH();
-
- /* Jump to the mariko fatal program. */
- reinterpret_cast(secmon::MemoryRegionVirtualTzramMarikoProgram.GetAddress())();
-
- /* The mariko fatal program never returns. */
- __builtin_unreachable();
+ /* We should never get to this point. */
+ AMS_ABORT("Returned from Mariko Fatal handler?\n");
}
}
diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp
index 926331463..7ed5f3377 100644
--- a/exosphere/program/source/smc/secmon_smc_power_management.cpp
+++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp
@@ -133,7 +133,7 @@ namespace ams::secmon::smc {
REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */
}
- void PowerOffCpu() {
+ void PowerOffCpuImpl() {
/* Get the current core id. */
const auto core_id = hw::GetCurrentCoreId();
@@ -503,9 +503,7 @@ namespace ams::secmon::smc {
}
- SmcResult SmcPowerOffCpu(SmcArguments &args) {
- AMS_UNUSED(args);
-
+ void PowerOffCpu() {
/* Get the current core id. */
const auto core_id = hw::GetCurrentCoreId();
@@ -514,15 +512,21 @@ namespace ams::secmon::smc {
/* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */
if (core_id == NumCores - 1) {
- PowerOffCpu();
+ PowerOffCpuImpl();
} else {
- PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpu);
+ PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpuImpl);
}
/* This code will never be reached. */
__builtin_unreachable();
}
+ SmcResult SmcPowerOffCpu(SmcArguments &args) {
+ AMS_UNUSED(args);
+
+ PowerOffCpu();
+ }
+
SmcResult SmcPowerOnCpu(SmcArguments &args) {
/* Get and validate the core to power on. */
const int which_core = args.r[1];
diff --git a/exosphere/program/source/smc/secmon_smc_power_management.hpp b/exosphere/program/source/smc/secmon_smc_power_management.hpp
index 65bd53df2..81202a000 100644
--- a/exosphere/program/source/smc/secmon_smc_power_management.hpp
+++ b/exosphere/program/source/smc/secmon_smc_power_management.hpp
@@ -19,6 +19,8 @@
namespace ams::secmon::smc {
+ NORETURN void PowerOffCpu();
+
SmcResult SmcPowerOffCpu(SmcArguments &args);
SmcResult SmcPowerOnCpu(SmcArguments &args);