fusee_cpp: implement cpu startup

This commit is contained in:
Michael Scire 2021-09-01 18:10:48 -07:00 committed by SciresM
parent 648ad51056
commit 40e2d4bbe6
12 changed files with 282 additions and 19 deletions

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "fusee_cpu.hpp"
namespace ams::nxboot {
namespace {
constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress();
constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
constexpr inline const uintptr_t FLOW = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress();
constexpr inline const uintptr_t EVP = secmon::MemoryRegionPhysicalDeviceExceptionVectors.GetAddress();
constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress();
bool IsPartitionPowered(u32 mask) {
return (reg::Read(PMC + APBDEV_PMC_PWRGATE_STATUS) & mask) == mask;
}
void PowerOnPartition(u32 status_mask, u32 toggle_mask) {
/* Check if the partition is already powered on. */
if (IsPartitionPowered(status_mask)) {
return;
}
/* Wait for PWRGATE_TOGGLE to be idle. */
auto timeout = 5000;
while (true) {
if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) {
break;
}
util::WaitMicroSeconds(1);
if ((--timeout) < 0) {
return;
}
}
/* Toggle on the desired partition. */
reg::SetField(toggle_mask, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, ENABLE));
reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, toggle_mask);
/* Wait for the partition to be powered. */
timeout = 5000;
while (true) {
if (IsPartitionPowered(status_mask)) {
break;
}
util::WaitMicroSeconds(1);
if ((--timeout) < 0) {
return;
}
}
}
}
void SetupCpu(uintptr_t entrypoint) {
/* Set ACTIVE_CLUSTER to FAST. */
reg::ReadWrite(FLOW + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, FAST));
/* Enable VDD_CPU. */
pmic::EnableVddCpu(fuse::GetRegulator());
/* Enable clock to the cpu. */
{
/* Initialize PllX */
if (!reg::HasValue(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_ENABLE, ENABLE))) {
/* Disable IDDQ. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_MISC3, CLK_RST_REG_BITS_VALUE(PLLX_MISC3_PLLX_IDDQ, 0));
/* Wait two microseconds. */
util::WaitMicroSeconds(2);
/* Configure PLLX dividers. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, 0x80404E02);
reg::Write(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, 0x00404E02);
/* Set PLLX_LOCK_ENABLE. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_MISC, CLK_RST_REG_BITS_ENUM(PLLX_MISC_PLLX_LOCK_ENABLE, ENABLE));
/* Enable PLLX. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, 0x40404E02);
}
/* Wait for PLLX to be locked. */
while (!reg::HasValue(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_LOCK, LOCK))) {
/* ... */
}
/* Select MSELECT clock source as PLLP_OUT0 with divider of 4. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_MSELECT_MSELECT_CLK_SRC, PLLP_OUT0),
CLK_RST_REG_BITS_VALUE(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 6));
/* Enable clock to MSELECT. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_V_CLK_ENB_MSELECT, ENABLE));
/* Configure CCLK_BURST_POLICY. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLK_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLX_OUT0_LJ),
CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLX_OUT0_LJ),
CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLX_OUT0_LJ),
CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLX_OUT0_LJ),
CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN));
/* Configure SUPER_CCLK_DIVIDER. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_ENB, ENABLE),
CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_FIQ, NO_IMPACT),
CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_FIQ, NO_IMPACT),
CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_COP_IRQ, NO_IMPACT),
CLK_RST_REG_BITS_ENUM (SUPER_CCLK_DIVIDER_SUPER_CDIV_DIS_FROM_CPU_IRQ, NO_IMPACT),
CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVIDEND, 0),
CLK_RST_REG_BITS_VALUE(SUPER_CCLK_DIVIDER_SUPER_CDIV_DIVISOR, 0));
/* Enable CPUG. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_CPUG, ENABLE));
}
/* Enable coresight. */
clkrst::EnableCsiteClock();
/* Restore PROD setting to CPU_SOFTRST_CTRL2 by clearing CAR2PMC_CPU_ACK_WIDTH. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2, CLK_RST_REG_BITS_VALUE(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0));
/* Power on cpu rails. */
{
PowerOnPartition(reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, ON)), reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_PARTID, CRAIL)));
PowerOnPartition(reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_STATUS_C0NC, ON)), reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_PARTID, C0NC)));
PowerOnPartition(reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CE0, ON)), reg::EncodeValue(PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_PARTID, CE0)));
}
/* Do RAM Repair. */
{
reg::Write(FLOW + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_REQ, ENABLE));
while (!reg::HasValue(FLOW + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_STS, DONE))) {
/* ... */
}
}
/* Configure CPU reset vector. */
reg::Write(EVP + EVP_CPU_RESET_VECTOR, 0);
reg::Write(SYSTEM + SB_AA64_RESET_LOW, entrypoint | 0x1);
reg::Write(SYSTEM + SB_AA64_RESET_HIGH, 0);
reg::Write(SYSTEM + SB_CSR, SB_REG_BITS_ENUM(CSR_NS_RST_VEC_WR_DIS, DISABLE));
reg::Read(SYSTEM + SB_CSR);
}
void StartCpu() {
/* NOTE: Here nintendo sets CPU_STRICT_TZ_APERTURE_CHECK, which we will not set. */
/* Clear MSELECT reset. */
reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_REG_BITS_ENUM(RST_DEVICES_V_SWR_MSELECT_RST, DISABLE));
/* Take non-cpu out of reset. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, ENABLE));
/* Clear cpu reset. */
reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CPURESET0, ENABLE),
CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET0, ENABLE),
CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_PRESETDBG, ENABLE),
CLK_RST_REG_BITS_ENUM(RST_CPUG_CMPLX_CLR_CLR_L2RESET, ENABLE));
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <exosphere.hpp>
namespace ams::nxboot {
void SetupCpu(uintptr_t entrypoint);
void StartCpu();
}

View file

@ -21,9 +21,11 @@
#include "fusee_ini.hpp"
#include "fusee_emummc.hpp"
#include "fusee_mmc.hpp"
#include "fusee_cpu.hpp"
#include "fusee_fatal.hpp"
#include "fusee_package2.hpp"
#include "fusee_malloc.hpp"
#include "fusee_secmon_sync.hpp"
#include "fs/fusee_fs_api.hpp"
namespace ams::nxboot {
@ -736,6 +738,15 @@ namespace ams::nxboot {
std::memcpy(mariko_fatal_dst, GetSecondaryArchive().mariko_fatal, sizeof(GetSecondaryArchive().mariko_fatal));
}
}
/* Setup the CPU to boot exosphere. */
SetupCpu(reinterpret_cast<uintptr_t>(exosphere_dst));
/* Set our bootloader state. */
SetBootloaderState(pkg1::BootloaderState_LoadedBootConfig);
/* Ensure that the CPU will see consistent data. */
hw::FlushEntireDataCache();
}
}
@ -770,8 +781,11 @@ namespace ams::nxboot {
/* Setup exosphere. */
ConfigureExosphere(soc_type, target_firmware, emummc_enabled);
/* TODO: Start CPU. */
/* Start CPU. */
/* NOTE: Security Engine unusable past this point. */
StartCpu();
WaitSecureMonitorState(pkg1::SecureMonitorState_Initialized);
/* TODO: Build modified package2. */
WaitForReboot();