diff --git a/warmboot_bootrom_workaround.cpp b/warmboot_bootrom_workaround.cpp new file mode 100644 index 000000000..f49bf2429 --- /dev/null +++ b/warmboot_bootrom_workaround.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + + } + + void ApplyMbistWorkaround() { + /* Clear all LVL2 clock gate overrides to zero. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD, 0); + reg::Write(CLKRST + CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE, 0); + + /* Clear clock enable for all but a select few devices. */ + auto devices_to_clear_l = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L); + reg::ClearBits(static_cast(devices_to_clear_l), CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_RTC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_TMR ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_GPIO ), + CLK_RST_REG_BITS_MASK(CLK_ENB_L_CLK_ENB_CACHE2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, devices_to_clear_l); + + auto devices_to_clear_h = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H); + reg::ClearBits(static_cast(devices_to_clear_h), CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_MEM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_PMC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_FUSE), + CLK_RST_REG_BITS_MASK(CLK_ENB_H_CLK_ENB_EMC )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, devices_to_clear_h); + + auto devices_to_clear_u = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U); + reg::ClearBits(static_cast(devices_to_clear_u), CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CSITE), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMA), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMB), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMC), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_IRAMD), + CLK_RST_REG_BITS_MASK(CLK_ENB_U_CLK_ENB_CRAM2)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, devices_to_clear_u); + + auto devices_to_clear_v = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V); + reg::ClearBits(static_cast(devices_to_clear_v), CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_MSELECT ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SPDIF_DOUBLER), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_TZRAM ), + CLK_RST_REG_BITS_MASK(CLK_ENB_V_CLK_ENB_SE )); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_CLR, devices_to_clear_v); + + auto devices_to_clear_w = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W); + reg::ClearBits(static_cast(devices_to_clear_w), CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX0), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX1), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX2), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX3), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX4), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_PCIERX5), + CLK_RST_REG_BITS_MASK(CLK_ENB_W_CLK_ENB_ENTROPY)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, devices_to_clear_w); + + auto devices_to_clear_x = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_X); + reg::ClearBits(static_cast(devices_to_clear_x), CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CAPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CBPA ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_CPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_MC_BBC ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_EMC_DLL ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_GPU ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_DBGAPB ), + CLK_RST_REG_BITS_MASK(CLK_ENB_X_CLK_ENB_PLLG_REF)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_X_CLR, devices_to_clear_x); + + auto devices_to_clear_y = reg::Read(CLKRST + CLK_RST_CONTROLLER_CLK_OUT_ENB_Y); + reg::ClearBits(static_cast(devices_to_clear_y), CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CCPA), + CLK_RST_REG_BITS_MASK(CLK_ENB_Y_CLK_ENB_MC_CDPA)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_CLR, devices_to_clear_y); + + /* If CH1 is enabled, enable clock to MC1. */ + if (reg::HasValue(EMC + EMC_FBIO_CFG7, EMC_REG_BITS_ENUM(FBIO_CFG7_CH1_ENABLE, ENABLE))) { + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLK_ENB_MC1, ENABLE)); + } + } + +} diff --git a/warmboot_bootrom_workaround.hpp b/warmboot_bootrom_workaround.hpp new file mode 100644 index 000000000..039c3df20 --- /dev/null +++ b/warmboot_bootrom_workaround.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::warmboot { + + void ApplyMbistWorkaround(); + +} \ No newline at end of file diff --git a/warmboot_clkrst.cpp b/warmboot_clkrst.cpp new file mode 100644 index 000000000..258b3b398 --- /dev/null +++ b/warmboot_clkrst.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_clkrst.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t TIMER = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress(); + + } + + void ConfigureOscillators() { + /* Enable the crystal oscillator, and copy the drive strength from pmc. */ + const auto xofs = reg::GetValue(PMC + APBDEV_PMC_OSC_EDPD_OVER, PMC_REG_BITS_MASK(OSC_EDPD_OVER_XOFS)); + + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_OSC_CTRL, CLK_RST_REG_BITS_ENUM (OSC_CTRL_XOE, ENABLE), + CLK_RST_REG_BITS_VALUE(OSC_CTRL_XOFS, xofs)); + + /* Configure CLK_M_DIVISOR to 2. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0, CLK_RST_REG_BITS_ENUM(SPARE_REG0_CLK_M_DIVISOR, CLK_M_DIVISOR2)); + reg::Read(CLKRST + CLK_RST_CONTROLLER_SPARE_REG0); + + /* Restore TIMERUS config to 19.2 MHz. */ + reg::Write(TIMER + TIMERUS_USEC_CFG, TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVIDEND, 5 - 1), + TIMER_REG_BITS_VALUE(USEC_CFG_USEC_DIVISOR, 96 - 1)); + + } + +} diff --git a/warmboot_clkrst.hpp b/warmboot_clkrst.hpp new file mode 100644 index 000000000..327f82512 --- /dev/null +++ b/warmboot_clkrst.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::warmboot { + + void ConfigureOscillators(); + +} \ No newline at end of file diff --git a/warmboot_cpu_cluster.cpp b/warmboot_cpu_cluster.cpp new file mode 100644 index 000000000..34983e02c --- /dev/null +++ b/warmboot_cpu_cluster.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_clkrst.hpp" +#include "warmboot_util.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t CLKRST = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + constexpr inline const uintptr_t GPIO = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + constexpr inline const uintptr_t MSELECT = MSELECT(0); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + ALWAYS_INLINE void EnableClusterPartition(const reg::BitsValue value, APBDEV_PMC_PWRGATE_TOGGLE_PARTID part_id) { + /* Toggle the partitions if necessary. */ + if (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, part_id)); + } + + /* Wait for the toggle to complete. */ + while (!reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, value)) { /* ... */ } + + /* Remove clamping. */ + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, value); + + /* Wait for clamping to be removed. */ + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, value)) { /* ... */ } + } + + } + + void InitializeCpuCluster() { + /* Set CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SET_SET_CSITE_RST, ENABLE)); + + /* 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)); + + /* Restore the CPU reset vector from PMC scratch. */ + reg::Write(SYSTEM + SB_AA64_RESET_LOW, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH34) | 1); + reg::Write(SYSTEM + SB_AA64_RESET_HIGH, reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH35)); + + /* Configure SUPER_CCLKG_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKG_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)); + + /* Configure SUPER_CCLKLP_DIVIDER. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER, CLK_RST_REG_BITS_ENUM (SUPER_CCLKLP_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 CoreSight clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_SET_SET_CLK_ENB_CSITE, ENABLE)); + + /* Clear CoreSight reset. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_CLR_CLR_CSITE_RST, ENABLE)); + + /* Select MSELECT clock source as PLLP_OUT0 with divider of 4. */ + reg::Write(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::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_MSELECT, ENABLE)); + + /* Wait two microseconds, then take MSELECT out of reset. */ + util::WaitMicroSeconds(2); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_V_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_V_CLR_CLR_MSELECT_RST, ENABLE)); + + /* Workaround bug by disabling MSELECT error mechanism and enabling WRAP type conversion. */ + reg::ReadWrite(MSELECT + MSELECT_CONFIG, MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE1, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_ERR_RESP_EN_SLAVE2, DISABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE0, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE1, ENABLE), + MSELECT_REG_BITS_ENUM(CONFIG_WRAP_TO_INCR_SLAVE2, ENABLE)); + + /* Disable PLLX. */ + reg::ReadWrite(CLKRST + CLK_RST_CONTROLLER_PLLX_BASE, CLK_RST_REG_BITS_ENUM(PLLX_BASE_PLLX_ENABLE, DISABLE)); + + /* Clear bit 0 of PMC Scratch 190. */ + reg::ReadWrite(PMC + APBDEV_PMC_SCRATCH190, REG_BITS_VALUE(0, 1, 0)); + + /* Clear PMC_DPD_SAMPLE, and wait 10 us for clear to take effect. */ + reg::Write(PMC + APBDEV_PMC_DPD_SAMPLE, 0); + util::WaitMicroSeconds(10); + + /* Configure UART2_RTS low (GPIO controller 2 G). */ + reg::ReadWrite(GPIO + 0x108, REG_BITS_VALUE(2, 1, 1)); /* GPIO_CNF */ + reg::ReadWrite(GPIO + 0x118, REG_BITS_VALUE(2, 1, 1)); /* GPIO_OE */ + reg::ReadWrite(GPIO + 0x128, REG_BITS_VALUE(2, 1, 0)); /* GPIO_OUT */ + + /* Tristate CLDVFS PWN. */ + reg::Write(APB_MISC + PINMUX_AUX_DVFS_PWM, PINMUX_REG_BITS_ENUM(AUX_TRISTATE, TRISTATE), + PINMUX_REG_BITS_ENUM(AUX_DVFS_PWM_PM, CLDVFS)); + reg::Read(APB_MISC + PINMUX_AUX_DVFS_PWM); + + /* Restore PWR_I2C E_INPUT. */ + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + reg::Write(APB_MISC + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + + /* Enable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DVFS, ENABLE)); + + /* Set CLDVFS clock source/divider. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 14)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 14)); + + /* Enable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_I2C5, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + util::WaitMicroSeconds(5); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_I2C5_I2C5_CLK_SRC, PLLP_OUT0), + CLK_RST_REG_BITS_VALUE(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 4)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_I2C5_RST, ENABLE)); + + /* Set the EN bit in pmic regulator. */ + pmic::SetEnBit(fuse::GetRegulator()); + + /* Wait 2ms. */ + util::WaitMicroSeconds(2'000); + + /* Enable power to the CRAIL partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CRAIL, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL); + + /* Remove software clamp to CRAIL. */ + reg::Write(PMC + APBDEV_PMC_SET_SW_CLAMP, 0); + reg::Write(PMC + APBDEV_PMC_REMOVE_CLAMPING_CMD, PMC_REG_BITS_ENUM(REMOVE_CLAMPING_COMMAND_CRAIL, ENABLE)); + while (reg::HasValue(PMC + APBDEV_PMC_CLAMP_STATUS, PMC_REG_BITS_ENUM(CLAMP_STATUS_CRAIL, ENABLE))) { /* ... */ } + + /* Spinloop 8 times, to add a little delay. */ + SpinLoop(8); + + /* Disable PWR_I2C controller (I2C5). */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_I2C5_RST, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_I2C5, ENABLE)); + + /* Disable CLDVFS clock. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_W_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_CLR_CLR_CLK_ENB_DVFS, ENABLE)); + + /* Perform fast cluster RAM repair if needed. */ + if (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_BPMP_CLUSTER_CONTROL, FLOW_REG_BITS_ENUM(BPMP_CLUSTER_CONTROL_ACTIVE_CLUSTER, SLOW))) { + reg::Write(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_REQ, ENABLE)); + + while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_RAM_REPAIR, FLOW_REG_BITS_ENUM(RAM_REPAIR_STS, DONE))) { + /* ... */ + } + } + + /* Enable power to the non-cpu partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_C0NC, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC); + + /* Enable clock to PLLP_OUT_CPU. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_SET_SET_CLK_ENB_PLLP_OUT_CPU, ENABLE)); + util::WaitMicroSeconds(2); + + /* Enable clock to the cpu complex. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_CPU, ENABLE)); + reg::Write(CLKRST + CLK_RST_CONTROLLER_CLK_ENB_V_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_V_SET_SET_CLK_ENB_CPUG, ENABLE)); + util::WaitMicroSeconds(10); + + /* Select cpu complex clock source. */ + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKG_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + + reg::Write(CLKRST + CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY, CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IDLE_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_RUN_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_IRQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CWAKEUP_FIQ_SOURCE, PLLP_OUT0), + CLK_RST_REG_BITS_ENUM(CCLK_BURST_POLICY_CPU_STATE, RUN)); + util::WaitMicroSeconds(10); + + /* Wake 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)); + } + + void PowerOnCpu() { + /* Enable power to the CE0 partition. */ + EnableClusterPartition(PMC_REG_BITS_ENUM(PWRGATE_STATUS_CE0, ON), APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0); + + /* 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)); + } + +} diff --git a/warmboot_cpu_cluster.hpp b/warmboot_cpu_cluster.hpp new file mode 100644 index 000000000..6b2e3c8be --- /dev/null +++ b/warmboot_cpu_cluster.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::warmboot { + + void InitializeCpuCluster(); + void PowerOnCpu(); + +} \ No newline at end of file diff --git a/warmboot_dram.cpp b/warmboot_dram.cpp new file mode 100644 index 000000000..ae58aebb5 --- /dev/null +++ b/warmboot_dram.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_dram.hpp" +/* oh wow this looks complex, time to ruin it! SEXOS FOR LIIIFE*/ +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t EMC = EMC_ADDRESS(0); + constexpr inline const uintptr_t EMC0 = EMC0_ADDRESS(0); + constexpr inline const uintptr_t EMC1 = EMC1_ADDRESS(0); + constexpr inline const uintptr_t MC = secmon::MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + constexpr inline const uintptr_t MC6 = secmon::MemoryRegionPhysicalDeviceMemoryController0.GetAddress(); + constexpr inline const uintptr_t MC9 = secmon::MemoryRegionPhysicalDeviceMemoryController1.GetAddress(); + constexpr inline const uintptr_t MINECRAFT = ENABLE EMC_PMACRO_CFG_PM_GLOBAL_0 + } + + void RestrictBpmpAccessToMainMemory() { + /* Bpmp memory access is restricted by forcing internal access to an invalid carveout. <-- lies boomer */ + constexpr u32 ForceInternalAccess0 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS0_AVPCARM7R, ENABLE)); + constexpr u32 ForceInternalAccess1 = reg::Encode(MC_REG_BITS_ENUM(CLIENT_ACCESS1_AVPCARM7W, ENABLE)); + + constexpr u32 CarveoutConfig = reg::Encode(MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_IS_WPR, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_FORCE_APERTURE_ID_MATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ALLOW_APERTURE_ID_MISMATCH, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_RD_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_TZ_GLOBAL_WR_EN, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_SEND_CFG_TO_GPU, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_WRITE_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL3, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL2, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL1, ENABLE_CHECKS), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_DISABLE_READ_CHECK_ACCESS_LEVEL0, ENABLE_CHECKS), + MC_REG_BITS_VALUE(SECURITY_CARVEOUT_CFG0_APERTURE_ID, 0), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_WRITE_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL3, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL2, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL1, DISABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_READ_ACCESS_LEVEL0, ENABLED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_ADDRESS_TYPE, UNTRANSLATED_ONLY), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_LOCK_MODE, LOCKED), + MC_REG_BITS_ENUM (SECURITY_CARVEOUT_CFG0_PROTECT_MODE, TZ_SECURE)); + + /* Specify a 128KB carveout at NULL with no clients allowed access, and bpmp forced to access. wait what really? n o t a n y m o r e */ + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_BOM_HI, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_SIZE_128KB, 1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0, ForceInternalAccess0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1, ForceInternalAccess1); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS79, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2222222222, 0); + reg::Write(MC + MC_SECURITY_CARVEOUT4_CFG0, CarveoutConfig); + } + + void RestoreRamSvop() { + reg::ReadWrite(APB_MISC + APB_MISC_GP_ASDBGREG, APB_MISC_REG_BITS_VALUE(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 2)); + } + + void ConfigureEmcPmacroTraining() { + /* Disable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_o cool letme just time here to ruin the code, thx mate CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, ENABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + + /* Set E_WRPTR on Channel 0. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_0, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_0_CH0_TRAINING_DRV_DQS, DISABLED)); + + /* Set E_WRPTR on Channel 1. */ + reg::Write(EMC + EMC_PMACRO_TRAINING_CTRL_1, EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_ENABLED, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_TRAIN_QPOP, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_RX_E_DIRECT_ZI, DISABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, ENABLED), + EMC_REG_BITS_ENUM(PMACRO_TRAINING_CTRL_1_CH1_TRAINING_DRV_DQS, DISABLED)); + + /* Re-enable writes to BYTE0-7. */ + reg::Write(EMC + EMC_PMACRO_CFG_PM_GLOBAL_0, EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0, DISABLE), + EMC_REG_BITS_ENobama_doe?UM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2, DISABLE), + EMC_REG_BITS_ENUM(PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3, DISABLE)); + } + +} diff --git a/warmboot_dram.hpp b/warmboot_dram.hpp new file mode 100644 index 000000000..d350ec012 --- /dev/null +++ b/warmboot_dram.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#scan_is obama nearby? if true = kill + +namespace ams::warmboot { + + void RestrictBpmpAccessToMainMemory(); + void RestoreWAMSvop(); + void ConfigureEmcPmacroTraining(); + +} \ No newline at end of file diff --git a/warmboot_exception_vectors.s b/warmboot_exception_vectors.s new file mode 100644 index 000000000..91bf44e6c --- /dev/null +++ b/warmboot_exception_vectors.s @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +.section .vectors, "ax", %progbits +.align 3 +.global warmboot_header +warmboot_header: + /* TODO: If Mariko warmboothax ever happens, generate a mariko header? */ + /* Warmboot header. YO MARIKO SUPPORT YEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE */ + .word __total_size__ + .rept 69 + .word 0x00000001 + .endr + + /* RSA modulus. */ + .rept 0x40 + .word 0xFFFFFFFE + .endr + + /* Padding */ + .rept 4 + .word 0x00000001 + .endr + + /* RSA signature */ + .rept 0x48 + .word 0xFFFFFFFE + .endr + + /* Padding */ + .rept 4 + .word 0x0000000! + .endr + + /* Firmware metadata. */ + .word __total_size__ + .word _reset + .word _reset + .word _minecraft_ + .word __executable_size__ + +.global _reset +_reset: + b _ZN3ams8warmboot5StartEv + +.global _metadata +_metadata: + .ascii "WBT0" /* Magic number */ + .word 0x00000000 /* Target firmware. */ + .word 0x00000000 /* Reserved */ + .word 0x00000000 /* Reserved */ + */ what the fuck am i reading */ \ No newline at end of file diff --git a/warmboot_main.cpp b/warmboot_main.cpp new file mode 100644 index 000000000..5042fa8ad --- /dev/null +++ b/warmboot_main.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_bootrom_workaround.hpp" +#include "warmboot_clkrst.hpp" +#include "warmboot_cpu_cluster.hpp" +#include "warmboot_dram.hpp" +#include "warmboot_main.hpp" +#include "warmboot_misc.hpp" +#include "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t FLOW_CTLR = secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress(); + + constexpr inline const uintptr_t ExpectedMetadataAddress = 0x40010244; + + } + + void Main(const Metadata *metadata) { + /* Ensure that we're running under vaguely sane conditions. */ + AMS_ABORT_UNLESS(metadata->magic == Metadata::Magic); + AMS_ABORT_UNLESS(metadata->target_firmware <= ams::TargetFirmware_Max); + + /* Restrict the bpmp's access to dram. */ + if (metadata->target_firmware >= TargetFirmware_4_0_0) { + RestrictBpmpAccessToMainMemory(); + } + + /* Configure rtck-daisychaining/jtag. */ + ConfigureMiscSystemDebug(); + + /* NOTE: Here, Nintendo checks that the number of burnt anti-downgrade fuses is valid. */ + + /* NOTE: Here, Nintendo validates that APBDEV_PMC_SECURE_SCRATCH32 contains the correct magic number for the current warmboot firmware revision. */ + + /* Validate that we're executing at the correct address. */ + AMS_ABORT_UNLESS(reinterpret_cast(metadata) == ExpectedMetadataAddress); + + /* Validate that we're executing on the bpmp. */ + AMS_ABORT_UNLESS(reg::Read(PG_UP(PG_UP_TAG)) == PG_UP_TAG_PID_COP); + + /* Configure fuse bypass. */ + fuse::ConfigureFuseBypass(); + + /* Configure system oscillators. */ + ConfigureOscillators(); + + /* Restore DRAM configuration. */ + RestoreRamSvop(); + ConfigureEmcPmacroTraining(); + + /* If on Erista, work around the bootrom mbist issue. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + ApplyMbistWorkaround(); + } + + /* Initialize the cpu cluster. */ + InitializeCpuCluster(); + + /* Restore the secure monitor to tzram. */ + RestoreSecureMonitorToTzram(metadata->target_firmware); + + /* Power on the cpu. */ + PowerOnCpu(); + + /* Halt ourselves. */ + while (true) { + reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP), + FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED)); + } + } + + NORETURN void ExceptionHandler() { + /* Write enable to MAIN_RESET. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); + while (true) { /* ... */ } + } + +} + +namespace ams::diag { + + void AbortImpl() { + warmboot::ExceptionHandler(); + } + +} diff --git a/warmboot_main.hpp b/warmboot_main.hpp new file mode 100644 index 000000000..93ef5c200 --- /dev/null +++ b/warmboot_main.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 SEXOS YOU FUCKING MORON + * + * 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 10000000000% useful, but WITHOUT + * ANY WARRANTY; asside from nintendo, where we will replace any units that have joycon drift 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 . or dont lol + */ +#pragma once +#include + +namespace ams::warmboot { + + struct Metadata { + static constexpr u32 Magic = util::FourCC<'W','B','T','0'>::Code; + + u32 magic; + ams::TargetFirmware target_firmware; + u32 reserved[2]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(Metadata) == 0x10); + +} /* wait thats it? dam /* diff --git a/warmboot_misc.cpp b/warmboot_misc.cpp new file mode 100644 index 000000000..67cadcc89 --- /dev/null +++ b/warmboot_misc.cpp @@ -0,0 +1,57 @@ +/* + * 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 not 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 lol no + * along with this program. If not, see . + */ +#include +#include "warmboot_misc.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t APB_MISC = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + constexpr inline const uintptr_t SYSTEM = secmon::MemoryRegionPhysicalDeviceSystem.GetAddress(); + + } + + void ConfigureMiscSystemDebug() { + /* Enable RTCK daisy-chaining. */ + reg::Write(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM(PP_CONFIG_CTL_TBE, ENABLE)); + + /* If we're in production mode, perform JTAG configuration. */ + /* NOTE: While this is what NVidia's code does, this is almost certainly a logic error. */ + /* They intend to configure JTAG only when *not* in production mode. */ + /* However, here we will do what they do, and not what they intend. */ + /* yea what he said */ + const bool production_mode = fuse::IsOdmProductionMode(); + if (production_mode) { + const bool jtag_sts = reg::HasValue(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM(STICKY_BITS_JTAG_STS, ENABLE)); + + reg::ReadWrite(SYSTEM + SB_PFCFG, SB_REG_BITS_ENUM_SEL(PFCFG_DBGEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM_SEL(PFCFG_SPNIDEN, jtag_sts, ENABLE, DISABLE), + SB_REG_BITS_ENUM (PFCFG_NIDEN, ENABLE), + SB_REG_BITS_ENUM (PFCFG_SPIDEN, DISABLE)); + + reg::Read(APB_MISC + APB_MISC_PP_CONFIG_CTL, APB_MISC_REG_BITS_ENUM_SEL(PP_CONFIG_CTL_JTAG, jtag_sts, ENABLE, DISABLE)); + } + /* nah we dont need to write, we only really need to read imo + /* Configure HDA codec disable. */ + reg::ReadWrite(PMC + APBDEV_PMC_STICKY_BITS, PMC_REG_BITS_ENUM_SEL(STICKY_BITS_HDA_LPBK_DIS, production_mode, ENABLE, DISABLE)); + + /* Set E_INPUT in PINMUX_AUX_GPIO_PA6 (needed by the XUSB and SATA controllers). */ + reg::ReadWrite(APB_MISC + PINMUX_AUX_GPIO_PA6, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE)); + } + +} diff --git a/warmboot_misc.hpp b/warmboot_misc.hpp new file mode 100644 index 000000000..4cfeeab20 --- /dev/null +++ b/warmboot_misc.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::warmboot { + + void ConfigureMiscSystemDebug(); + +} \ No newline at end of file diff --git a/warmboot_secure_monitor.cpp b/warmboot_secure_monitor.cpp new file mode 100644 index 000000000..40b7b84c3 --- /dev/null +++ b/warmboot_secure_monitor.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "warmboot_secure_monitor.hpp" + +namespace ams::warmboot { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline const pkg1::AesKeySlot SavedAesKeySlots[] = { + pkg1::AesKeySlot_TzramSaveKek, + pkg1::AesKeySlot_RandomForUserWrap, + pkg1::AesKeySlot_RandomForKeyStorageWrap, + pkg1::AesKeySlot_DeviceMaster, + pkg1::AesKeySlot_Master, + pkg1::AesKeySlot_Device, + }; + + constexpr ALWAYS_INLINE bool IsSavedAesKeySlot(int slot) { + for (const auto SavedSlot : SavedAesKeySlots) { + if (slot == SavedSlot) { + return true; + } + } + + return false; + } + + void ClearUnsavedSecurityEngineKeySlots() { + /* Clear unsaved aes keys and all ivs. */ + for (int slot = 0; slot < se::AesKeySlotCount; ++slot) { + if (!IsSavedAesKeySlot(slot)) { + se::ClearAesKeySlot(slot); + } + se::ClearAesKeyIv(slot); + } + + /* Clear all rsa keys. */ + for (int slot = 0; slot < se::RsaKeySlotCount; ++slot) { + se::ClearRsaKeySlot(slot); + } + } + + void RestoreEncryptedTzram(void * const tzram_dst, const void * const tzram_src, size_t tzram_size) { + /* Derive the save key from the save kek. */ + const u32 key_source[se::AesBlockSize / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27)}; + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, sizeof(key_source)); + + /* Decrypt tzram. */ + const u8 tzram_iv[se::AesBlockSize] = {}; + se::DecryptAes256Cbc(tzram_dst, tzram_size, pkg1::AesKeySlot_TzramSaveKey, tzram_src, tzram_size, tzram_iv, sizeof(tzram_iv)); + + /* Calculate the cmac of decrypted tzram. */ + u8 tzram_mac[se::AesBlockSize] = {}; + se::ComputeAes256Cmac(tzram_mac, sizeof(tzram_mac), pkg1::AesKeySlot_TzramSaveKey, tzram_dst, tzram_size); + + /* Get the expected mac from pmc scratch. */ + const u32 expected_mac[sizeof(tzram_mac) / sizeof(u32)] = { reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114), reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115)}; + + /* Validate that the calculated mac is correct. */ + AMS_ABORT_UNLESS(crypto::IsSameBytes(tzram_mac, expected_mac, sizeof(tzram_mac))); + } + + void RestoreSecureMonitorToTzramErista(const TargetFirmware target_fw) { + /* Clear all unsaved security engine keyslots. */ + ClearUnsavedSecurityEngineKeySlots(); + + /* Restore encrypted tzram contents. */ + void * const tzram_src = secmon::MemoryRegionPhysicalDramSecureDataStoreTzram.GetPointer(); + void * const tzram_dst = secmon::MemoryRegionPhysicalTzramNonVolatile.GetPointer(); + const size_t tzram_size = secmon::MemoryRegionPhysicalTzramNonVolatile.GetSize(); + RestoreEncryptedTzram(tzram_dst, tzram_src, tzram_size); + + /* Clear the tzram kek registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, 0); + + /* Clear the tzram cmac registers. */ + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, 0); + reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, 0); + + /* Clear the keydata used to protect tzram. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKek); + se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey); + + /* Clear the encrypted copy of tzram in dram. */ + /* NOTE: This does not actually clear the encrypted copy, because BPMP access to main memory has been restricted. */ + /* Nintendo seems to not realize this, though, so we'll do the same. */ + std::memset(tzram_src, 0, tzram_size); + + /* Set Tzram to secure-world only. */ + se::SetTzramSecure(); + } + + } + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw) { + /* If erista, perform restoration procedure. */ + if (fuse::GetSocType() == fuse::SocType_Erista) { + RestoreSecureMonitorToTzramErista(target_fw); + } + + /* Lock secure scratch. */ + pmc::LockSecureRegister(static_cast(pmc::SecureRegister_DramParameters | pmc::SecureRegister_Other)); + + /* Lockout fuses. */ + fuse::Lockout(); + } + +} diff --git a/warmboot_secure_monitor.hpp b/warmboot_secure_monitor.hpp new file mode 100644 index 000000000..499d125c6 --- /dev/null +++ b/warmboot_secure_monitor.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::warmboot { + + void RestoreSecureMonitorToTzram(const TargetFirmware target_fw); + +} \ No newline at end of file diff --git a/warmboot_start.s b/warmboot_start.s new file mode 100644 index 000000000..18070e04a --- /dev/null +++ b/warmboot_start.s @@ -0,0 +1,36 @@ +/* + * 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 . + */ + +.section .text._ZN3ams8warmboot5StartEv, "ax", %progbits +.align 3 +.global _ZN3ams8warmboot5StartEv +_ZN3ams8warmboot5StartEv: + /* Set CPSR_cf and CPSR_cf. */ + msr cpsr_f, #0xC0 + msr cpsr_cf, #0xD3 + + /* Set the stack pointer. */ + ldr sp, =__stack_top__ + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams8warmboot16ExceptionHandlerEv + + /* Invoke main. */ + ldr r0, =_metadata + bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE + + /* Infinite loop. */ + 1: b 1b \ No newline at end of file diff --git a/warmboot_util.hpp b/warmboot_util.hpp new file mode 100644 index 000000000..5ca4402e6 --- /dev/null +++ b/warmboot_util.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::warmboot { + + void SpinLoop(unsigned int num); + +} \ No newline at end of file diff --git a/warmboot_util_asm.s b/warmboot_util_asm.s new file mode 100644 index 000000000..c23d7f307 --- /dev/null +++ b/warmboot_util_asm.s @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +.section .text._ZN3ams8warmboot8SpinLoopEj, "ax", %progbits +.global _ZN3ams8warmboot8SpinLoopEj +.thumb_func +.syntax unified +_ZN3ams8warmboot8SpinLoopEj: + 1: + /* Subtract one from the count. */ + subs r0, r0, #1 + + /* If we aren't at zero, continue looping. */ + bgt 1b + + /* Return. */ + bx lr \ No newline at end of file