diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 773feb347..ea37f82d5 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -32,14 +32,14 @@ namespace ams::secmon { void Main() { /* Set library register addresses. */ - /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ + actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); i2c::SetRegisterAddress(i2c::Port_5, MemoryRegionVirtualDeviceI2c5.GetAddress()); - /* pinmux::SetRegisterAddress(); */ + pinmux::SetRegisterAddress(MemoryRegionVirtualDeviceApbMisc.GetAddress(), MemoryRegionVirtualDeviceGpio.GetAddress()); pmc::SetRegisterAddress(MemoryRegionVirtualDevicePmc.GetAddress()); se::SetRegisterAddress(MemoryRegionVirtualDeviceSecurityEngine.GetAddress()); uart::SetRegisterAddress(MemoryRegionVirtualDeviceUart.GetAddress()); diff --git a/exosphere2/program/source/secmon_map.cpp b/exosphere2/program/source/secmon_map.cpp index a2c1d0088..229bd121b 100644 --- a/exosphere2/program/source/secmon_map.cpp +++ b/exosphere2/program/source/secmon_map.cpp @@ -28,6 +28,17 @@ namespace ams::secmon { InvalidateL3Entries(l3, boot_code, boot_code_size); } + constexpr void UnmapTzramImpl(u64 *l1, u64 *l2, u64 *l3) { + /* Unmap the L3 entries corresponding to tzram. */ + InvalidateL3Entries(l3, MemoryRegionPhysicalTzram.GetAddress(), MemoryRegionPhysicalTzram.GetSize()); + + /* Unmap the L2 entries corresponding to those L3 entries. */ + InvalidateL2Entries(l2, MemoryRegionPhysicalTzramL2.GetAddress(), MemoryRegionPhysicalTzramL2.GetSize()); + + /* Unmap the L1 entry corresponding to to those L2 entries. */ + InvalidateL1Entries(l1, MemoryRegionPhysical.GetAddress(), MemoryRegionPhysical.GetSize()); + } + } void UnmapBootCode() { @@ -49,4 +60,16 @@ namespace ams::secmon { secmon::EnsureMappingConsistency(); } + void UnmapTzram() { + /* Get the tables. */ + u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer(); + u64 * const l2_l3 = MemoryRegionVirtualTzramL2L3PageTable.GetPointer(); + + /* Unmap. */ + UnmapTzramImpl(l1, l2_l3, l2_l3); + + /* Ensure the mappings are consistent. */ + secmon::EnsureMappingConsistency(); + } + } diff --git a/exosphere2/program/source/secmon_map.hpp b/exosphere2/program/source/secmon_map.hpp index 9d0aac678..aa8c7f32e 100644 --- a/exosphere2/program/source/secmon_map.hpp +++ b/exosphere2/program/source/secmon_map.hpp @@ -18,6 +18,6 @@ namespace ams::secmon { - /* TODO */ + void UnmapTzram(); } \ No newline at end of file diff --git a/exosphere2/program/source/secmon_setup.cpp b/exosphere2/program/source/secmon_setup.cpp index fa791e079..f6fc4a84f 100644 --- a/exosphere2/program/source/secmon_setup.cpp +++ b/exosphere2/program/source/secmon_setup.cpp @@ -16,9 +16,12 @@ #include #include "secmon_setup.hpp" #include "secmon_error.hpp" +#include "secmon_map.hpp" #include "secmon_cpu_context.hpp" #include "secmon_interrupt_handler.hpp" #include "secmon_misc.hpp" +#include "smc/secmon_smc_power_management.hpp" +#include "smc/secmon_smc_se_lock.hpp" namespace ams::secmon { @@ -887,11 +890,58 @@ namespace ams::secmon { reg::Read (MC + MC_SMMU_TLB_CONFIG); } + void ValidateResetExpected() { + /* We're coming out of reset, so check that we expected to come out of reset. */ + if (!IsResetExpected()) { + secmon::SetError(pkg1::ErrorInfo_UnexpectedReset); + AMS_ABORT("unexpected reset"); + } + SetResetExpected(false); + } + void ActmonInterruptHandler() { SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt); AMS_ABORT("actmon observed bpmp wakeup"); } + void ExitChargerHiZMode() { + /* Setup I2c-1. */ + pinmux::SetupI2c1(); + clkrst::EnableI2c1Clock(); + + /* Initialize I2c-1. */ + i2c::Initialize(i2c::Port_1); + + /* Exit Hi-Z mode. */ + charger::ExitHiZMode(); + + /* Disable clock to I2c-1. */ + clkrst::DisableI2c1Clock(); + } + + bool IsExitLp0() { + return reg::Read(MC + MC_SECURITY_CFG3) == 0; + } + + void LogExitLp0() { + /* NOTE: Nintendo only does this on dev, but we will always do it. */ + if (true /* !pkg1::IsProduction() */) { + log::Initialize(); + log::SendText("OHAYO\n", 6); + log::Flush(); + } + } + + void SetupForLp0Exit() { + /* Exit HiZ mode in charger, if we need to. */ + if (smc::IsChargerHiZModeEnabled()) { + ExitChargerHiZMode(); + } + + /* Unlock the security engine. */ + secmon::smc::UnlockSecurityEngine(); + } + } void Setup1() { @@ -908,6 +958,14 @@ namespace ams::secmon { gic::InitializeCommon(); } + void Setup1ForWarmboot() { + /* Initialize the security engine. */ + se::Initialize(); + + /* Initialize the gic. */ + gic::InitializeCommon(); + } + void SaveSecurityEngineAesKeySlotTestVector() { GenerateSecurityEngineAesKeySlotTestVector(g_se_aes_key_slot_test_vector, sizeof(g_se_aes_key_slot_test_vector)); } @@ -1007,7 +1065,36 @@ namespace ams::secmon { } void SetupSocSecurityWarmboot() { - /* ... */ + /* Check that we're allowed to continue. */ + ValidateResetExpected(); + + /* Unmap the tzram identity mapping. */ + UnmapTzram(); + + /* If we're exiting LP0, there's a little more work for us to do. */ + if (IsExitLp0()) { + /* Log that we're exiting LP0. */ + LogExitLp0(); + + /* Perform initial setup. */ + Setup1ForWarmboot(); + + /* Setup the Soc security. */ + SetupSocSecurity(); + + /* Set the PMC and MC as secure-only. */ + SetupPmcAndMcSecure(); + + /* Perform Lp0-exit specific init. */ + SetupForLp0Exit(); + + /* Setup the Soc protections. */ + SetupSocProtections(); + } + + /* Perform remaining CPU initialization. */ + SetupCpuCoreContext(); + SetupCpuSErrorDebug(); } void SetupSocProtections() { diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 7434bf217..a6f664957 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -19,6 +19,12 @@ namespace ams::secmon::smc { + namespace { + + constinit bool g_charger_hi_z_mode_enabled = false; + + } + SmcResult SmcPowerOffCpu(const SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; @@ -34,4 +40,12 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + bool IsChargerHiZModeEnabled() { + return g_charger_hi_z_mode_enabled; + } + + void SetChargerHiZModeEnabled(bool en) { + g_charger_hi_z_mode_enabled = en; + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.hpp b/exosphere2/program/source/smc/secmon_smc_power_management.hpp index 7b67d6e78..bc6f45229 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.hpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.hpp @@ -24,4 +24,7 @@ namespace ams::secmon::smc { SmcResult SmcSuspendCpu(const SmcArguments &args); + bool IsChargerHiZModeEnabled(); + void SetChargerHiZModeEnabled(bool en); + } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp new file mode 100644 index 000000000..d748ce70a --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp @@ -0,0 +1,41 @@ +/* + * 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 "../secmon_error.hpp" +#include "secmon_smc_se_lock.hpp" + +namespace ams::secmon::smc { + + namespace { + + constinit std::atomic_bool g_is_locked = false; + + } + + bool TryLockSecurityEngine() { + bool value = false; + return g_is_locked.compare_exchange_strong(value, true); + } + + void UnlockSecurityEngine() { + g_is_locked = false; + } + + bool IsSecurityEngineLocked() { + return g_is_locked; + } + +} diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.hpp b/exosphere2/program/source/smc/secmon_smc_se_lock.hpp new file mode 100644 index 000000000..db8e62cf0 --- /dev/null +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.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 +#include "secmon_smc_common.hpp" + +namespace ams::secmon::smc { + + bool TryLockSecurityEngine(); + void UnlockSecurityEngine(); + bool IsSecurityEngineLocked(); + +} diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index c6468ab62..e93e8b8fa 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libexosphere/include/exosphere/charger.hpp b/libraries/libexosphere/include/exosphere/charger.hpp new file mode 100644 index 000000000..7241f3ab7 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/charger.hpp @@ -0,0 +1,25 @@ +/* + * 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::charger { + + bool IsHiZMode(); + void EnterHiZMode(); + void ExitHiZMode(); + +} diff --git a/libraries/libexosphere/include/exosphere/clkrst.hpp b/libraries/libexosphere/include/exosphere/clkrst.hpp index 2f0143977..e4b31740d 100644 --- a/libraries/libexosphere/include/exosphere/clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/clkrst.hpp @@ -26,5 +26,8 @@ namespace ams::clkrst { void EnableUartBClock(); void EnableUartCClock(); void EnableActmonClock(); + void EnableI2c1Clock(); + + void DisableI2c1Clock(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp index 2f1cca8c5..3c54fbedc 100644 --- a/libraries/libexosphere/include/exosphere/log.hpp +++ b/libraries/libexosphere/include/exosphere/log.hpp @@ -19,5 +19,9 @@ namespace ams::log { void Initialize(); + void Finalize(); + + void SendText(const void *text, size_t size); + void Flush(); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pinmux.hpp b/libraries/libexosphere/include/exosphere/pinmux.hpp new file mode 100644 index 000000000..f79035b27 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pinmux.hpp @@ -0,0 +1,29 @@ +/* + * 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::pinmux { + + void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address); + + void SetupUartA(); + void SetupUartB(); + void SetupUartC(); + void SetupI2c1(); + void SetupI2c5(); + +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 035b28594..1529b566f 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -56,12 +56,16 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364) /* CLK_SOURCE */ +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124) +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C) #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) #define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3e8) /* CLK_ENB_*_INDEX */ +#define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX (0x0F) #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) @@ -80,8 +84,11 @@ DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, O DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON); -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); -DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2) +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); + +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); diff --git a/libraries/libexosphere/include/exosphere/uart.hpp b/libraries/libexosphere/include/exosphere/uart.hpp index 920f39125..d3f921693 100644 --- a/libraries/libexosphere/include/exosphere/uart.hpp +++ b/libraries/libexosphere/include/exosphere/uart.hpp @@ -39,4 +39,8 @@ namespace ams::uart { void Initialize(Port port, int baud_rate, u32 flags); -} \ No newline at end of file + void SendText(Port port, const void *data, size_t size); + + void WaitFlush(Port port); + +} diff --git a/libraries/libexosphere/source/charger/charger_api.cpp b/libraries/libexosphere/source/charger/charger_api.cpp new file mode 100644 index 000000000..7aeb5c37a --- /dev/null +++ b/libraries/libexosphere/source/charger/charger_api.cpp @@ -0,0 +1,55 @@ +/* + * 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 + +namespace ams::charger { + + namespace { + + /* https://www.ti.com/lit/ds/symlink/bq24193.pdf */ + constexpr inline int I2cAddressBq24193 = 0x6B; + + constexpr inline int Bq24193RegisterInputSourceControl = 0x00; + + /* 8.5.1.1 EN_HIZ */ + enum EnHiZ : u8 { + EnHiZ_Disable = (0u << 7), + EnHiZ_Enable = (1u << 7), + + EnHiZ_Mask = (1u << 7), + }; + + } + + bool IsHiZMode() { + return (i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl) & EnHiZ_Mask) == EnHiZ_Enable; + } + + void EnterHiZMode() { + u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl); + ctrl &= ~EnHiZ_Mask; + ctrl |= EnHiZ_Enable; + i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl); + } + + void ExitHiZMode() { + u8 ctrl = i2c::QueryByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl); + ctrl &= ~EnHiZ_Mask; + ctrl |= EnHiZ_Disable; + i2c::SendByte(i2c::Port_1, I2cAddressBq24193, Bq24193RegisterInputSourceControl, ctrl); + } + +} diff --git a/libraries/libexosphere/source/clkrst/clkrst_api.cpp b/libraries/libexosphere/source/clkrst/clkrst_api.cpp index 76601b6eb..c9ac4f983 100644 --- a/libraries/libexosphere/source/clkrst/clkrst_api.cpp +++ b/libraries/libexosphere/source/clkrst/clkrst_api.cpp @@ -49,13 +49,13 @@ namespace ams::clkrst { reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 0)); } - // void DisableClock(const ClockParameters ¶m) { - // /* Hold reset. */ - // reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); - // - // /* Disable clock. */ - // reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); - // } + void DisableClock(const ClockParameters ¶m) { + /* Hold reset. */ + reg::ReadWrite(g_register_address + param.reset_offset, REG_BITS_VALUE(param.index, 1, 1)); + + /* Disable clock. */ + reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 0)); + } #define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \ constexpr inline const ClockParameters _VARNAME_ = { \ @@ -70,6 +70,8 @@ namespace ams::clkrst { DEFINE_CLOCK_PARAMETERS(UartAClock, L, UARTA, PLLP_OUT0, 0); DEFINE_CLOCK_PARAMETERS(UartBClock, L, UARTB, PLLP_OUT0, 0); DEFINE_CLOCK_PARAMETERS(UartCClock, H, UARTC, PLLP_OUT0, 0); + DEFINE_CLOCK_PARAMETERS(I2c1Clock, L, I2C1, CLK_M, 0); + DEFINE_CLOCK_PARAMETERS(I2c5Clock, H, I2C5, CLK_M, 0); DEFINE_CLOCK_PARAMETERS(ActmonClock, V, ACTMON, CLK_M, 0); } @@ -98,4 +100,16 @@ namespace ams::clkrst { EnableClock(ActmonClock); } + void EnableI2c1Clock() { + EnableClock(I2c1Clock); + } + + void EnableI2c5Clock() { + EnableClock(I2c1Clock); + } + + void DisableI2c1Clock() { + DisableClock(I2c1Clock); + } + } diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp index 0cf20e207..2ea3c5c70 100644 --- a/libraries/libexosphere/source/log/log_api.cpp +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -40,9 +40,30 @@ namespace ams::log { } }(); + ALWAYS_INLINE void SetupUart() { + if constexpr (UartLogPort == uart::Port_ReservedDebug) { + /* Logging to the debug port. */ + pinmux::SetupUartA(); + clkrst::EnableUartAClock(); + } else if constexpr (UartLogPort == uart::Port_LeftJoyCon) { + /* Logging to left joy-con (e.g. with Joyless). */ + pinmux::SetupUartB(); + clkrst::EnableUartBClock(); + } else if constexpr (UartLogPort == uart::Port_RightJoyCon) { + /* Logging to right joy-con (e.g. with Joyless). */ + pinmux::SetupUartC(); + clkrst::EnableUartCClock(); + } else { + __builtin_unreachable(); + } + } + } void Initialize() { + /* Initialize pinmux and clock for the target uart port. */ + SetupUart(); + /* Initialize the target uart port. */ uart::Initialize(UartLogPort, 115200, UartPortFlags); @@ -50,4 +71,20 @@ namespace ams::log { g_initialized_uart = true; } + void Finalize() { + g_initialized_uart = false; + } + + void SendText(const void *text, size_t size) { + if (g_initialized_uart) { + uart::SendText(UartLogPort, text, size); + } + } + + void Flush() { + if (g_initialized_uart) { + uart::WaitFlush(UartLogPort); + } + } + } \ No newline at end of file diff --git a/libraries/libexosphere/source/pinmux/pinmux_api.cpp b/libraries/libexosphere/source/pinmux/pinmux_api.cpp new file mode 100644 index 000000000..513797dfb --- /dev/null +++ b/libraries/libexosphere/source/pinmux/pinmux_api.cpp @@ -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 . + */ +#include +#include "pinmux_registers.hpp" + +namespace ams::pinmux { + + namespace { + + constinit uintptr_t g_pinmux_address = secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress(); + constinit uintptr_t g_gpio_address = secmon::MemoryRegionPhysicalDeviceGpio.GetAddress(); + + } + + void SetRegisterAddress(uintptr_t pinmux_address, uintptr_t gpio_address) { + g_pinmux_address = pinmux_address; + g_gpio_address = gpio_address; + } + + void SetupUartA() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-A. */ + reg::Write(PINMUX + PINMUX_AUX_UART1_TX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_RX, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_RTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART1_CTS, PINMUX_REG_BITS_ENUM(AUX_UART1_PM, UARTA), + PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + + void SetupUartB() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-B. */ + reg::Write(PINMUX + PINMUX_AUX_UART2_TX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_RX, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_RTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART2_CTS, PINMUX_REG_BITS_ENUM(AUX_UART2_PM, UARTB), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + /* Configure GPIO for Uart-B. */ + reg::ReadWrite(g_gpio_address + 0x108, REG_BITS_VALUE(0, 4, 0)); + } + + void SetupUartC() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure Uart-B. */ + reg::Write(PINMUX + PINMUX_AUX_UART3_TX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_RX, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_RTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_UART3_CTS, PINMUX_REG_BITS_ENUM(AUX_UART3_PM, UARTC), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + /* Configure GPIO for Uart-C. */ + reg::ReadWrite(g_gpio_address + 0x00C, REG_BITS_VALUE(1, 4, 0)); + } + + void SetupI2c1() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure I2c1 */ + reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_GEN1_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_GEN1_I2C_PM, I2C1), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + + void SetupI2c5() { + /* Get the registers. */ + const uintptr_t PINMUX = g_pinmux_address; + + /* Configure I2c5 */ + reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SCL, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + + reg::Write(PINMUX + PINMUX_AUX_PWR_I2C_SDA, PINMUX_REG_BITS_ENUM(AUX_PWR_I2C_PM, I2CPMU), + PINMUX_REG_BITS_ENUM(AUX_PUPD, NONE), + PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH), + PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE), + PINMUX_REG_BITS_ENUM(AUX_LOCK, DISABLE), + PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE)); + } + +} \ No newline at end of file diff --git a/libraries/libexosphere/source/pinmux/pinmux_registers.hpp b/libraries/libexosphere/source/pinmux/pinmux_registers.hpp new file mode 100644 index 000000000..4fd1c4d51 --- /dev/null +++ b/libraries/libexosphere/source/pinmux/pinmux_registers.hpp @@ -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 . + */ +#include + +namespace ams::pinmux { + + #define PINMUX_AUX_GEN1_I2C_SCL (0x30BC) + #define PINMUX_AUX_GEN1_I2C_SDA (0x30C0) + #define PINMUX_AUX_PWR_I2C_SCL (0x30DC) + #define PINMUX_AUX_PWR_I2C_SDA (0x30E0) + + #define PINMUX_AUX_UART1_TX (0x30E4) + #define PINMUX_AUX_UART1_RX (0x30E8) + #define PINMUX_AUX_UART1_RTS (0x30EC) + #define PINMUX_AUX_UART1_CTS (0x30F0) + #define PINMUX_AUX_UART2_TX (0x30F4) + #define PINMUX_AUX_UART2_RX (0x30F8) + #define PINMUX_AUX_UART2_RTS (0x30FC) + #define PINMUX_AUX_UART2_CTS (0x3100) + #define PINMUX_AUX_UART3_TX (0x3104) + #define PINMUX_AUX_UART3_RX (0x3108) + #define PINMUX_AUX_UART3_RTS (0x310C) + #define PINMUX_AUX_UART3_CTS (0x3110) + + #define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME) + #define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE) + #define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM) + #define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE); + DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE); + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3); + + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3); + +} diff --git a/libraries/libexosphere/source/uart/uart_api.cpp b/libraries/libexosphere/source/uart/uart_api.cpp index 533748106..02e6d59a3 100644 --- a/libraries/libexosphere/source/uart/uart_api.cpp +++ b/libraries/libexosphere/source/uart/uart_api.cpp @@ -58,6 +58,28 @@ namespace ams::uart { } } + constexpr inline u32 LockBit = (1 << 6); + + void Lock(volatile UartRegisters *reg) { + while (true) { + if (reg->mie != 0) { + continue; + } + + reg->irda_csr = LockBit; + + if (reg->mie == 0) { + break; + } + + reg->irda_csr = 0; + } + } + + void Unlock(volatile UartRegisters *reg) { + reg->irda_csr = 0; + } + } void SetRegisterAddress(uintptr_t address) { @@ -110,4 +132,36 @@ namespace ams::uart { uart->spr = 0; } -} \ No newline at end of file + void SendText(Port port, const void *data, size_t size) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Get pointer to data. */ + const u8 *p = static_cast(data); + + /* Lock the uart registers. */ + Lock(uart); + ON_SCOPE_EXIT { Unlock(uart); }; + + /* Send each byte. */ + for (size_t i = 0; i < size; ++i) { + WaitFifoNotFull(uart); + + if (p[i] == '\n') { + *reinterpret_cast(std::addressof(uart->thr)) = '\r'; + WaitFifoNotFull(uart); + } + + *reinterpret_cast(std::addressof(uart->thr)) = p[i]; + } + } + + void WaitFlush(Port port) { + /* Get the registers. */ + auto *uart = GetRegisters(port); + + /* Wait for idle. */ + WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE); + } + +}