add entire atmosphere source code and also add ram timing fixes

This commit is contained in:
souldbminersmwc
2025-08-31 14:07:54 -04:00
parent fd91bc07d8
commit ca285351db
3743 changed files with 565998 additions and 23 deletions

View File

@@ -0,0 +1,21 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#define ATOMICS_AP0_TRIGGER 0x000
#define ATOMICS_AP0_RESULT(id) (0xc00 + id * 4)
#define TRIGGER_CMD_GET 4

View File

@@ -0,0 +1,63 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere.hpp>
/* Message Flags */
#define BPMP_MSG_DO_ACK (1 << 0)
#define BPMP_MSG_RING_DOORBELL (1 << 1)
/* Messages */
#define MRQ_PING 0
#define MRQ_ENABLE_SUSPEND 17
#define MRQ_CPU_PMIC_SELECT 28
/* BPMP Power states. */
#define TEGRA_BPMP_PM_CC1 9
#define TEGRA_BPMP_PM_CC4 12
#define TEGRA_BPMP_PM_CC6 14
#define TEGRA_BPMP_PM_CC7 15
#define TEGRA_BPMP_PM_SC1 17
#define TEGRA_BPMP_PM_SC2 18
#define TEGRA_BPMP_PM_SC3 19
#define TEGRA_BPMP_PM_SC4 20
#define TEGRA_BPMP_PM_SC7 23
/* Channel states. */
#define CH_MASK(ch) (0x3u << ((ch) * 2))
#define SL_SIGL(ch) (0x0u << ((ch) * 2))
#define SL_QUED(ch) (0x1u << ((ch) * 2))
#define MA_FREE(ch) (0x2u << ((ch) * 2))
#define MA_ACKD(ch) (0x3u << ((ch) * 2))
constexpr inline int MessageSize = 0x80;
constexpr inline int MessageDataSizeMax = 0x78;
struct MailboxData {
s32 code;
s32 flags;
u8 data[MessageDataSizeMax];
};
static_assert(ams::util::is_pod<MailboxData>::value);
static_assert(sizeof(MailboxData) == MessageSize);
struct ChannelData {
MailboxData *ib;
MailboxData *ob;
};

View File

@@ -0,0 +1,25 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#define ICTLR_REG_BASE(irq) ((((irq) - 32) >> 5) * 0x100)
#define ICTLR_FIR_SET(irq) (ICTLR_REG_BASE(irq) + 0x18)
#define ICTLR_FIR_CLR(irq) (ICTLR_REG_BASE(irq) + 0x1c)
#define FIR_BIT(irq) (1 << ((irq) & 0x1f))
#define INT_GIC_BASE (0)
#define INT_PRI_BASE (INT_GIC_BASE + 32)
#define INT_SHR_SEM_OUTBOX_IBF (INT_PRI_BASE + 6)

View File

@@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
constexpr IoRegionExtents g_io_region_extents[4] = {
{ KPhysicalAddress(0x12000000), 224_MB }, /* PCIE_A2 */
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
{ Null<KPhysicalAddress>, 0 },
};
constexpr bool IsValidIoPoolTypeImpl(ams::svc::IoPoolType pool_type) {
return pool_type == ams::svc::IoPoolType_PcieA2;
}

View File

@@ -0,0 +1,639 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_k_sleep_manager.hpp"
#include "kern_secure_monitor.hpp"
#include "kern_lps_driver.hpp"
namespace ams::kern::init {
void StartOtherCore(const ams::kern::init::KInitArguments *init_args);
}
namespace ams::kern::board::nintendo::nx {
namespace {
/* Struct representing registers saved on wake/sleep. */
class SavedSystemRegisters {
private:
u64 elr_el1;
u64 sp_el0;
u64 spsr_el1;
u64 daif;
u64 cpacr_el1;
u64 vbar_el1;
u64 csselr_el1;
u64 cntp_ctl_el0;
u64 cntp_cval_el0;
u64 cntkctl_el1;
u64 tpidr_el0;
u64 tpidrro_el0;
u64 mdscr_el1;
u64 contextidr_el1;
u64 dbgwcrN_el1[16];
u64 dbgwvrN_el1[16];
u64 dbgbcrN_el1[16];
u64 dbgbvrN_el1[16];
u64 pmccfiltr_el0;
u64 pmccntr_el0;
u64 pmcntenset_el0;
u64 pmcr_el0;
u64 pmevcntrN_el0[31];
u64 pmevtyperN_el0[31];
u64 pmintenset_el1;
u64 pmovsset_el0;
u64 pmselr_el0;
u64 pmuserenr_el0;
public:
void Save();
void Restore() const;
};
constexpr s32 SleepManagerThreadPriority = 2;
/* Globals for sleep/wake. */
constinit u64 g_sleep_target_cores;
constinit KLightLock g_request_lock;
constinit KLightLock g_cv_lock;
constinit KLightConditionVariable g_cv{util::ConstantInitialize};
alignas(1_KB) constinit u64 g_sleep_buffers[cpu::NumCores][1_KB / sizeof(u64)];
constinit ams::kern::init::KInitArguments g_sleep_init_arguments[cpu::NumCores];
constinit SavedSystemRegisters g_sleep_system_registers[cpu::NumCores] = {};
void WaitOtherCpuPowerOff() {
constexpr u64 PmcPhysicalAddress = 0x7000E400;
constexpr u32 PWRGATE_STATUS_CE123_MASK = ((1u << 3) - 1) << 9;
u32 value;
do {
bool res = smc::ReadWriteRegister(std::addressof(value), PmcPhysicalAddress + APBDEV_PMC_PWRGATE_STATUS, 0, 0);
MESOSPHERE_ASSERT(res);
MESOSPHERE_UNUSED(res);
} while ((value & PWRGATE_STATUS_CE123_MASK) != 0);
}
void SavedSystemRegisters::Save() {
/* Save system registers. */
this->tpidr_el0 = cpu::GetTpidrEl0();
this->elr_el1 = cpu::GetElrEl1();
this->sp_el0 = cpu::GetSpEl0();
this->spsr_el1 = cpu::GetSpsrEl1();
this->daif = cpu::GetDaif();
this->cpacr_el1 = cpu::GetCpacrEl1();
this->vbar_el1 = cpu::GetVbarEl1();
this->csselr_el1 = cpu::GetCsselrEl1();
this->cntp_ctl_el0 = cpu::GetCntpCtlEl0();
this->cntp_cval_el0 = cpu::GetCntpCvalEl0();
this->cntkctl_el1 = cpu::GetCntkCtlEl1();
this->tpidrro_el0 = cpu::GetTpidrRoEl0();
/* Save pmu registers. */
{
/* Get and clear pmcr_el0 */
this->pmcr_el0 = cpu::GetPmcrEl0();
cpu::SetPmcrEl0(0);
cpu::EnsureInstructionConsistency();
/* Save other pmu registers. */
this->pmuserenr_el0 = cpu::GetPmUserEnrEl0();
this->pmselr_el0 = cpu::GetPmSelrEl0();
this->pmccfiltr_el0 = cpu::GetPmcCfiltrEl0();
this->pmcntenset_el0 = cpu::GetPmCntEnSetEl0();
this->pmintenset_el1 = cpu::GetPmIntEnSetEl1();
this->pmovsset_el0 = cpu::GetPmOvsSetEl0();
this->pmccntr_el0 = cpu::GetPmcCntrEl0();
switch (cpu::PerformanceMonitorsControlRegisterAccessor(this->pmcr_el0).GetN()) {
#define HANDLE_PMU_CASE(N) \
case (N+1): \
this->pmevcntrN_el0 [ N ] = cpu::GetPmevCntr##N##El0(); \
this->pmevtyperN_el0[ N ] = cpu::GetPmevTyper##N##El0(); \
[[fallthrough]]
HANDLE_PMU_CASE(30);
HANDLE_PMU_CASE(29);
HANDLE_PMU_CASE(28);
HANDLE_PMU_CASE(27);
HANDLE_PMU_CASE(26);
HANDLE_PMU_CASE(25);
HANDLE_PMU_CASE(24);
HANDLE_PMU_CASE(23);
HANDLE_PMU_CASE(22);
HANDLE_PMU_CASE(21);
HANDLE_PMU_CASE(20);
HANDLE_PMU_CASE(19);
HANDLE_PMU_CASE(18);
HANDLE_PMU_CASE(17);
HANDLE_PMU_CASE(16);
HANDLE_PMU_CASE(15);
HANDLE_PMU_CASE(14);
HANDLE_PMU_CASE(13);
HANDLE_PMU_CASE(12);
HANDLE_PMU_CASE(11);
HANDLE_PMU_CASE(10);
HANDLE_PMU_CASE( 9);
HANDLE_PMU_CASE( 8);
HANDLE_PMU_CASE( 7);
HANDLE_PMU_CASE( 6);
HANDLE_PMU_CASE( 5);
HANDLE_PMU_CASE( 4);
HANDLE_PMU_CASE( 3);
HANDLE_PMU_CASE( 2);
HANDLE_PMU_CASE( 1);
HANDLE_PMU_CASE( 0);
#undef HANDLE_PMU_CASE
case 0:
default:
break;
}
}
/* Save debug registers. */
const u64 dfr0 = cpu::GetIdAa64Dfr0El1();
this->mdscr_el1 = cpu::GetMdscrEl1();
this->contextidr_el1 = cpu::GetContextidrEl1();
/* Save watchpoints. */
switch (cpu::DebugFeatureRegisterAccessor(dfr0).GetNumWatchpoints()) {
#define HANDLE_DBG_CASE(N) \
case N: \
this->dbgwcrN_el1[ N ] = cpu::GetDbgWcr##N##El1(); \
this->dbgwvrN_el1[ N ] = cpu::GetDbgWvr##N##El1(); \
[[fallthrough]]
HANDLE_DBG_CASE(15);
HANDLE_DBG_CASE(14);
HANDLE_DBG_CASE(13);
HANDLE_DBG_CASE(12);
HANDLE_DBG_CASE(11);
HANDLE_DBG_CASE(10);
HANDLE_DBG_CASE( 9);
HANDLE_DBG_CASE( 8);
HANDLE_DBG_CASE( 7);
HANDLE_DBG_CASE( 6);
HANDLE_DBG_CASE( 5);
HANDLE_DBG_CASE( 4);
HANDLE_DBG_CASE( 3);
HANDLE_DBG_CASE( 2);
#undef HANDLE_DBG_CASE
case 1:
this->dbgwcrN_el1[1] = cpu::GetDbgWcr1El1();
this->dbgwvrN_el1[1] = cpu::GetDbgWvr1El1();
this->dbgwcrN_el1[0] = cpu::GetDbgWcr0El1();
this->dbgwvrN_el1[0] = cpu::GetDbgWvr0El1();
[[fallthrough]];
default:
break;
}
/* Save breakpoints. */
switch (cpu::DebugFeatureRegisterAccessor(dfr0).GetNumBreakpoints()) {
#define HANDLE_DBG_CASE(N) \
case N: \
this->dbgbcrN_el1[ N ] = cpu::GetDbgBcr##N##El1(); \
this->dbgbvrN_el1[ N ] = cpu::GetDbgBvr##N##El1(); \
[[fallthrough]]
HANDLE_DBG_CASE(15);
HANDLE_DBG_CASE(14);
HANDLE_DBG_CASE(13);
HANDLE_DBG_CASE(12);
HANDLE_DBG_CASE(11);
HANDLE_DBG_CASE(10);
HANDLE_DBG_CASE( 9);
HANDLE_DBG_CASE( 8);
HANDLE_DBG_CASE( 7);
HANDLE_DBG_CASE( 6);
HANDLE_DBG_CASE( 5);
HANDLE_DBG_CASE( 4);
HANDLE_DBG_CASE( 3);
HANDLE_DBG_CASE( 2);
#undef HANDLE_DBG_CASE
case 1:
this->dbgbcrN_el1[1] = cpu::GetDbgBcr1El1();
this->dbgbvrN_el1[1] = cpu::GetDbgBvr1El1();
[[fallthrough]];
default:
break;
}
this->dbgbcrN_el1[0] = cpu::GetDbgBcr0El1();
this->dbgbvrN_el1[0] = cpu::GetDbgBvr0El1();
cpu::EnsureInstructionConsistency();
/* Clear mdscr_el1. */
cpu::SetMdscrEl1(0);
cpu::EnsureInstructionConsistency();
}
void SavedSystemRegisters::Restore() const {
/* Restore debug registers. */
const u64 dfr0 = cpu::GetIdAa64Dfr0El1();
cpu::EnsureInstructionConsistency();
cpu::SetMdscrEl1(0);
cpu::EnsureInstructionConsistency();
cpu::SetOslarEl1(0);
cpu::EnsureInstructionConsistency();
/* Restore watchpoints. */
switch (cpu::DebugFeatureRegisterAccessor(dfr0).GetNumWatchpoints()) {
#define HANDLE_DBG_CASE(N) \
case N: \
cpu::SetDbgWcr##N##El1(this->dbgwcrN_el1[ N ]); \
cpu::SetDbgWvr##N##El1(this->dbgwvrN_el1[ N ]); \
[[fallthrough]]
HANDLE_DBG_CASE(15);
HANDLE_DBG_CASE(14);
HANDLE_DBG_CASE(13);
HANDLE_DBG_CASE(12);
HANDLE_DBG_CASE(11);
HANDLE_DBG_CASE(10);
HANDLE_DBG_CASE( 9);
HANDLE_DBG_CASE( 8);
HANDLE_DBG_CASE( 7);
HANDLE_DBG_CASE( 6);
HANDLE_DBG_CASE( 5);
HANDLE_DBG_CASE( 4);
HANDLE_DBG_CASE( 3);
HANDLE_DBG_CASE( 2);
#undef HANDLE_DBG_CASE
case 1:
cpu::SetDbgWcr1El1(this->dbgwcrN_el1[1]);
cpu::SetDbgWvr1El1(this->dbgwvrN_el1[1]);
cpu::SetDbgWcr0El1(this->dbgwcrN_el1[0]);
cpu::SetDbgWvr0El1(this->dbgwvrN_el1[0]);
[[fallthrough]];
default:
break;
}
/* Restore breakpoints. */
switch (cpu::DebugFeatureRegisterAccessor(dfr0).GetNumBreakpoints()) {
#define HANDLE_DBG_CASE(N) \
case N: \
cpu::SetDbgBcr##N##El1(this->dbgbcrN_el1[ N ]); \
cpu::SetDbgBvr##N##El1(this->dbgbvrN_el1[ N ]); \
[[fallthrough]]
HANDLE_DBG_CASE(15);
HANDLE_DBG_CASE(14);
HANDLE_DBG_CASE(13);
HANDLE_DBG_CASE(12);
HANDLE_DBG_CASE(11);
HANDLE_DBG_CASE(10);
HANDLE_DBG_CASE( 9);
HANDLE_DBG_CASE( 8);
HANDLE_DBG_CASE( 7);
HANDLE_DBG_CASE( 6);
HANDLE_DBG_CASE( 5);
HANDLE_DBG_CASE( 4);
HANDLE_DBG_CASE( 3);
HANDLE_DBG_CASE( 2);
#undef HANDLE_DBG_CASE
case 1:
cpu::SetDbgBcr1El1(this->dbgbcrN_el1[1]);
cpu::SetDbgBvr1El1(this->dbgbvrN_el1[1]);
[[fallthrough]];
default:
break;
}
cpu::SetDbgBcr0El1(this->dbgbcrN_el1[0]);
cpu::SetDbgBvr0El1(this->dbgbvrN_el1[0]);
cpu::EnsureInstructionConsistency();
cpu::SetContextidrEl1(this->contextidr_el1);
cpu::EnsureInstructionConsistency();
cpu::SetMdscrEl1(this->mdscr_el1);
cpu::EnsureInstructionConsistency();
/* Restore pmu registers. */
cpu::SetPmUserEnrEl0(0);
cpu::PerformanceMonitorsControlRegisterAccessor(0).SetEventCounterReset(true).SetCycleCounterReset(true).Store();
cpu::EnsureInstructionConsistency();
cpu::SetPmOvsClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
cpu::SetPmIntEnClrEl1(static_cast<u64>(static_cast<u32>(~u32())));
cpu::SetPmCntEnClrEl0(static_cast<u64>(static_cast<u32>(~u32())));
switch (cpu::PerformanceMonitorsControlRegisterAccessor(this->pmcr_el0).GetN()) {
#define HANDLE_PMU_CASE(N) \
case (N+1): \
cpu::SetPmevCntr##N##El0 (this->pmevcntrN_el0 [ N ]); \
cpu::SetPmevTyper##N##El0(this->pmevtyperN_el0[ N ]); \
[[fallthrough]]
HANDLE_PMU_CASE(30);
HANDLE_PMU_CASE(29);
HANDLE_PMU_CASE(28);
HANDLE_PMU_CASE(27);
HANDLE_PMU_CASE(26);
HANDLE_PMU_CASE(25);
HANDLE_PMU_CASE(24);
HANDLE_PMU_CASE(23);
HANDLE_PMU_CASE(22);
HANDLE_PMU_CASE(21);
HANDLE_PMU_CASE(20);
HANDLE_PMU_CASE(19);
HANDLE_PMU_CASE(18);
HANDLE_PMU_CASE(17);
HANDLE_PMU_CASE(16);
HANDLE_PMU_CASE(15);
HANDLE_PMU_CASE(14);
HANDLE_PMU_CASE(13);
HANDLE_PMU_CASE(12);
HANDLE_PMU_CASE(11);
HANDLE_PMU_CASE(10);
HANDLE_PMU_CASE( 9);
HANDLE_PMU_CASE( 8);
HANDLE_PMU_CASE( 7);
HANDLE_PMU_CASE( 6);
HANDLE_PMU_CASE( 5);
HANDLE_PMU_CASE( 4);
HANDLE_PMU_CASE( 3);
HANDLE_PMU_CASE( 2);
HANDLE_PMU_CASE( 1);
HANDLE_PMU_CASE( 0);
#undef HANDLE_PMU_CASE
case 0:
default:
break;
}
cpu::SetPmUserEnrEl0 (this->pmuserenr_el0);
cpu::SetPmSelrEl0 (this->pmselr_el0);
cpu::SetPmcCfiltrEl0 (this->pmccfiltr_el0);
cpu::SetPmCntEnSetEl0(this->pmcntenset_el0);
cpu::SetPmIntEnSetEl1(this->pmintenset_el1);
cpu::SetPmOvsSetEl0 (this->pmovsset_el0);
cpu::SetPmcCntrEl0 (this->pmccntr_el0);
cpu::EnsureInstructionConsistency();
cpu::SetPmcrEl0(this->pmcr_el0);
cpu::EnsureInstructionConsistency();
/* Restore system registers. */
cpu::SetTtbr0El1 (KPageTable::GetKernelTtbr0());
cpu::SetTpidrEl0 (this->tpidr_el0);
cpu::SetElrEl1 (this->elr_el1);
cpu::SetSpEl0 (this->sp_el0);
cpu::SetSpsrEl1 (this->spsr_el1);
cpu::SetDaif (this->daif);
cpu::SetCpacrEl1 (this->cpacr_el1);
cpu::SetVbarEl1 (this->vbar_el1);
cpu::SetCsselrEl1 (this->csselr_el1);
cpu::SetCntpCtlEl0 (this->cntp_ctl_el0);
cpu::SetCntpCvalEl0(this->cntp_cval_el0);
cpu::SetCntkCtlEl1 (this->cntkctl_el1);
cpu::SetTpidrRoEl0 (this->tpidrro_el0);
cpu::EnsureInstructionConsistency();
/* Invalidate the entire tlb. */
cpu::InvalidateEntireTlb();
}
}
void KSleepManager::Initialize() {
/* Create a sleep manager thread for each core. */
for (size_t core_id = 0; core_id < cpu::NumCores; core_id++) {
/* Reserve a thread from the system limit. */
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_ThreadCountMax, 1));
/* Create a new thread. */
KThread *new_thread = KThread::Create();
MESOSPHERE_ABORT_UNLESS(new_thread != nullptr);
/* Launch the new thread. */
MESOSPHERE_R_ABORT_UNLESS(KThread::InitializeKernelThread(new_thread, KSleepManager::ProcessRequests, reinterpret_cast<uintptr_t>(g_sleep_buffers[core_id]), SleepManagerThreadPriority, static_cast<s32>(core_id)));
/* Register the new thread. */
KThread::Register(new_thread);
/* Run the thread. */
new_thread->Run();
}
}
void KSleepManager::SleepSystem() {
/* Ensure device mappings are not modified during sleep. */
KDevicePageTable::Lock();
ON_SCOPE_EXIT { KDevicePageTable::Unlock(); };
/* Request that the system sleep. */
{
KScopedLightLock lk(g_request_lock);
/* Signal the manager to sleep on all cores. */
{
KScopedLightLock lk(g_cv_lock);
MESOSPHERE_ABORT_UNLESS(g_sleep_target_cores == 0);
g_sleep_target_cores = (1ul << cpu::NumCores) - 1;
g_cv.Broadcast();
while (g_sleep_target_cores != 0) {
g_cv.Wait(std::addressof(g_cv_lock));
}
}
}
}
void KSleepManager::ProcessRequests(uintptr_t sleep_buffer) {
const auto target_fw = GetTargetFirmware();
const s32 core_id = GetCurrentCoreId();
ams::kern::init::KInitArguments * const init_args = g_sleep_init_arguments + core_id;
KPhysicalAddress start_core_phys_addr = Null<KPhysicalAddress>;
KPhysicalAddress init_args_phys_addr = Null<KPhysicalAddress>;
/* Get the physical addresses we'll need. */
{
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(start_core_phys_addr), KProcessAddress(&::ams::kern::init::StartOtherCore)));
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(init_args_phys_addr), KProcessAddress(init_args)));
}
const u64 target_core_mask = (1ul << core_id);
const bool use_legacy_lps_driver = target_fw < TargetFirmware_2_0_0;
/* Loop, processing sleep when requested. */
while (true) {
/* Wait for a request. */
{
KScopedLightLock lk(g_cv_lock);
while ((g_sleep_target_cores & target_core_mask) == 0) {
g_cv.Wait(std::addressof(g_cv_lock));
}
}
/* If on core 0, ensure the legacy lps driver is initialized. */
if (use_legacy_lps_driver && core_id == 0) {
lps::Initialize();
}
/* Perform Sleep/Wake sequence. */
{
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Save the system registers for the current core. */
g_sleep_system_registers[core_id].Save();
/* Invalidate the entire tlb. */
cpu::InvalidateEntireTlb();
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* If on core 0, put the device page tables to sleep. */
if (core_id == 0) {
KDevicePageTable::Sleep();
}
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* Wait 100us before continuing. */
{
const s64 timeout = KHardwareTimer::GetTick() + ams::svc::Tick(TimeSpan::FromMicroSeconds(100));
while (KHardwareTimer::GetTick() < timeout) {
__asm__ __volatile__("" ::: "memory");
}
}
/* Save the interrupt manager's state. */
Kernel::GetInterruptManager().Save(core_id);
/* Setup the initial arguments. */
{
/* Determine whether we're running on a cortex-a53 or a-57. */
cpu::MainIdRegisterAccessor midr_el1;
const auto implementer = midr_el1.GetImplementer();
const auto primary_part = midr_el1.GetPrimaryPartNumber();
const bool needs_cpu_ctlr = (implementer == cpu::MainIdRegisterAccessor::Implementer::ArmLimited) && (primary_part == cpu::MainIdRegisterAccessor::PrimaryPartNumber::CortexA57 || primary_part == cpu::MainIdRegisterAccessor::PrimaryPartNumber::CortexA53);
init_args->cpuactlr = needs_cpu_ctlr ? cpu::GetCpuActlrEl1() : 0;
init_args->cpuectlr = needs_cpu_ctlr ? cpu::GetCpuEctlrEl1() : 0;
init_args->sp = 0;
init_args->entrypoint = reinterpret_cast<uintptr_t>(::ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry);
init_args->argument = sleep_buffer;
}
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* Log that the core is going to sleep. */
MESOSPHERE_LOG("Core[%d]: Going to sleep, buffer = %010lx\n", core_id, sleep_buffer);
/* If we're on a core other than zero, we can just invoke the sleep handler. */
if (core_id != 0) {
CpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
} else {
/* Wait for all other cores to be powered off. */
WaitOtherCpuPowerOff();
/* If we're using the legacy lps driver, enable suspend. */
if (use_legacy_lps_driver) {
MESOSPHERE_R_ABORT_UNLESS(lps::EnableSuspend(true));
}
/* Log that we're about to enter SC7. */
MESOSPHERE_LOG("Entering SC7\n");
/* Save the debug log state. */
KDebugLog::Save();
/* Invoke the sleep handler. */
if (!use_legacy_lps_driver) {
/* When not using the legacy driver, invoke directly. */
CpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
} else {
lps::InvokeCpuSleepHandler(sleep_buffer, GetInteger(start_core_phys_addr), GetInteger(init_args_phys_addr));
}
/* Restore the debug log state. */
KDebugLog::Restore();
/* Log that we're about to exit SC7. */
MESOSPHERE_LOG("Exiting SC7\n");
/* Wake up the other cores. */
cpu::MultiprocessorAffinityRegisterAccessor mpidr;
const auto arg = mpidr.GetCpuOnArgument();
for (s32 i = 1; i < static_cast<s32>(cpu::NumCores); ++i) {
KSystemControl::Init::TurnOnCpu(arg | i, g_sleep_init_arguments + i);
}
}
/* Log that the core is waking from sleep. */
MESOSPHERE_LOG("Core[%d]: Woke from sleep.\n", core_id);
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* Restore the interrupt manager's state. */
Kernel::GetInterruptManager().Restore(core_id);
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* If on core 0, wake up the device page tables. */
if (core_id == 0) {
KDevicePageTable::Wakeup();
/* If we're using the legacy driver, resume the bpmp firmware. */
if (use_legacy_lps_driver) {
lps::ResumeBpmpFirmware();
}
}
/* Ensure that all cores get to this point before continuing. */
cpu::SynchronizeAllCores();
/* Restore the system registers for the current core. */
g_sleep_system_registers[core_id].Restore();
}
/* Signal request completed. */
{
KScopedLightLock lk(g_cv_lock);
g_sleep_target_cores &= ~target_core_mask;
if (g_sleep_target_cores == 0) {
g_cv.Broadcast();
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere.hpp>
namespace ams::kern::board::nintendo::nx {
class KSleepManager {
private:
static void ResumeEntry(uintptr_t arg);
static void ProcessRequests(uintptr_t buffer);
public:
static void Initialize();
static void SleepSystem();
public:
static void CpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_args);
};
}

View File

@@ -0,0 +1,158 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
#define cpuactlr_el1 s3_1_c15_c2_0
#define cpuectlr_el1 s3_1_c15_c2_1
#define LOAD_IMMEDIATE_32(reg, val) \
mov reg, #(((val) >> 0x00) & 0xFFFF); \
movk reg, #(((val) >> 0x10) & 0xFFFF), lsl#16
/* ams::kern::board::nintendo::nx::KSleepManager::CpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg) */
.section .sleep._ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm, "ax", %progbits
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm, %function
_ZN3ams4kern5board8nintendo2nx13KSleepManager15CpuSleepHandlerEmmm:
/* Save arguments. */
mov x16, x1
mov x17, x2
/* Enable access to FPU registers. */
mrs x1, cpacr_el1
orr x1, x1, #0x100000
msr cpacr_el1, x1
dsb sy
isb
/* Save callee-save registers. */
stp x18, x19, [x0], #0x10
stp x20, x21, [x0], #0x10
stp x22, x23, [x0], #0x10
stp x24, x25, [x0], #0x10
stp x26, x27, [x0], #0x10
stp x28, x29, [x0], #0x10
stp x30, xzr, [x0], #0x10
/* Save stack pointer. */
mov x1, sp
str x1, [x0], #8
/* Save fpcr/fpsr. */
mrs x1, fpcr
mrs x2, fpsr
stp w1, w2, [x0], #8
/* Save the floating point registers. */
stp q0, q1, [x0], #0x20
stp q2, q3, [x0], #0x20
stp q4, q5, [x0], #0x20
stp q6, q7, [x0], #0x20
stp q8, q9, [x0], #0x20
stp q10, q11, [x0], #0x20
stp q12, q13, [x0], #0x20
stp q14, q15, [x0], #0x20
stp q16, q17, [x0], #0x20
stp q28, q19, [x0], #0x20
stp q20, q21, [x0], #0x20
stp q22, q23, [x0], #0x20
stp q24, q25, [x0], #0x20
stp q26, q27, [x0], #0x20
stp q28, q29, [x0], #0x20
stp q30, q31, [x0], #0x20
/* Save tpidr/cntv_cval_el0. */
mrs x1, tpidr_el1
mrs x2, cntv_cval_el0
stp x1, x2, [x0], #0x10
/* Get the current core id. */
mrs x0, mpidr_el1
and x0, x0, #0xFF
/* If we're on core 0, suspend. */
cbz x0, 1f
/* Otherwise, power off. */
LOAD_IMMEDIATE_32(x0, 0x84000002)
smc #1
0: b 0b
1: /* Suspend. */
LOAD_IMMEDIATE_32(x0, 0xC4000001)
LOAD_IMMEDIATE_32(x1, 0x0201001B)
mov x2, x16
mov x3, x17
smc #1
0: b 0b
/* ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry(uintptr_t arg) */
.section .sleep._ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm, "ax", %progbits
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm, %function
_ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm:
/* Enable access to FPU registers. */
mrs x1, cpacr_el1
orr x1, x1, #0x100000
msr cpacr_el1, x1
dsb sy
isb
/* Restore callee-save registers. */
ldp x18, x19, [x0], #0x10
ldp x20, x21, [x0], #0x10
ldp x22, x23, [x0], #0x10
ldp x24, x25, [x0], #0x10
ldp x26, x27, [x0], #0x10
ldp x28, x29, [x0], #0x10
ldp x30, xzr, [x0], #0x10
/* Restore stack pointer. */
ldr x1, [x0], #8
mov sp, x1
/* Restore fpcr/fpsr. */
ldp w1, w2, [x0], #8
msr fpcr, x1
msr fpsr, x2
/* Restore the floating point registers. */
ldp q0, q1, [x0], #0x20
ldp q2, q3, [x0], #0x20
ldp q4, q5, [x0], #0x20
ldp q6, q7, [x0], #0x20
ldp q8, q9, [x0], #0x20
ldp q10, q11, [x0], #0x20
ldp q12, q13, [x0], #0x20
ldp q14, q15, [x0], #0x20
ldp q16, q17, [x0], #0x20
ldp q28, q19, [x0], #0x20
ldp q20, q21, [x0], #0x20
ldp q22, q23, [x0], #0x20
ldp q24, q25, [x0], #0x20
ldp q26, q27, [x0], #0x20
ldp q28, q29, [x0], #0x20
ldp q30, q31, [x0], #0x20
/* Restore tpidr/cntv_cval_el0. */
ldp x1, x2, [x0], #0x10
msr tpidr_el1, x1
msr cntv_cval_el0, x2
dsb sy
isb
/* Return. */
ret

View File

@@ -0,0 +1,715 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_secure_monitor.hpp"
#include "kern_k_sleep_manager.hpp"
namespace ams::kern::board::nintendo::nx {
namespace {
constexpr size_t SecureAlignment = 128_KB;
constexpr size_t SecureSizeMax = util::AlignDown(512_MB - 1, SecureAlignment);
/* Global variables for panic. */
constinit const volatile bool g_call_smc_on_panic = false;
/* Global variables for secure memory. */
constinit KSpinLock g_secure_applet_lock;
constinit bool g_secure_applet_memory_used = false;
constinit KVirtualAddress g_secure_applet_memory_address = Null<KVirtualAddress>;
constinit KSpinLock g_secure_region_lock;
constinit bool g_secure_region_used = false;
constinit KPhysicalAddress g_secure_region_phys_addr = Null<KPhysicalAddress>;
constinit size_t g_secure_region_size = 0;
ALWAYS_INLINE util::BitPack32 GetKernelConfigurationForInit() {
u64 value = 0;
smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration);
return util::BitPack32{static_cast<u32>(value)};
}
ALWAYS_INLINE u32 GetMemoryModeForInit() {
u64 value = 0;
smc::init::GetConfig(&value, 1, smc::ConfigItem::MemoryMode);
return static_cast<u32>(value);
}
ALWAYS_INLINE smc::MemoryArrangement GetMemoryArrangeForInit() {
switch(GetMemoryModeForInit() & 0x3F) {
case 0x01:
default:
return smc::MemoryArrangement_4GB;
case 0x02:
return smc::MemoryArrangement_4GBForAppletDev;
case 0x03:
return smc::MemoryArrangement_4GBForSystemDev;
case 0x11:
return smc::MemoryArrangement_6GB;
case 0x12:
return smc::MemoryArrangement_6GBForAppletDev;
case 0x21:
return smc::MemoryArrangement_8GB;
}
}
ALWAYS_INLINE u64 GenerateRandomU64ForInit() {
u64 value;
smc::init::GenerateRandomBytes(std::addressof(value), sizeof(value));
return value;
}
ALWAYS_INLINE u64 GenerateRandomU64FromSmc() {
u64 value;
smc::GenerateRandomBytes(std::addressof(value), sizeof(value));
return value;
}
ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) {
u64 value;
smc::GetConfig(&value, 1, which);
return value;
}
ALWAYS_INLINE u32 GetConfigU32(smc::ConfigItem which) {
return static_cast<u32>(GetConfigU64(which));
}
ALWAYS_INLINE bool GetConfigBool(smc::ConfigItem which) {
return GetConfigU64(which) != 0;
}
ALWAYS_INLINE bool CheckRegisterAllowedTable(const u8 *table, const size_t offset) {
return (table[(offset / sizeof(u32)) / BITSIZEOF(u8)] & (1u << ((offset / sizeof(u32)) % BITSIZEOF(u8)))) != 0;
}
/* TODO: Generate this from a list of register names (see similar logic in exosphere)? */
constexpr inline const u8 McKernelRegisterWhitelist[(PageSize / sizeof(u32)) / BITSIZEOF(u8)] = {
0x9F, 0x31, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xC0, 0x73, 0x3E, 0x6F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xE4, 0xFF, 0xFF, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/* TODO: Generate this from a list of register names (see similar logic in exosphere)? */
constexpr inline const u8 McUserRegisterWhitelist[(PageSize / sizeof(u32)) / BITSIZEOF(u8)] = {
0x00, 0x00, 0x20, 0x00, 0xF0, 0xFF, 0xF7, 0x01,
0xCD, 0xFE, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E,
0x30, 0x05, 0x06, 0xB0, 0x71, 0xC8, 0x43, 0x04,
0x80, 0xFF, 0x08, 0x80, 0x03, 0x38, 0x8E, 0x1F,
0xC8, 0xFF, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x00,
0xF0, 0x1F, 0x00, 0x30, 0xF0, 0x03, 0x03, 0x30,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0xFE, 0x0F,
0x01, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
bool IsRegisterAccessibleToPrivileged(ams::svc::PhysicalAddress address) {
/* Find the region for the address. */
const KMemoryRegion *region = KMemoryLayout::Find(KPhysicalAddress(address));
if (AMS_LIKELY(region != nullptr)) {
if (AMS_LIKELY(region->IsDerivedFrom(KMemoryRegionType_MemoryController))) {
/* Check the region is valid. */
MESOSPHERE_ABORT_UNLESS(region->GetEndAddress() != 0);
/* Get the offset within the region. */
const size_t offset = address - region->GetAddress();
MESOSPHERE_ABORT_UNLESS(offset < region->GetSize());
/* Check the whitelist. */
if (AMS_LIKELY(CheckRegisterAllowedTable(McKernelRegisterWhitelist, offset))) {
return true;
}
}
}
return false;
}
bool IsRegisterAccessibleToUser(ams::svc::PhysicalAddress address) {
/* Find the region for the address. */
const KMemoryRegion *region = KMemoryLayout::Find(KPhysicalAddress(address));
if (AMS_LIKELY(region != nullptr)) {
/* The PMC is always allowed. */
if (region->IsDerivedFrom(KMemoryRegionType_PowerManagementController)) {
return true;
}
/* Memory controller is allowed if the register is whitelisted. */
if (region->IsDerivedFrom(KMemoryRegionType_MemoryController ) ||
region->IsDerivedFrom(KMemoryRegionType_MemoryController0) ||
region->IsDerivedFrom(KMemoryRegionType_MemoryController1))
{
/* Check the region is valid. */
MESOSPHERE_ABORT_UNLESS(region->GetEndAddress() != 0);
/* Get the offset within the region. */
const size_t offset = address - region->GetAddress();
MESOSPHERE_ABORT_UNLESS(offset < region->GetSize());
/* Check the whitelist. */
if (AMS_LIKELY(CheckRegisterAllowedTable(McUserRegisterWhitelist, offset))) {
return true;
}
}
}
return false;
}
bool SetSecureRegion(KPhysicalAddress phys_addr, size_t size) {
/* Ensure size is valid. */
if (size > SecureSizeMax) {
return false;
}
/* Ensure address and size are aligned. */
if (!util::IsAligned(GetInteger(phys_addr), SecureAlignment)) {
return false;
}
if (!util::IsAligned(size, SecureAlignment)) {
return false;
}
/* Disable interrupts and acquire the secure region lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_region_lock);
/* If size is non-zero, we're allocating the secure region. Otherwise, we're freeing it. */
if (size != 0) {
/* Verify that the secure region is free. */
if (g_secure_region_used) {
return false;
}
/* Set the secure region. */
g_secure_region_used = true;
g_secure_region_phys_addr = phys_addr;
g_secure_region_size = size;
} else {
/* Verify that the secure region is in use. */
if (!g_secure_region_used) {
return false;
}
/* Verify that the address being freed is the secure region. */
if (phys_addr != g_secure_region_phys_addr) {
return false;
}
/* Clear the secure region. */
g_secure_region_used = false;
g_secure_region_phys_addr = Null<KPhysicalAddress>;
g_secure_region_size = 0;
}
/* Configure the carveout with the secure monitor. */
smc::ConfigureCarveout(1, GetInteger(phys_addr), size);
return true;
}
Result AllocateSecureMemoryForApplet(KVirtualAddress *out, size_t size) {
/* Verify that the size is valid. */
R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
R_UNLESS(size <= KSystemControl::SecureAppletMemorySize, svc::ResultOutOfMemory());
/* Disable interrupts and acquire the secure applet lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_applet_lock);
/* Check that memory is reserved for secure applet use. */
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null<KVirtualAddress>);
/* Verify that the secure applet memory isn't already being used. */
R_UNLESS(!g_secure_applet_memory_used, svc::ResultOutOfMemory());
/* Return the secure applet memory. */
g_secure_applet_memory_used = true;
*out = g_secure_applet_memory_address;
R_SUCCEED();
}
void FreeSecureMemoryForApplet(KVirtualAddress address, size_t size) {
/* Disable interrupts and acquire the secure applet lock. */
KScopedInterruptDisable di;
KScopedSpinLock lk(g_secure_applet_lock);
/* Verify that the memory being freed is correct. */
MESOSPHERE_ABORT_UNLESS(address == g_secure_applet_memory_address);
MESOSPHERE_ABORT_UNLESS(size <= KSystemControl::SecureAppletMemorySize);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_used);
/* Release the secure applet memory. */
g_secure_applet_memory_used = false;
}
u32 GetVersionIdentifier() {
u32 value = 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO) << 0;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR) << 8;
value |= static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 16;
value |= static_cast<u64>('M') << 24;
return value;
}
}
/* Initialization. */
size_t KSystemControl::Init::GetRealMemorySize() {
/* TODO: Move this into a header for the MC in general. */
constexpr u32 MemoryControllerConfigurationRegister = 0x70019050;
u32 config_value;
smc::init::ReadWriteRegister(std::addressof(config_value), MemoryControllerConfigurationRegister, 0, 0);
return static_cast<size_t>(config_value & 0x3FFF) << 20;
}
size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetKernelConfigurationForInit().Get<smc::KernelConfiguration::MemorySize>()) {
case smc::MemorySize_4GB:
default: /* All invalid modes should go to 4GB. */
return 4_GB;
case smc::MemorySize_6GB:
return 6_GB;
case smc::MemorySize_8GB:
return 8_GB;
}
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
return GetKernelConfigurationForInit().Get<smc::KernelConfiguration::IncreaseThreadResourceLimit>();
}
size_t KSystemControl::Init::GetApplicationPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
switch (GetMemoryArrangeForInit()) {
case smc::MemoryArrangement_4GB:
default:
return 3285_MB;
case smc::MemoryArrangement_4GBForAppletDev:
return 2048_MB;
case smc::MemoryArrangement_4GBForSystemDev:
return 3285_MB;
case smc::MemoryArrangement_6GB:
return 4916_MB;
case smc::MemoryArrangement_6GBForAppletDev:
return 3285_MB;
case smc::MemoryArrangement_8GB:
return 6964_MB;
}
}();
/* Return (possibly) adjusted size. */
return base_pool_size;
}
size_t KSystemControl::Init::GetAppletPoolSize() {
/* Get the base pool size. */
const size_t base_pool_size = []() ALWAYS_INLINE_LAMBDA -> size_t {
switch (GetMemoryArrangeForInit()) {
case smc::MemoryArrangement_4GB:
default:
return 507_MB;
case smc::MemoryArrangement_4GBForAppletDev:
return 1554_MB;
case smc::MemoryArrangement_4GBForSystemDev:
return 448_MB;
case smc::MemoryArrangement_6GB:
return 562_MB;
case smc::MemoryArrangement_6GBForAppletDev:
return 2193_MB;
case smc::MemoryArrangement_8GB:
return 562_MB;
}
}();
/* Return (possibly) adjusted size. */
/* NOTE: On 20.0.0+ the browser requires much more memory in the applet pool in order to function. */
/* Thus, we have to reduce our extra system memory size by 26 MB to compensate. */
const size_t ExtraSystemMemoryForAtmosphere = kern::GetTargetFirmware() >= ams::TargetFirmware_20_0_0 ? 14_MB : 40_MB;
return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
}
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
/* Verify that our minimum is at least as large as Nintendo's. */
constexpr size_t MinimumSizeWithFatal = ::ams::svc::RequiredNonSecureSystemMemorySizeWithFatal;
static_assert(MinimumSizeWithFatal >= 0x2C04000);
constexpr size_t MinimumSizeWithoutFatal = ::ams::svc::RequiredNonSecureSystemMemorySize;
static_assert(MinimumSizeWithoutFatal >= 0x2A00000);
/* Include fatal in non-seure size on 16.0.0+. */
return kern::GetTargetFirmware() >= ams::TargetFirmware_16_0_0 ? MinimumSizeWithFatal : MinimumSizeWithoutFatal;
}
u8 KSystemControl::Init::GetDebugLogUartPort() {
/* Get the log configuration. */
u64 value = 0;
smc::init::GetConfig(std::addressof(value), 1, smc::ConfigItem::ExosphereLogConfiguration);
/* Extract the port. */
return static_cast<u8>((value >> 32) & 0xFF);
}
void KSystemControl::Init::CpuOnImpl(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<smc::SmcId_Supervisor>(core_id, entrypoint, arg)) == 0);
}
/* Randomness for Initialization. */
void KSystemControl::Init::GenerateRandom(u64 *dst, size_t count) {
MESOSPHERE_INIT_ABORT_UNLESS(count <= 7);
smc::init::GenerateRandomBytes(dst, count * sizeof(u64));
}
u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64ForInit);
}
/* System Initialization. */
void KSystemControl::ConfigureKTargetSystem() {
/* Configure KTargetSystem. */
volatile auto *ts = const_cast<volatile KTargetSystem::KTargetSystemData *>(std::addressof(KTargetSystem::s_data));
{
/* Set whether we're in debug mode. */
{
ts->is_not_debug_mode = !GetConfigBool(smc::ConfigItem::IsDebugMode);
/* If we're not in debug mode, we don't want to initialize uart logging. */
ts->disable_debug_logging = ts->is_not_debug_mode;
}
/* Set Kernel Configuration. */
{
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
ts->disable_debug_memory_fill = !kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>();
ts->disable_user_exception_handlers = !kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>();
ts->disable_dynamic_resource_limits = kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>();
ts->disable_user_pmu_access = !kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>();
/* Configure call smc on panic. */
*const_cast<volatile bool *>(std::addressof(g_call_smc_on_panic)) = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>();
}
/* Set Kernel Debugging. */
{
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
ts->disable_kernel_debugging = !GetConfigBool(smc::ConfigItem::DisableProgramVerification);
}
}
}
void KSystemControl::InitializePhase1() {
/* Enable KTargetSystem. */
KTargetSystem::SetInitialized();
/* Check KTargetSystem was configured correctly. */
{
/* Check IsDebugMode. */
{
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugMode() == GetConfigBool(smc::ConfigItem::IsDebugMode));
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugLoggingEnabled() == GetConfigBool(smc::ConfigItem::IsDebugMode));
}
/* Check Kernel Configuration. */
{
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDebugMemoryFillEnabled() == kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsUserExceptionHandlersEnabled() == kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled() == !kernel_config.Get<smc::KernelConfiguration::DisableDynamicResourceLimits>());
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsUserPmuAccessEnabled() == kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>());
MESOSPHERE_ABORT_UNLESS(g_call_smc_on_panic == kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>());
}
/* Check Kernel Debugging. */
{
MESOSPHERE_ABORT_UNLESS(KTargetSystem::IsKernelDebuggingEnabled() == GetConfigBool(smc::ConfigItem::DisableProgramVerification));
}
}
/* Initialize random and resource limit. */
{
u64 seed;
smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed));
KSystemControlBase::InitializePhase1Base(seed);
}
/* Configure the Kernel Carveout region. */
{
const auto carveout = KMemoryLayout::GetCarveoutRegionExtents();
MESOSPHERE_ABORT_UNLESS(carveout.GetEndAddress() != 0);
smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize());
}
}
void KSystemControl::InitializePhase2() {
/* Initialize the sleep manager. */
KSleepManager::Initialize();
/* Get the secure applet memory. */
const auto &secure_applet_memory = KMemoryLayout::GetSecureAppletMemoryRegion();
MESOSPHERE_INIT_ABORT_UNLESS(secure_applet_memory.GetSize() == SecureAppletMemorySize);
g_secure_applet_memory_address = secure_applet_memory.GetAddress();
/* Initialize KTrace (and potentially other init). */
KSystemControlBase::InitializePhase2();
}
u32 KSystemControl::GetCreateProcessMemoryPool() {
return KMemoryManager::Pool_Unsafe;
}
/* Privileged Access. */
void KSystemControl::ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
MESOSPHERE_ABORT_UNLESS(util::IsAligned(address, sizeof(u32)));
MESOSPHERE_ABORT_UNLESS(IsRegisterAccessibleToPrivileged(address));
MESOSPHERE_ABORT_UNLESS(smc::ReadWriteRegister(out, address, mask, value));
}
Result KSystemControl::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
R_UNLESS(AMS_LIKELY(util::IsAligned(address, sizeof(u32))), svc::ResultInvalidAddress());
R_UNLESS(AMS_LIKELY(IsRegisterAccessibleToUser(address)), svc::ResultInvalidAddress());
R_UNLESS(AMS_LIKELY(smc::ReadWriteRegister(out, address, mask, value)), svc::ResultInvalidAddress());
R_SUCCEED();
}
/* Randomness. */
void KSystemControl::GenerateRandom(u64 *dst, size_t count) {
MESOSPHERE_INIT_ABORT_UNLESS(count <= 7);
smc::GenerateRandomBytes(dst, count * sizeof(u64));
}
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(s_random_lock);
if (AMS_LIKELY(!s_uninitialized_random_generator)) {
return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); });
} else {
return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc);
}
}
u64 KSystemControl::GenerateRandomU64() {
KScopedInterruptDisable intr_disable;
KScopedSpinLock lk(s_random_lock);
if (AMS_LIKELY(!s_uninitialized_random_generator)) {
return s_random_generator.GenerateRandomU64();
} else {
return GenerateRandomU64FromSmc();
}
}
void KSystemControl::SleepSystem() {
MESOSPHERE_LOG("SleepSystem() was called\n");
KSleepManager::SleepSystem();
}
void KSystemControl::StopSystem(void *arg) {
if (arg != nullptr) {
/* Get the address of the legacy IRAM region. */
const KVirtualAddress iram_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 64_KB;
constexpr size_t RebootPayloadSize = 0x24000;
/* NOTE: Atmosphere extension; if we received an exception context from Panic(), */
/* generate a fatal error report using it. */
const KExceptionContext *e_ctx = static_cast<const KExceptionContext *>(arg);
auto *f_ctx = GetPointer<::ams::impl::FatalErrorContext>(iram_address + 0x2E000);
/* Clear the fatal context. */
std::memset(f_ctx, 0xCC, sizeof(*f_ctx));
/* Set metadata. */
f_ctx->magic = ::ams::impl::FatalErrorContext::Magic;
f_ctx->error_desc = ::ams::impl::FatalErrorContext::KernelPanicDesc;
f_ctx->program_id = (static_cast<u64>(util::FourCC<'M', 'E', 'S', 'O'>::Code) << 0) | (static_cast<u64>(util::FourCC<'S', 'P', 'H', 'R'>::Code) << 32);
/* Set identifier. */
f_ctx->report_identifier = KHardwareTimer::GetTick();
/* Set module base. */
f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress();
/* Set afsr1. */
f_ctx->afsr0 = GetVersionIdentifier();
f_ctx->afsr1 = static_cast<u32>(kern::GetTargetFirmware());
/* Set efsr/far. */
f_ctx->far = cpu::GetFarEl1();
f_ctx->esr = cpu::GetEsrEl1();
/* Copy registers. */
for (size_t i = 0; i < util::size(e_ctx->x); ++i) {
f_ctx->gprs[i] = e_ctx->x[i];
}
f_ctx->sp = e_ctx->sp;
f_ctx->pc = cpu::GetElrEl1();
/* Dump stack trace. */
{
uintptr_t fp = e_ctx->x[29];
for (f_ctx->stack_trace_size = 0; f_ctx->stack_trace_size < ::ams::impl::FatalErrorContext::MaxStackTrace && fp != 0 && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); ++(f_ctx->stack_trace_size)) {
struct {
uintptr_t fp;
uintptr_t lr;
} *stack_frame = reinterpret_cast<decltype(stack_frame)>(fp);
f_ctx->stack_trace[f_ctx->stack_trace_size] = stack_frame->lr;
fp = stack_frame->fp;
}
}
/* Dump stack. */
{
uintptr_t sp = e_ctx->sp;
for (f_ctx->stack_dump_size = 0; f_ctx->stack_dump_size < ::ams::impl::FatalErrorContext::MaxStackDumpSize && cpu::GetPhysicalAddressWritable(nullptr, sp + f_ctx->stack_dump_size, true); f_ctx->stack_dump_size += sizeof(u64)) {
*reinterpret_cast<u64 *>(f_ctx->stack_dump + f_ctx->stack_dump_size) = *reinterpret_cast<u64 *>(sp + f_ctx->stack_dump_size);
}
}
/* Try to get a payload address. */
const KMemoryRegion *cached_region = nullptr;
u64 reboot_payload_paddr = 0;
if (smc::TryGetConfig(std::addressof(reboot_payload_paddr), 1, smc::ConfigItem::ExospherePayloadAddress) && KMemoryLayout::IsLinearMappedPhysicalAddress(cached_region, reboot_payload_paddr, RebootPayloadSize)) {
/* If we have a payload, reboot to it. */
const KVirtualAddress reboot_payload = KMemoryLayout::GetLinearVirtualAddress(KPhysicalAddress(reboot_payload_paddr));
/* Clear IRAM. */
std::memset(GetVoidPointer(iram_address), 0xCC, RebootPayloadSize);
/* Copy the payload to iram. */
for (size_t i = 0; i < RebootPayloadSize / sizeof(u32); ++i) {
GetPointer<volatile u32>(iram_address)[i] = GetPointer<volatile u32>(reboot_payload)[i];
}
}
smc::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToFatalError);
}
if (g_call_smc_on_panic) {
/* If we should, instruct the secure monitor to display a panic screen. */
smc::ShowError(0xF00);
}
AMS_INFINITE_LOOP();
}
/* User access. */
void KSystemControl::CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) {
/* Invoke the secure monitor. */
return smc::CallSecureMonitorFromUser(args);
}
/* Secure Memory. */
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
if (pool == KMemoryManager::Pool_Applet) {
return 0;
} else {
return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
}
}
Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
/* Applet secure memory is handled separately. */
if (pool == KMemoryManager::Pool_Applet) {
R_RETURN(AllocateSecureMemoryForApplet(out, size));
}
/* Ensure the size is aligned. */
const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
R_UNLESS(util::IsAligned(size, alignment), svc::ResultInvalidSize());
/* Allocate the memory. */
const size_t num_pages = size / PageSize;
const KPhysicalAddress paddr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(num_pages, alignment / PageSize, KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool), KMemoryManager::Direction_FromFront));
R_UNLESS(paddr != Null<KPhysicalAddress>, svc::ResultOutOfMemory());
/* Ensure we don't leak references to the memory on error. */
ON_RESULT_FAILURE { Kernel::GetMemoryManager().Close(paddr, num_pages); };
/* If the memory isn't already secure, set it as secure. */
if (pool != KMemoryManager::Pool_System) {
/* Set the secure region. */
R_UNLESS(SetSecureRegion(paddr, size), svc::ResultOutOfMemory());
}
/* We succeeded. */
*out = KPageTable::GetHeapVirtualAddress(paddr);
R_SUCCEED();
}
void KSystemControl::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) {
/* Applet secure memory is handled separately. */
if (pool == KMemoryManager::Pool_Applet) {
return FreeSecureMemoryForApplet(address, size);
}
/* Ensure the size is aligned. */
const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), alignment));
MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, alignment));
/* If the memory isn't secure system, reset the secure region. */
if (pool != KMemoryManager::Pool_System) {
/* Check that the size being freed is the current secure region size. */
MESOSPHERE_ABORT_UNLESS(g_secure_region_size == size);
/* Get the physical address. */
const KPhysicalAddress paddr = KPageTable::GetHeapPhysicalAddress(address);
MESOSPHERE_ABORT_UNLESS(paddr != Null<KPhysicalAddress>);
/* Check that the memory being freed is the current secure region. */
MESOSPHERE_ABORT_UNLESS(paddr == g_secure_region_phys_addr);
/* Free the secure region. */
MESOSPHERE_ABORT_UNLESS(SetSecureRegion(paddr, 0));
}
/* Close the secure region's pages. */
Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
}
}

View File

@@ -0,0 +1,463 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_lps_driver.hpp"
#include "kern_k_sleep_manager.hpp"
#include "kern_bpmp_api.hpp"
#include "kern_atomics_registers.hpp"
#include "kern_ictlr_registers.hpp"
#include "kern_sema_registers.hpp"
namespace ams::kern::board::nintendo::nx::lps {
namespace {
constexpr inline int ChannelCount = 12;
constexpr inline TimeSpan ChannelTimeout = TimeSpan::FromSeconds(1);
constinit bool g_lps_init_done = false;
constinit bool g_bpmp_connected = false;
constinit bool g_bpmp_mail_initialized = false;
constinit KSpinLock g_bpmp_mrq_lock;
constinit KVirtualAddress g_evp_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_flow_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_prictlr_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_sema_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_atomics_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_clkrst_address = Null<KVirtualAddress>;
constinit KVirtualAddress g_pmc_address = Null<KVirtualAddress>;
constinit ChannelData g_channel_area[ChannelCount] = {};
constinit u32 g_csite_clk_source = 0;
ALWAYS_INLINE u32 Read(KVirtualAddress address) {
return *GetPointer<volatile u32>(address);
}
ALWAYS_INLINE void Write(KVirtualAddress address, u32 value) {
*GetPointer<volatile u32>(address) = value;
}
void InitializeDeviceVirtualAddresses() {
/* Retrieve randomized mappings. */
g_evp_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsExceptionVectors);
g_flow_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsFlowController);
g_prictlr_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsPrimaryICtlr);
g_sema_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsSemaphore);
g_atomics_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsAtomics);
g_clkrst_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsClkRst);
g_pmc_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_PowerManagementController);
}
/* NOTE: linux "do_cc4_init" */
void ConfigureCc3AndCc4() {
/* Configure CC4/CC3 as enabled with time threshold as 2 microseconds. */
Write(g_flow_address + FLOW_CTLR_CC4_HVC_CONTROL, (0x2 << 3) | 0x1);
/* Configure Retention with threshold 2 microseconds. */
Write(g_flow_address + FLOW_CTLR_CC4_RETENTION_CONTROL, (0x2 << 3));
/* Configure CC3/CC3 retry threshold as 2 microseconds. */
Write(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY, (0x2 << 3));
/* Read the retry register to ensure writes take. */
Read(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY);
}
constexpr bool IsValidMessageDataSize(int size) {
return 0 <= size && size < MessageDataSizeMax;
}
/* NOTE: linux "bpmp_valid_txfer" */
constexpr bool IsTransferValid(const void *ob, int ob_size, void *ib, int ib_size) {
return IsValidMessageDataSize(ob_size) && IsValidMessageDataSize(ib_size) && (ob_size == 0 || ob != nullptr) && (ib_size == 0 || ib != nullptr);
}
/* NOTE: linux "bpmp_ob_channel" */
int BpmpGetOutboundChannel() {
return GetCurrentCoreId();
}
/* NOTE: linux "bpmp_ch_sta" */
u32 BpmpGetChannelState(int channel) {
cpu::DataSynchronizationBarrier();
return Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) & CH_MASK(channel);
}
/* NOTE: linux "bpmp_master_free" */
bool BpmpIsMasterFree(int channel) {
return BpmpGetChannelState(channel) == MA_FREE(channel);
}
/* NOTE: linux "bpmp_master_acked" */
bool BpmpIsMasterAcked(int channel) {
return BpmpGetChannelState(channel) == MA_ACKD(channel);
}
/* NOTE: linux "bpmp_signal_slave" */
void BpmpSignalSlave(int channel) {
Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, CH_MASK(channel));
cpu::DataSynchronizationBarrier();
}
/* NOTE: linux "bpmp_free_master" */
void BpmpFreeMaster(int channel) {
/* Transition state from ack'd to free. */
Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, ((MA_ACKD(channel)) ^ (MA_FREE(channel))));
cpu::DataSynchronizationBarrier();
}
/* NOTE: linux "bpmp_ring_doorbell" */
void BpmpRingDoorbell() {
Write(g_prictlr_address + ICTLR_FIR_SET(INT_SHR_SEM_OUTBOX_IBF), FIR_BIT(INT_SHR_SEM_OUTBOX_IBF));
cpu::DataSynchronizationBarrier();
}
/* NOTE: linux "bpmp_wait_master_free" */
int BpmpWaitMasterFree(int channel) {
/* Check if the master is already freed. */
if (BpmpIsMasterFree(channel)) {
return 0;
}
/* Spin-poll for the master to be freed until timeout occurs. */
const auto start_tick = KHardwareTimer::GetTick();
const auto timeout = ams::svc::Tick(ChannelTimeout);
do {
if (BpmpIsMasterFree(channel)) {
return 0;
}
} while ((KHardwareTimer::GetTick() - start_tick) < timeout);
/* The master didn't become free. */
return -1;
}
/* NOTE: linux "bpmp_wait_ack" */
int BpmpWaitAck(int channel) {
/* Check if the master is already ACK'd. */
if (BpmpIsMasterAcked(channel)) {
return 0;
}
/* Spin-poll for the master to be ACK'd until timeout occurs. */
const auto start_tick = KHardwareTimer::GetTick();
const auto timeout = ams::svc::Tick(ChannelTimeout);
do {
if (BpmpIsMasterAcked(channel)) {
return 0;
}
} while ((KHardwareTimer::GetTick() - start_tick) < timeout);
/* The master didn't get ACK'd. */
return -1;
}
/* NOTE: linux "bpmp_write_ch" */
int BpmpWriteChannel(int channel, int mrq, int flags, const void *data, size_t data_size) {
/* Wait to be able to master the mailbox. */
if (int res = BpmpWaitMasterFree(channel); res != 0) {
return res;
}
/* Prepare the message. */
MailboxData *mb = g_channel_area[channel].ob;
mb->code = mrq;
mb->flags = flags;
if (data != nullptr) {
std::memcpy(mb->data, data, data_size);
}
/* Signal to slave that message is available. */
BpmpSignalSlave(channel);
return 0;
}
/* NOTE: linux "__bpmp_read_ch" */
int BpmpReadChannel(int channel, void *data, size_t data_size) {
/* Get the message. */
MailboxData *mb = g_channel_area[channel].ib;
/* Copy any return data. */
if (data != nullptr) {
std::memcpy(data, mb->data, data_size);
}
/* Free the channel. */
BpmpFreeMaster(channel);
/* Return result. */
return mb->code;
}
/* NOTE: linux "tegra_bpmp_send_receive_atomic" or "tegra_bpmp_send_receive". */
int BpmpSendAndReceive(int mrq, const void *ob, int ob_size, void *ib, int ib_size) {
/* Validate that the data transfer is valid. */
if (!IsTransferValid(ob, ob_size, ib, ib_size)) {
return -1;
}
/* Validate that the bpmp is connected. */
if (!g_bpmp_connected) {
return -1;
}
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Acquire exclusive access to send mrqs. */
KScopedSpinLock lk(g_bpmp_mrq_lock);
/* Send the message. */
int channel = BpmpGetOutboundChannel();
if (int res = BpmpWriteChannel(channel, mrq, BPMP_MSG_DO_ACK, ob, ob_size); res != 0) {
return res;
}
/* Send "doorbell" irq to the bpmp firmware. */
BpmpRingDoorbell();
/* Wait for the bpmp firmware to acknowledge our request. */
if (int res = BpmpWaitAck(channel); res != 0) {
return res;
}
/* Read the data the bpmp sent back. */
return BpmpReadChannel(channel, ib, ib_size);
}
/* NOTE: linux "tegra_bpmp_send" */
int BpmpSend(int mrq, const void *ob, int ob_size) {
/* Validate that the data transfer is valid. */
if (!IsTransferValid(ob, ob_size, nullptr, 0)) {
return -1;
}
/* Validate that the bpmp is connected. */
if (!g_bpmp_connected) {
return -1;
}
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Acquire exclusive access to send mrqs. */
KScopedSpinLock lk(g_bpmp_mrq_lock);
/* Send the message. */
int channel = BpmpGetOutboundChannel();
if (int res = BpmpWriteChannel(channel, mrq, 0, ob, ob_size); res != 0) {
return res;
}
/* Send "doorbell" irq to the bpmp firmware. */
BpmpRingDoorbell();
return 0;
}
/* NOTE: modified linux "tegra_bpmp_enable_suspend" */
int BpmpEnableSuspend(int mode, int flags) {
/* Prepare data for bpmp. */
const s32 data[] = { mode, flags };
/* Send the data. */
return BpmpSend(MRQ_ENABLE_SUSPEND, data, sizeof(data));
}
/* NOTE: linux "__bpmp_connect" */
int ConnectToBpmp() {
/* Check if we've already connected. */
if (g_bpmp_connected) {
return 0;
}
/* Verify that the resource semaphore state is set. */
if (Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) == 0) {
return -1;
}
/* Get the channels, which the bpmp firmware has configured in advance. */
{
const KVirtualAddress iram_virt_addr = KMemoryLayout::GetDeviceVirtualAddress (KMemoryRegionType_LegacyLpsIram);
const KPhysicalAddress iram_phys_addr = KMemoryLayout::GetDevicePhysicalAddress(KMemoryRegionType_LegacyLpsIram);
for (auto i = 0; i < ChannelCount; ++i) {
/* Trigger a get command for the desired channel. */
Write(g_atomics_address + ATOMICS_AP0_TRIGGER, TRIGGER_CMD_GET | (i << 16));
/* Retrieve the channel phys-addr-in-iram, and convert it to a kernel address. */
auto *ch = GetPointer<MailboxData>(iram_virt_addr + (Read(g_atomics_address + ATOMICS_AP0_RESULT(i)) - GetInteger(iram_phys_addr)));
/* Verify the channel isn't null. */
/* NOTE: This is an utterly nonsense check, as this would require the bpmp firmware to specify */
/* a phys-to-virt diff as an address. On 1.0.0, which had no ASLR, this was 0x8028C000. */
/* However, Nintendo has the check, and we'll preserve it to be faithful. */
if (ch == nullptr) {
return -1;
}
/* Set the channel in the channel area. */
g_channel_area[i].ib = ch;
g_channel_area[i].ob = ch;
}
}
/* Mark driver as connected to bpmp. */
g_bpmp_connected = true;
return 0;
}
/* NOTE: Modified linux "bpmp_mail_init" */
int InitializeBpmpMail() {
/* Check if we've already initialized. */
if (g_bpmp_mail_initialized) {
return 0;
}
/* Mark function as having been called. */
g_bpmp_mail_initialized = true;
/* Forward declare result/reply variables. */
int res, request = 0, reply = 0;
/* Try to connect to the bpmp. */
if (res = ConnectToBpmp(); res != 0) {
MESOSPHERE_LOG("bpmp: connect error returns %d\n", res);
return res;
}
/* Ensure that we can successfully ping the bpmp. */
request = 1;
if (res = BpmpSendAndReceive(MRQ_PING, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) {
MESOSPHERE_LOG("bpmp: MRQ_PING error returns %d with reply %d\n", res, reply);
return res;
}
/* Configure the PMIC. */
request = 1;
if (res = BpmpSendAndReceive(MRQ_CPU_PMIC_SELECT, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) {
MESOSPHERE_LOG("bpmp: MRQ_CPU_PMIC_SELECT for MAX77621 error returns %d with reply %d\n", res, reply);
return res;
}
return 0;
}
}
void Initialize() {
if (!g_lps_init_done) {
/* Get the addresses of the devices the driver needs. */
InitializeDeviceVirtualAddresses();
/* Configure CC3/CC4. */
ConfigureCc3AndCc4();
/* Initialize ccplex <-> bpmp mail. */
/* NOTE: Nintendo does not check that this call succeeds. */
InitializeBpmpMail();
g_lps_init_done = true;
}
}
Result EnableSuspend(bool enable) {
/* If we're not on core 0, there's nothing to do. */
R_SUCCEED_IF(GetCurrentCoreId() != 0);
/* If we're not enabling suspend, there's nothing to do. */
R_SUCCEED_IF(!enable);
/* Instruct BPMP to enable suspend-to-sc7. */
R_UNLESS(BpmpEnableSuspend(TEGRA_BPMP_PM_SC7, 0) == 0, svc::ResultInvalidState());
R_SUCCEED();
}
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg) {
/* Verify that we're allowed to perform suspension. */
MESOSPHERE_ABORT_UNLESS(g_lps_init_done);
MESOSPHERE_ABORT_UNLESS(GetCurrentCoreId() == 0);
/* Save the CSITE clock source. */
g_csite_clk_source = Read(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE);
/* Configure CSITE clock source as CLK_M. */
Write(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE, (0x6 << 29));
/* Clear the top bit of PMC_SCRATCH4. */
Write(g_pmc_address + APBDEV_PMC_SCRATCH4, Read(g_pmc_address + APBDEV_PMC_SCRATCH4) & 0x7FFFFFFF);
/* Write 1 to PMC_SCRATCH0. This will cause the bootrom to use the warmboot code-path. */
Write(g_pmc_address + APBDEV_PMC_SCRATCH0, 1);
/* Read PMC_SCRATCH0 to be sure our write takes. */
Read(g_pmc_address + APBDEV_PMC_SCRATCH0);
/* Invoke the sleep hander. */
KSleepManager::CpuSleepHandler(arg, entry, entry_arg);
/* Disable deep power down. */
Write(g_pmc_address + APBDEV_PMC_DPD_ENABLE, 0);
/* Restore the saved CSITE clock source. */
Write(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE, g_csite_clk_source);
/* Read the CSITE clock source to ensure our configuration takes. */
Read(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE);
/* Configure CC3/CC4. */
ConfigureCc3AndCc4();
}
void ResumeBpmpFirmware() {
/* Halt the bpmp. */
Write(g_flow_address + FLOW_CTLR_HALT_COP_EVENTS, (0x2 << 29));
/* Hold the bpmp in reset. */
Write(g_clkrst_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x2);
/* Read the saved bpmp entrypoint, and write it to the relevant exception vector. */
const u32 bpmp_entry = Read(g_pmc_address + APBDEV_PMC_SCRATCH39);
Write(g_evp_address + EVP_COP_RESET_VECTOR, bpmp_entry);
/* Verify that we can read back the address we wrote. */
while (Read(g_evp_address + EVP_COP_RESET_VECTOR) != bpmp_entry) {
/* ... */
}
/* Spin for 40 ticks, to give enough time for the bpmp to be reset. */
const auto start_tick = KHardwareTimer::GetTick();
do {
__asm__ __volatile__("" ::: "memory");
} while ((KHardwareTimer::GetTick() - start_tick) < 40);
/* Take the bpmp out of reset. */
Write(g_clkrst_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x2);
/* Resume the bpmp. */
Write(g_flow_address + FLOW_CTLR_HALT_COP_EVENTS, (0x0 << 29));
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere.hpp>
namespace ams::kern::board::nintendo::nx {
namespace lps {
void Initialize();
Result EnableSuspend(bool enable);
void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry, uintptr_t entry_arg);
void ResumeBpmpFirmware();
}
}

View File

@@ -0,0 +1,237 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_secure_monitor.hpp"
namespace ams::kern::board::nintendo::nx::smc {
namespace {
enum UserFunctionId : u32 {
UserFunctionId_SetConfig = 0xC3000401,
UserFunctionId_GetConfigUser = 0xC3000002,
UserFunctionId_GetResult = 0xC3000003,
UserFunctionId_GetResultData = 0xC3000404,
UserFunctionId_ModularExponentiate = 0xC3000E05,
UserFunctionId_GenerateRandomBytes = 0xC3000006,
UserFunctionId_GenerateAesKek = 0xC3000007,
UserFunctionId_LoadAesKey = 0xC3000008,
UserFunctionId_ComputeAes = 0xC3000009,
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
UserFunctionId_ComputeCmac = 0xC300040B,
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
};
enum FunctionId : u32 {
FunctionId_GetConfig = 0xC3000004,
FunctionId_GenerateRandomBytes = 0xC3000005,
FunctionId_ShowError = 0xC3000006,
FunctionId_ConfigureCarveout = 0xC3000007,
FunctionId_ReadWriteRegister = 0xC3000008,
/* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
FunctionId_SetConfig = 0xC3000409,
};
constexpr size_t GenerateRandomBytesSizeMax = sizeof(::ams::svc::lp64::SecureMonitorArguments) - sizeof(::ams::svc::lp64::SecureMonitorArguments{}.r[0]);
/* Global lock for generate random bytes. */
constinit KSpinLock g_generate_random_lock;
bool TryGetConfigImpl(u64 *out, size_t num_qwords, ConfigItem config_item) {
/* Create the arguments .*/
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GetConfig, static_cast<u32>(config_item) } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* If successful, copy the output. */
const bool success = static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
if (AMS_LIKELY(success)) {
for (size_t i = 0; i < num_qwords && i < 7; i++) {
out[i] = args.r[1 + i];
}
}
return success;
}
bool SetConfigImpl(ConfigItem config_item, u64 value) {
/* Create the arguments .*/
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_SetConfig, static_cast<u32>(config_item), 0, value } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* Return whether the call was successful. */
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
}
bool ReadWriteRegisterImpl(u32 *out, u64 address, u32 mask, u32 value) {
/* Create the arguments .*/
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ReadWriteRegister, address, mask, value } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* Unconditionally write the output. */
*out = static_cast<u32>(args.r[1]);
/* Return whether the call was successful. */
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
}
bool GenerateRandomBytesImpl(void *dst, size_t size) {
/* Create the arguments. */
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GenerateRandomBytes, size } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* If successful, copy the output. */
const bool success = static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
if (AMS_LIKELY(success)) {
std::memcpy(dst, std::addressof(args.r[1]), size);
}
return success;
}
bool ConfigureCarveoutImpl(size_t which, uintptr_t address, size_t size) {
/* Create the arguments .*/
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ConfigureCarveout, static_cast<u64>(which), static_cast<u64>(address), static_cast<u64>(size) } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* Return whether the call was successful. */
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
}
bool ShowErrorImpl(u32 color) {
/* Create the arguments .*/
ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ShowError, color } };
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_Supervisor>(args.r);
/* Return whether the call was successful. */
return static_cast<SmcResult>(args.r[0]) == SmcResult::Success;
}
void CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) {
/* Call into the secure monitor. */
::ams::kern::arch::arm64::smc::SecureMonitorCall<SmcId_User>(args->r);
}
}
/* SMC functionality needed for init. */
namespace init {
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
/* Ensure we successfully get the config. */
MESOSPHERE_INIT_ABORT_UNLESS(TryGetConfigImpl(out, num_qwords, config_item));
}
void GenerateRandomBytes(void *dst, size_t size) {
/* Check that the size is valid. */
MESOSPHERE_INIT_ABORT_UNLESS(0 < size && size <= GenerateRandomBytesSizeMax);
/* Ensure we successfully generate the random bytes. */
MESOSPHERE_INIT_ABORT_UNLESS(GenerateRandomBytesImpl(dst, size));
}
void ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) {
/* Ensure we successfully access the register. */
MESOSPHERE_INIT_ABORT_UNLESS(ReadWriteRegisterImpl(out, address, mask, value));
}
}
bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Get the config. */
return TryGetConfigImpl(out, num_qwords, config_item);
}
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
/* Ensure we successfully get the config. */
MESOSPHERE_ABORT_UNLESS(TryGetConfig(out, num_qwords, config_item));
}
bool SetConfig(ConfigItem config_item, u64 value) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Set the config. */
return SetConfigImpl(config_item, value);
}
bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Access the register. */
return ReadWriteRegisterImpl(out, address, mask, value);
}
void ConfigureCarveout(size_t which, uintptr_t address, size_t size) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Ensure that we successfully configure the carveout. */
MESOSPHERE_ABORT_UNLESS(ConfigureCarveoutImpl(which, address, size));
}
void GenerateRandomBytes(void *dst, size_t size) {
/* Check that the size is valid. */
MESOSPHERE_ABORT_UNLESS(0 < size && size <= GenerateRandomBytesSizeMax);
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Acquire the exclusive right to generate random bytes. */
KScopedSpinLock lk(g_generate_random_lock);
/* Ensure we successfully generate the random bytes. */
MESOSPHERE_ABORT_UNLESS(GenerateRandomBytesImpl(dst, size));
}
void ShowError(u32 color) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Ensure we successfully show the error. */
MESOSPHERE_ABORT_UNLESS(ShowErrorImpl(color));
}
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
/* Disable interrupts. */
KScopedInterruptDisable di;
/* Perform the call. */
CallSecureMonitorFromUserImpl(args);
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere.hpp>
#include <mesosphere/arch/arm64/kern_secure_monitor_base.hpp>
namespace ams::kern::board::nintendo::nx::smc {
/* Types. */
enum SmcId {
SmcId_User = 0,
SmcId_Supervisor = 1,
};
enum MemorySize {
MemorySize_4GB = 0,
MemorySize_6GB = 1,
MemorySize_8GB = 2,
};
enum MemoryArrangement {
MemoryArrangement_4GB = 0,
MemoryArrangement_4GBForAppletDev = 1,
MemoryArrangement_4GBForSystemDev = 2,
MemoryArrangement_6GB = 3,
MemoryArrangement_6GBForAppletDev = 4,
MemoryArrangement_8GB = 5,
};
enum class ConfigItem : u32 {
/* Standard config items. */
DisableProgramVerification = 1,
DramId = 2,
SecurityEngineIrqNumber = 3,
Version = 4,
HardwareType = 5,
IsRetail = 6,
IsRecoveryBoot = 7,
DeviceId = 8,
BootReason = 9,
MemoryMode = 10,
IsDebugMode = 11,
KernelConfiguration = 12,
IsChargerHiZModeEnabled = 13,
IsQuest = 14,
RegulatorType = 15,
DeviceUniqueKeyGeneration = 16,
Package2Hash = 17,
/* Extension config items for exosphere. */
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
};
enum class SmcResult {
Success = 0,
NotImplemented = 1,
InvalidArgument = 2,
InProgress = 3,
NoAsyncOperation = 4,
InvalidAsyncOperation = 5,
NotPermitted = 6,
};
struct KernelConfiguration {
using DebugFillMemory = util::BitPack32::Field<0, 1, bool>;
using EnableUserExceptionHandlers = util::BitPack32::Field<DebugFillMemory::Next, 1, bool>;
using EnableUserPmuAccess = util::BitPack32::Field<EnableUserExceptionHandlers::Next, 1, bool>;
using IncreaseThreadResourceLimit = util::BitPack32::Field<EnableUserPmuAccess::Next, 1, bool>;
using DisableDynamicResourceLimits = util::BitPack32::Field<IncreaseThreadResourceLimit::Next, 1, bool>;
using Reserved5 = util::BitPack32::Field<DisableDynamicResourceLimits::Next, 3, u32>;
using UseSecureMonitorPanicCall = util::BitPack32::Field<Reserved5::Next, 1, bool>;
using Reserved9 = util::BitPack32::Field<UseSecureMonitorPanicCall::Next, 7, u32>;
using MemorySize = util::BitPack32::Field<Reserved9::Next, 2, smc::MemorySize>;
};
enum UserRebootType {
UserRebootType_None = 0,
UserRebootType_ToRcm = 1,
UserRebootType_ToPayload = 2,
UserRebootType_ToFatalError = 3,
};
void GenerateRandomBytes(void *dst, size_t size);
bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
void ConfigureCarveout(size_t which, uintptr_t address, size_t size);
bool SetConfig(ConfigItem config_item, u64 value);
void ShowError(u32 color);
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
namespace init {
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
void GenerateRandomBytes(void *dst, size_t size);
void ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value);
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#define RES_SEMA_SHRD_SMP_STA 0x000
#define RES_SEMA_SHRD_SMP_SET 0x004
#define RES_SEMA_SHRD_SMP_CLR 0x008

View File

@@ -0,0 +1,27 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_secure_monitor.hpp"
namespace ams::kern::board::qemu::virt {
/* User access. */
void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
/* Invoke the secure monitor. */
return smc::CallSecureMonitorFromUser(args);
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <mesosphere.hpp>
#include "kern_secure_monitor.hpp"
namespace ams::kern::board::qemu::virt::smc {
namespace {
enum UserFunctionId : u32 {
UserFunctionId_SetConfig = 0xC3000401,
UserFunctionId_GetConfig = 0xC3000002,
UserFunctionId_GetResult = 0xC3000003,
UserFunctionId_GetResultData = 0xC3000404,
UserFunctionId_ModularExponentiate = 0xC3000E05,
UserFunctionId_GenerateRandomBytes = 0xC3000006,
UserFunctionId_GenerateAesKek = 0xC3000007,
UserFunctionId_LoadAesKey = 0xC3000008,
UserFunctionId_ComputeAes = 0xC3000009,
UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
UserFunctionId_ComputeCmac = 0xC300040B,
UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
UserFunctionId_LoadPreparedAesKey = 0xC3000011,
UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
};
}
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
MESOSPHERE_LOG("Received SMC [%p %p %p %p %p %p %p %p] from %s\n", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]), GetCurrentProcess().GetName());
switch (args->r[0]) {
case UserFunctionId_GetConfig:
{
switch (static_cast<ConfigItem>(args->r[1])) {
case ConfigItem::ExosphereApiVersion:
args->r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
(static_cast<u64>(13) << 32) |
(static_cast<u64>(GetTargetFirmware()) << 0);
break;
default:
MESOSPHERE_PANIC("Unhandled GetConfig\n");
}
args->r[0] = static_cast<u64>(SmcResult::Success);
}
break;
default:
MESOSPHERE_PANIC("Unhandled SMC [%p %p %p %p %p %p %p %p]", reinterpret_cast<void *>(args->r[0]), reinterpret_cast<void *>(args->r[1]), reinterpret_cast<void *>(args->r[2]), reinterpret_cast<void *>(args->r[3]), reinterpret_cast<void *>(args->r[4]), reinterpret_cast<void *>(args->r[5]), reinterpret_cast<void *>(args->r[6]), reinterpret_cast<void *>(args->r[7]));
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere.hpp>
namespace ams::kern::board::qemu::virt::smc {
enum class ConfigItem : u32 {
/* Standard config items. */
DisableProgramVerification = 1,
DramId = 2,
SecurityEngineIrqNumber = 3,
Version = 4,
HardwareType = 5,
IsRetail = 6,
IsRecoveryBoot = 7,
DeviceId = 8,
BootReason = 9,
MemoryMode = 10,
IsDebugMode = 11,
KernelConfiguration = 12,
IsChargerHiZModeEnabled = 13,
IsQuest = 14,
RegulatorType = 15,
DeviceUniqueKeyGeneration = 16,
Package2Hash = 17,
/* Extension config items for exosphere. */
ExosphereApiVersion = 65000,
ExosphereNeedsReboot = 65001,
ExosphereNeedsShutdown = 65002,
ExosphereGitCommitHash = 65003,
ExosphereHasRcmBugPatch = 65004,
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
ExosphereForceEnableUsb30 = 65010,
ExosphereSupportedHosVersion = 65011,
};
enum class SmcResult {
Success = 0,
NotImplemented = 1,
InvalidArgument = 2,
InProgress = 3,
NoAsyncOperation = 4,
InvalidAsyncOperation = 5,
NotPermitted = 6,
};
void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
}