diff --git a/Source/Atmosphere-Patches/secmon_smc_handler.cpp b/Source/Atmosphere-Patches/secmon_smc_handler.cpp
new file mode 100644
index 00000000..c5b7369f
--- /dev/null
+++ b/Source/Atmosphere-Patches/secmon_smc_handler.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 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_error.hpp"
+#include "../secmon_misc.hpp"
+#include "secmon_smc_common.hpp"
+#include "secmon_smc_handler.hpp"
+#include "secmon_smc_aes.hpp"
+#include "secmon_smc_carveout.hpp"
+#include "secmon_smc_device_unique_data.hpp"
+#include "secmon_smc_error.hpp"
+#include "secmon_smc_info.hpp"
+#include "secmon_smc_memory_access.hpp"
+#include "secmon_smc_power_management.hpp"
+#include "secmon_smc_random.hpp"
+#include "secmon_smc_register_access.hpp"
+#include "secmon_smc_result.hpp"
+#include "secmon_smc_rsa.hpp"
+
+namespace ams::secmon::smc {
+
+ namespace {
+
+ struct HandlerInfo {
+ u32 function_id;
+ u32 restriction_mask;
+ SmcHandler handler;
+ };
+
+ struct HandlerTable {
+ const HandlerInfo *entries;
+ size_t count;
+ };
+
+ enum HandlerType : int {
+ HandlerType_User = 0,
+ HandlerType_Kern = 1,
+ HandlerType_Count = 2,
+ };
+
+ enum Restriction {
+ Restriction_None = (0 << 0),
+ Restriction_Normal = (1 << 0),
+ Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
+ Restriction_SafeModeNotAllowed = (1 << 2),
+ };
+
+ enum SmcCallRange {
+ SmcCallRange_ArmArch = 0,
+ SmcCallRange_Cpu = 1,
+ SmcCallRange_Sip = 2,
+ SmcCallRange_Oem = 3,
+ SmcCallRange_Standard = 4,
+
+ SmcCallRange_TrustedApp = 0x30,
+ };
+
+ enum SmcArgumentType {
+ ArgumentType_Integer = 0,
+ ArgumentType_Pointer = 1,
+ };
+
+ enum SmcConvention {
+ Convention_Smc32 = 0,
+ Convention_Smc64 = 1,
+ };
+
+ enum SmcCallType {
+ SmcCallType_YieldingCall = 0,
+ SmcCallType_FastCall = 1,
+ };
+
+ struct SmcFunctionId {
+ using FunctionId = util::BitPack64::Field< 0, 8, u32>;
+ using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
+ using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
+ using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
+ using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
+ using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
+ using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
+ using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
+ using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
+ using Reserved = util::BitPack64::Field<16, 8, u32>;
+ using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
+ using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
+ using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
+ using Reserved2 = util::BitPack64::Field<32, 32, u32>;
+ };
+
+ constinit HandlerInfo g_user_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
+ { 0xC3000002, Restriction_Normal, SmcGetConfigUser },
+ { 0xC3000003, Restriction_Normal, SmcGetResult },
+ { 0xC3000404, Restriction_Normal, SmcGetResultData },
+ { 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
+ { 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
+ { 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
+ { 0xC3000008, Restriction_Normal, SmcLoadAesKey },
+ { 0xC3000009, Restriction_Normal, SmcComputeAes },
+ { 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
+ { 0xC300040B, Restriction_Normal, SmcComputeCmac },
+ { 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
+ { 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
+ { 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
+ { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
+ { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
+ { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
+ };
+
+ /* Deprecated handlerss. */
+ constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = {
+ 0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey
+ };
+
+ constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = {
+ 0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey
+ };
+
+ constinit HandlerInfo g_kern_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
+ { 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
+ { 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
+ { 0xC3000004, Restriction_Normal, SmcGetConfigKern },
+ { 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
+ { 0xC3000006, Restriction_Normal, SmcShowError },
+ { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
+ { 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
+
+ /* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
+ { 0xC3000409, Restriction_Normal, SmcSetConfig },
+ };
+
+ constinit HandlerInfo g_ams_handlers[] = {
+ { 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
+ { 0xF0000201, Restriction_None, SmcIramCopy },
+ { 0xF0000002, Restriction_None, SmcReadWriteRegister },
+ { 0xF0000003, Restriction_None, SmcWriteAddress },
+ { 0xF0000404, Restriction_None, SmcGetEmummcConfig },
+ { 0xF0000005, Restriction_None, SmcShowError },
+ };
+
+ constexpr const HandlerInfo GetSecureDataHandlerInfo = {
+ 0x67891234, Restriction_None, SmcGetSecureData
+ };
+
+ constinit HandlerTable g_handler_tables[] = {
+ { g_user_handlers, util::size(g_user_handlers) },
+ { g_kern_handlers, util::size(g_kern_handlers) },
+ };
+
+ constinit HandlerTable g_ams_handler_table = {
+ g_ams_handlers, util::size(g_ams_handlers)
+ };
+
+ NORETURN void InvalidSmcError(u64 id) {
+ SetError(pkg1::ErrorInfo_UnknownSmc);
+ AMS_ABORT("Invalid SMC: %lx", id);
+ }
+
+ const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
+ /* Ensure we have a valid handler type. */
+ if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
+ InvalidSmcError(id);
+ }
+
+ /* Provide support for legacy SmcGetSecureData. */
+ if (id == GetSecureDataHandlerInfo.function_id) {
+ return g_handler_tables[HandlerType_User];
+ }
+
+ /* Check if we're a user SMC. */
+ if (type == HandlerType_User) {
+ /* Nintendo uses OEM SMCs. */
+ /* We will assign Atmosphere extension SMCs the TrustedApplication range. */
+ if (util::BitPack64{id}.Get() == SmcCallRange_TrustedApp) {
+ return g_ams_handler_table;
+ }
+
+ /* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
+ if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
+ InvalidSmcError(id);
+ }
+ }
+
+ return g_handler_tables[type];
+ }
+
+ const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
+ /* Provide support for legacy SmcGetSecureData. */
+ if (id == GetSecureDataHandlerInfo.function_id) {
+ return GetSecureDataHandlerInfo;
+ }
+
+ /* Get and check the index. */
+ const auto index = util::BitPack64{id}.Get();
+ if (AMS_UNLIKELY(index >= table.count)) {
+ InvalidSmcError(id);
+ }
+
+ /* Get and check the handler info. */
+ const auto &handler_info = table.entries[index];
+
+ /* Check that the handler isn't null. */
+ if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
+ InvalidSmcError(id);
+ }
+
+ /* Check that the handler's id matches. */
+ if (AMS_UNLIKELY(handler_info.function_id != id)) {
+ InvalidSmcError(id);
+ }
+
+ return handler_info;
+ }
+
+ bool IsHandlerRestricted(const HandlerInfo &info) {
+ return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
+ }
+
+ SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
+ /* Check if the smc is restricted. */
+ if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) {
+ return SmcResult::NotPermitted;
+ }
+
+ /* Invoke the smc. */
+ return info.handler(args);
+ }
+
+ }
+
+ void ConfigureSmcHandlersForTargetFirmware() {
+ const auto target_fw = GetTargetFirmware();
+
+ if (target_fw < TargetFirmware_5_0_0) {
+ g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo;
+ g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo;
+ }
+ }
+
+ void HandleSmc(int type, SmcArguments &args) {
+ /* Get the table. */
+ const auto &table = GetHandlerTable(static_cast(type), args.r[0]);
+
+ /* Get the handler info. */
+ const auto &info = GetHandlerInfo(table, args.r[0]);
+
+ /* Set the invocation result. */
+ args.r[0] = static_cast(InvokeSmcHandler(info, args));
+ }
+
+}