/* * 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 <exosphere.hpp> #include "secmon_cpu_context.hpp" #include "secmon_page_mapper.hpp" #include "secmon_mariko_fatal_error.hpp" #include "secmon_user_power_management.hpp" #include "rebootstub_bin.h" namespace ams::secmon { namespace { constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); constexpr inline const u32 RebootStubPhysicalAddress = MemoryRegionPhysicalIramRebootStub.GetAddress(); enum RebootStubAction { RebootStubAction_ShutDown = 0, RebootStubAction_JumpToPayload = 1, }; NORETURN void PerformPmcReboot() { /* Write MAIN_RST. */ reg::Write(PMC + APBDEV_PMC_CNTRL, 0x10); while (true) { /* ... */ } } void LoadRebootStub(u32 action) { /* Configure the bootrom to boot to warmboot payload. */ reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x1); /* Patch the bootrom to perform an SVC immediately after the second spare write. */ reg::Write(PMC + APBDEV_PMC_SCRATCH45, 0x2E38DFFF); reg::Write(PMC + APBDEV_PMC_SCRATCH46, 0x6001DC28); /* Patch the bootrom to jump to the reboot stub we'll prepare in iram on SVC. */ reg::Write(PMC + APBDEV_PMC_SCRATCH33, RebootStubPhysicalAddress); reg::Write(PMC + APBDEV_PMC_SCRATCH40, 0x6000F208); { /* Map the iram page. */ AtmosphereIramPageMapper mapper(RebootStubPhysicalAddress); AMS_ABORT_UNLESS(mapper.Map()); /* Copy the reboot stub. */ AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress, rebootstub_bin, rebootstub_bin_size)); /* Set the reboot type. */ AMS_ABORT_UNLESS(mapper.CopyToMapping(RebootStubPhysicalAddress + 4, std::addressof(action), sizeof(action))); } } } void PerformUserRebootByPmic() { /* Ensure that i2c-5 is usable for communicating with the pmic. */ clkrst::EnableI2c5Clock(); i2c::Initialize(i2c::Port_5); /* Reboot. */ pmic::ShutdownSystem(true); } void PerformUserRebootToRcm() { /* Configure the bootrom to boot to rcm. */ reg::Write(PMC + APBDEV_PMC_SCRATCH0, 0x2); /* Reboot. */ PerformPmcReboot(); } void PerformUserRebootToPayload() { /* Load our reboot stub to iram. */ LoadRebootStub(RebootStubAction_JumpToPayload); /* Reboot. */ PerformPmcReboot(); } void PerformUserRebootToFatalError() { if (fuse::GetSocType() == fuse::SocType_Erista) { /* On Erista, we reboot to fatal error by jumping to fusee primary's handler. */ return PerformUserRebootToPayload(); } else /* if (fuse::GetSocType() == fuse::SocType_Mariko) */ { /* Call the fatal error handler. */ HandleMarikoFatalErrorInterrupt(); /* We should never get to this point. */ AMS_ABORT("Returned from Mariko Fatal handler?\n"); } } void PerformUserShutDown() { if (fuse::GetSocType() == fuse::SocType_Mariko) { /* Ensure that i2c-5 is usable for communicating with the pmic. */ clkrst::EnableI2c5Clock(); i2c::Initialize(i2c::Port_5); /* On Mariko shutdown via pmic. */ pmic::ShutdownSystem(false); } else /* if (fuse::GetSocType() == fuse::SocType_Erista) */ { /* Load our reboot stub to iram. */ LoadRebootStub(RebootStubAction_ShutDown); /* Reboot. */ PerformPmcReboot(); } } }