From 93fb060facf2fd311c0d549503303008f58a9078 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 2 May 2019 19:33:12 -0700 Subject: [PATCH] boot: Implement DetectBootReason --- stratosphere/boot/source/boot_boot_reason.cpp | 113 ++++++++++++++++++ stratosphere/boot/source/boot_functions.hpp | 3 + stratosphere/boot/source/boot_main.cpp | 3 +- stratosphere/boot/source/boot_pmic_driver.cpp | 63 ++++++++++ stratosphere/boot/source/boot_pmic_driver.hpp | 45 +++++++ stratosphere/boot/source/boot_rtc_driver.cpp | 41 +++++++ stratosphere/boot/source/boot_rtc_driver.hpp | 41 +++++++ stratosphere/boot/source/boot_spl_utils.cpp | 8 ++ stratosphere/spl/source/spl_types.hpp | 2 +- 9 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 stratosphere/boot/source/boot_boot_reason.cpp create mode 100644 stratosphere/boot/source/boot_pmic_driver.cpp create mode 100644 stratosphere/boot/source/boot_pmic_driver.hpp create mode 100644 stratosphere/boot/source/boot_rtc_driver.cpp create mode 100644 stratosphere/boot/source/boot_rtc_driver.hpp diff --git a/stratosphere/boot/source/boot_boot_reason.cpp b/stratosphere/boot/source/boot_boot_reason.cpp new file mode 100644 index 000000000..bbaf6c568 --- /dev/null +++ b/stratosphere/boot/source/boot_boot_reason.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2018-2019 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 "boot_functions.hpp" +#include "boot_pmic_driver.hpp" +#include "boot_rtc_driver.hpp" + +static u32 g_boot_reason = 0; +static bool g_detected_boot_reason = false; + +struct BootReasonValue { + union { + struct { + u8 power_intr; + u8 rtc_intr; + u8 nv_erc; + u8 boot_reason; + }; + u32 value; + }; +}; + +static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) { + if (power_intr & 0x08) { + return 2; + } + if (rtc_intr & 0x02) { + return 3; + } + if (power_intr & 0x80) { + return 1; + } + if (rtc_intr & 0x04) { + if (nv_erc != 0x80 && !Boot::IsRecoveryBoot()) { + return 4; + } + } + if ((nv_erc & 0x40) && ac_ok) { + return 1; + } + return 0; +} + +void Boot::DetectBootReason() { + u8 power_intr; + u8 rtc_intr; + u8 rtc_intr_m; + u8 nv_erc; + bool ac_ok; + + /* Get values from PMIC. */ + { + PmicDriver pmic_driver; + if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) { + std::abort(); + } + if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) { + std::abort(); + } + if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) { + std::abort(); + } + } + + /* Get values from RTC. */ + { + RtcDriver rtc_driver; + if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) { + std::abort(); + } + if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) { + std::abort(); + } + } + + /* Set global derived boot reason. */ + g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok); + + /* Set boot reason for SPL. */ + { + BootReasonValue boot_reason_value; + boot_reason_value.power_intr = power_intr; + boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m; + boot_reason_value.nv_erc = nv_erc; + boot_reason_value.boot_reason = g_boot_reason; + if (R_FAILED(splSetBootReason(boot_reason_value.value))) { + std::abort(); + } + } + + g_detected_boot_reason = true; +} + +u32 Boot::GetBootReason() { + if (!g_detected_boot_reason) { + std::abort(); + } + + return g_boot_reason; +} diff --git a/stratosphere/boot/source/boot_functions.hpp b/stratosphere/boot/source/boot_functions.hpp index 4430cc08a..1ae03612e 100644 --- a/stratosphere/boot/source/boot_functions.hpp +++ b/stratosphere/boot/source/boot_functions.hpp @@ -29,6 +29,7 @@ class Boot { static void ChangeGpioVoltageTo1_8v(); static void SetInitialGpioConfiguration(); static void CheckClock(); + static void DetectBootReason(); /* Power utilities. */ static void RebootSystem(); @@ -45,6 +46,8 @@ class Boot { /* SPL Utilities. */ static HardwareType GetHardwareType(); + static u32 GetBootReason(); + static bool IsRecoveryBoot(); /* I2C Utilities. */ static Result ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 114c5e939..274e25c8d 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -119,7 +119,8 @@ int main(int argc, char **argv) /* Check USB PLL/UTMIP clock. */ Boot::CheckClock(); - /* TODO: DetectBootReason(); */ + /* Talk to PMIC/RTC, set boot reason with SPL. */ + Boot::DetectBootReason(); /* TODO: ShowSplashScreen(); */ diff --git a/stratosphere/boot/source/boot_pmic_driver.cpp b/stratosphere/boot/source/boot_pmic_driver.cpp new file mode 100644 index 000000000..74482e1b0 --- /dev/null +++ b/stratosphere/boot/source/boot_pmic_driver.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2019 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 +#include "boot_functions.hpp" +#include "boot_pmic_driver.hpp" + +void PmicDriver::ShutdownSystem() { + if (R_FAILED(this->ShutdownSystem(false))) { + std::abort(); + } +} + +void PmicDriver::RebootSystem() { + if (R_FAILED(this->ShutdownSystem(true))) { + std::abort(); + } +} + +Result PmicDriver::GetAcOk(bool *out) { + u8 power_status; + Result rc = this->GetPowerStatus(&power_status); + if (R_FAILED(rc)) { + return rc; + } + + *out = (power_status & 0x02) != 0; + return ResultSuccess; +} + +Result PmicDriver::GetPowerIntr(u8 *out) { + const u8 addr = 0x0B; + return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); +} + +Result PmicDriver::GetPowerStatus(u8 *out) { + const u8 addr = 0x15; + return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); +} + +Result PmicDriver::GetNvErc(u8 *out) { + const u8 addr = 0x0C; + return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); +} + +Result PmicDriver::ShutdownSystem(bool reboot) { + /* TODO: Implement this. */ + std::abort(); +} \ No newline at end of file diff --git a/stratosphere/boot/source/boot_pmic_driver.hpp b/stratosphere/boot/source/boot_pmic_driver.hpp new file mode 100644 index 000000000..ec185fac6 --- /dev/null +++ b/stratosphere/boot/source/boot_pmic_driver.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2019 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 +#include + +#include "i2c_driver/i2c_api.hpp" + +class PmicDriver { + private: + I2cSessionImpl i2c_session; + public: + PmicDriver() { + I2cDriver::Initialize(); + I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic); + } + + ~PmicDriver() { + I2cDriver::CloseSession(this->i2c_session); + I2cDriver::Finalize(); + } + private: + Result GetPowerStatus(u8 *out); + Result ShutdownSystem(bool reboot); + public: + void ShutdownSystem(); + void RebootSystem(); + Result GetAcOk(bool *out); + Result GetPowerIntr(u8 *out); + Result GetNvErc(u8 *out); +}; \ No newline at end of file diff --git a/stratosphere/boot/source/boot_rtc_driver.cpp b/stratosphere/boot/source/boot_rtc_driver.cpp new file mode 100644 index 000000000..f142c655e --- /dev/null +++ b/stratosphere/boot/source/boot_rtc_driver.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2019 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 +#include "boot_functions.hpp" +#include "boot_rtc_driver.hpp" + +Result RtcDriver::ReadRtcRegister(u8 *out, u8 address) { + const u8 update_addr = 0x04; + const u8 update_val = 0x10; + Result rc = Boot::WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr)); + if (R_FAILED(rc)) { + return rc; + } + svcSleepThread(16'000'000ul); + return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address)); +} + +Result RtcDriver::GetRtcIntr(u8 *out) { + const u8 addr = 0x00; + return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); +} + +Result RtcDriver::GetRtcIntrM(u8 *out) { + const u8 addr = 0x01; + return this->ReadRtcRegister(out, addr); +} diff --git a/stratosphere/boot/source/boot_rtc_driver.hpp b/stratosphere/boot/source/boot_rtc_driver.hpp new file mode 100644 index 000000000..0fdafece6 --- /dev/null +++ b/stratosphere/boot/source/boot_rtc_driver.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2019 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 +#include + +#include "i2c_driver/i2c_api.hpp" + +class RtcDriver { + private: + I2cSessionImpl i2c_session; + public: + RtcDriver() { + I2cDriver::Initialize(); + I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc); + } + + ~RtcDriver() { + I2cDriver::CloseSession(this->i2c_session); + I2cDriver::Finalize(); + } + private: + Result ReadRtcRegister(u8 *out, u8 address); + public: + Result GetRtcIntr(u8 *out); + Result GetRtcIntrM(u8 *out); +}; \ No newline at end of file diff --git a/stratosphere/boot/source/boot_spl_utils.cpp b/stratosphere/boot/source/boot_spl_utils.cpp index 2602deb3c..79bb9db4f 100644 --- a/stratosphere/boot/source/boot_spl_utils.cpp +++ b/stratosphere/boot/source/boot_spl_utils.cpp @@ -23,3 +23,11 @@ HardwareType Boot::GetHardwareType() { } return static_cast(out_val); } + +bool Boot::IsRecoveryBoot() { + u64 val = 0; + if (R_FAILED(splGetConfig(SplConfigItem_IsRecoveryBoot, &val))) { + std::abort(); + } + return val != 0; +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index c4cebbe2f..dc96bc9d6 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -65,7 +65,7 @@ struct AsyncOperationKey { struct BootReasonValue { u8 power_intr; u8 rtc_intr; - u8 _0x3; + u8 nv_erc; u8 boot_reason; }; static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");