mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-30 06:25:20 -04:00
exo2: Initial work on the exosphere rewrite.
exo2: Implement uncompressor stub and boot code up to Main(). exo2: implement some more init (uart/gic) exo2: implement more of init exo2: improve reg api, add keyslot flag setters exo2: implement se aes decryption/enc exo2: fix bugs in loader stub/mmu mappings exo2: start skeletoning bootconfig/global context types arch: fix makefile flags exo2: implement through master key derivation exo2: implement device master keygen exo2: more init through start of SetupSocSecurity exo2: implement pmc secure scratch management se: implement sticky bit validation libexosphere: fix building for arm32 libexo: fix makefile flags libexo: support building for arm64/arm sc7fw: skeleton binary sc7fw: skeleton a little more sc7fw: implement all non-dram functionality exo2: fix DivideUp error sc7fw: implement more dram code, fix reg library errors sc7fw: complete sc7fw impl. exo2: skeleton the rest of SetupSocSecurity exo2: implement fiq interrupt handler exo2: implement all exception handlers exo2: skeleton the entire smc api, implement the svc invoker exo2: implement rest of SetupSocSecurity exo2: correct slave security errors exo2: fix register definition exo2: minor fixes
This commit is contained in:
parent
71e0102f7a
commit
f66b41c027
192 changed files with 15093 additions and 24 deletions
42
libraries/libexosphere/source/actmon/actmon_api.cpp
Normal file
42
libraries/libexosphere/source/actmon/actmon_api.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::actmon {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceActivityMonitor.GetAddress();
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void HandleInterrupt() {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void StartMonitoringBpmp(InterruptHandler handler) {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void StopMonitoringBpmp() {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
}
|
98
libraries/libexosphere/source/clkrst/clkrst_api.cpp
Normal file
98
libraries/libexosphere/source/clkrst/clkrst_api.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "clkrst_registers.hpp"
|
||||
|
||||
namespace ams::clkrst {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress();
|
||||
|
||||
struct ClockParameters {
|
||||
uintptr_t reset_offset;
|
||||
uintptr_t clk_enb_offset;
|
||||
uintptr_t clk_src_offset;
|
||||
u8 index;
|
||||
u8 clk_src;
|
||||
u8 clk_div;
|
||||
};
|
||||
|
||||
void EnableClock(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));
|
||||
|
||||
/* Set the clock source. */
|
||||
if (param.clk_src != 0) {
|
||||
reg::Write(g_register_address + param.clk_src_offset, (param.clk_src << 29) | (param.clk_div << 0));
|
||||
}
|
||||
|
||||
/* Enable clk. */
|
||||
reg::ReadWrite(g_register_address + param.clk_enb_offset, REG_BITS_VALUE(param.index, 1, 1));
|
||||
|
||||
/* Release reset. */
|
||||
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));
|
||||
// }
|
||||
|
||||
#define DEFINE_CLOCK_PARAMETERS(_VARNAME_, _REG_, _NAME_, _CLK_, _DIV_) \
|
||||
constexpr inline const ClockParameters _VARNAME_ = { \
|
||||
.reset_offset = CLK_RST_CONTROLLER_RST_DEVICES_##_REG_, \
|
||||
.clk_enb_offset = CLK_RST_CONTROLLER_CLK_OUT_ENB_##_REG_, \
|
||||
.clk_src_offset = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_, \
|
||||
.index = CLK_RST_CONTROLLER_CLK_ENB_##_NAME_##_INDEX, \
|
||||
.clk_src = CLK_RST_CONTROLLER_CLK_SOURCE_##_NAME_##_##_NAME_##_CLK_SRC_##_CLK_, \
|
||||
.clk_div = _DIV_, \
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void SetFuseVisibility(bool visible) {
|
||||
reg::ReadWrite(g_register_address + CLK_RST_CONTROLLER_MISC_CLK_ENB, CLK_RST_REG_BITS_VALUE(MISC_CLK_ENB_CFG_ALL_VISIBLE, visible ? 1 : 0));
|
||||
}
|
||||
|
||||
void EnableUartAClock() {
|
||||
EnableClock(UartAClock);
|
||||
}
|
||||
|
||||
void EnableUartBClock() {
|
||||
EnableClock(UartAClock);
|
||||
}
|
||||
|
||||
void EnableUartCClock() {
|
||||
EnableClock(UartAClock);
|
||||
}
|
||||
|
||||
|
||||
}
|
71
libraries/libexosphere/source/clkrst/clkrst_registers.hpp
Normal file
71
libraries/libexosphere/source/clkrst/clkrst_registers.hpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::clkrst {
|
||||
|
||||
/* Clock source enums. */
|
||||
#define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME)
|
||||
#define CLK_RST_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (CLK_RST_CONTROLLER, NAME, VALUE)
|
||||
#define CLK_RST_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (CLK_RST_CONTROLLER, NAME, ENUM)
|
||||
#define CLK_RST_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(CLK_RST_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
|
||||
|
||||
#define DEFINE_CLK_RST_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (CLK_RST_CONTROLLER, NAME, __OFFSET__, __WIDTH__)
|
||||
#define DEFINE_CLK_RST_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE)
|
||||
#define DEFINE_CLK_RST_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
|
||||
#define DEFINE_CLK_RST_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
|
||||
#define DEFINE_CLK_RST_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 (CLK_RST_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
|
||||
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_SOURCE (0x000)
|
||||
|
||||
#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048)
|
||||
|
||||
DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1);
|
||||
|
||||
/* RST_DEVICES */
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_U (0x00C)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_X (0x28C)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_Y (0x2A4)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_V (0x358)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_W (0x35C)
|
||||
|
||||
/* CLK_OUT_ENB */
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_L (0x010)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_H (0x014)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_U (0x018)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X (0x280)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_Y (0x298)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_V (0x360)
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
|
||||
|
||||
/* CLK_SOURCE */
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
|
||||
|
||||
/* CLK_ENB_*_INDEX */
|
||||
#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)
|
||||
|
||||
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)
|
||||
|
||||
}
|
121
libraries/libexosphere/source/fuse/fuse_api.cpp
Normal file
121
libraries/libexosphere/source/fuse/fuse_api.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "fuse_registers.hpp"
|
||||
|
||||
namespace ams::fuse {
|
||||
|
||||
namespace {
|
||||
|
||||
struct OdmWord4 {
|
||||
using HardwareState1 = util::BitPack32::Field<0, 2, int>;
|
||||
using HardwareType1 = util::BitPack32::Field<HardwareState1::Next, 1, int>;
|
||||
using DramId = util::BitPack32::Field<HardwareType1::Next, 5, int>;
|
||||
using HardwareType2 = util::BitPack32::Field<DramId::Next, 1, int>;
|
||||
using HardwareState2 = util::BitPack32::Field<HardwareType2::Next, 1, int>;
|
||||
using QuestState = util::BitPack32::Field<HardwareState2::Next, 1, int>;
|
||||
using FormatVersion = util::BitPack32::Field<QuestState::Next, 1, int>;
|
||||
using Reserved = util::BitPack32::Field<FormatVersion::Next, 4, int>;
|
||||
using HardwareType3 = util::BitPack32::Field<Reserved::Next, 4, int>;
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE int GetHardwareStateValue(const util::BitPack32 odm_word4) {
|
||||
constexpr auto HardwareState1Shift = 0;
|
||||
constexpr auto HardwareState2Shift = OdmWord4::HardwareState1::Count + HardwareState1Shift;
|
||||
|
||||
return (odm_word4.Get<OdmWord4::HardwareState1>() << HardwareState1Shift) |
|
||||
(odm_word4.Get<OdmWord4::HardwareState2>() << HardwareState2Shift);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE int GetHardwareTypeValue(const util::BitPack32 odm_word4) {
|
||||
constexpr auto HardwareType1Shift = 0;
|
||||
constexpr auto HardwareType2Shift = OdmWord4::HardwareType1::Count + HardwareType1Shift;
|
||||
constexpr auto HardwareType3Shift = OdmWord4::HardwareType2::Count + HardwareType2Shift;
|
||||
|
||||
return (odm_word4.Get<OdmWord4::HardwareType1>() << HardwareType1Shift) |
|
||||
(odm_word4.Get<OdmWord4::HardwareType2>() << HardwareType2Shift) |
|
||||
(odm_word4.Get<OdmWord4::HardwareType3>() << HardwareType3Shift);
|
||||
}
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceFuses.GetAddress();
|
||||
|
||||
ALWAYS_INLINE volatile FuseRegisterRegion *GetRegisterRegion() {
|
||||
return reinterpret_cast<volatile FuseRegisterRegion *>(g_register_address);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE volatile FuseRegisters &GetRegisters() {
|
||||
return GetRegisterRegion()->fuse;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE volatile FuseChipRegisters &GetChipRegisters() {
|
||||
return GetRegisterRegion()->chip;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void SetWriteSecureOnly() {
|
||||
reg::Write(GetRegisters().FUSE_PRIVATEKEYDISABLE, FUSE_REG_BITS_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, KEY_INVISIBLE));
|
||||
}
|
||||
|
||||
void Lockout() {
|
||||
reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE));
|
||||
}
|
||||
|
||||
u32 GetOdmWord(int index) {
|
||||
return GetChipRegisters().FUSE_RESERVED_ODM[index];
|
||||
}
|
||||
|
||||
HardwareType GetHardwareType() {
|
||||
/* Read the odm word. */
|
||||
const util::BitPack32 odm_word4 = { GetOdmWord(4) };
|
||||
|
||||
/* Get the value. */
|
||||
const auto value = GetHardwareTypeValue(odm_word4);
|
||||
|
||||
switch (value) {
|
||||
case 0x01: return HardwareType_Icosa;
|
||||
case 0x02: return (true /* TODO: GetSocType() == SocType_Mariko */) ? HardwareType_Calcio : HardwareType_Copper;
|
||||
case 0x04: return HardwareType_Iowa;
|
||||
case 0x08: return HardwareType_Hoag;
|
||||
case 0x10: return HardwareType_Five;
|
||||
default: return HardwareType_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
HardwareState GetHardwareState() {
|
||||
/* Read the odm word. */
|
||||
const util::BitPack32 odm_word4 = { GetOdmWord(4) };
|
||||
|
||||
/* Get the value. */
|
||||
const auto value = GetHardwareStateValue(odm_word4);
|
||||
|
||||
switch (value) {
|
||||
case 3: return HardwareState_Development;
|
||||
case 4: return HardwareState_Production;
|
||||
default: return HardwareState_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
pmic::Regulator GetRegulator() {
|
||||
/* TODO: How should mariko be handled? This reads from ODM word 28 in fuses (not presesnt in erista...). */
|
||||
return pmic::Regulator_Erista_Max77621;
|
||||
}
|
||||
|
||||
}
|
221
libraries/libexosphere/source/fuse/fuse_registers.hpp
Normal file
221
libraries/libexosphere/source/fuse/fuse_registers.hpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::fuse {
|
||||
|
||||
struct FuseRegisters {
|
||||
u32 FUSE_FUSECTRL;
|
||||
u32 FUSE_FUSEADDR;
|
||||
u32 FUSE_FUSERDATA;
|
||||
u32 FUSE_FUSEWDATA;
|
||||
u32 FUSE_FUSETIME_RD1;
|
||||
u32 FUSE_FUSETIME_RD2;
|
||||
u32 FUSE_FUSETIME_PGM1;
|
||||
u32 FUSE_FUSETIME_PGM2;
|
||||
u32 FUSE_PRIV2INTFC_START;
|
||||
u32 FUSE_FUSEBYPASS;
|
||||
u32 FUSE_PRIVATEKEYDISABLE;
|
||||
u32 FUSE_DISABLEREGPROGRAM;
|
||||
u32 FUSE_WRITE_ACCESS_SW;
|
||||
u32 FUSE_PWR_GOOD_SW;
|
||||
u32 _0x38;
|
||||
u32 FUSE_PRIV2RESHIFT;
|
||||
u32 _0x40[0x3];
|
||||
u32 FUSE_FUSETIME_RD3;
|
||||
u32 _0x50[0xC];
|
||||
u32 FUSE_PRIVATE_KEY0_NONZERO;
|
||||
u32 FUSE_PRIVATE_KEY1_NONZERO;
|
||||
u32 FUSE_PRIVATE_KEY2_NONZERO;
|
||||
u32 FUSE_PRIVATE_KEY3_NONZERO;
|
||||
u32 FUSE_PRIVATE_KEY4_NONZERO;
|
||||
u32 _0x94[0x1B];
|
||||
};
|
||||
static_assert(util::is_pod<FuseRegisters>::value);
|
||||
static_assert(sizeof(FuseRegisters) == 0x100);
|
||||
|
||||
struct FuseChipRegisters {
|
||||
u32 FUSE_PRODUCTION_MODE;
|
||||
u32 FUSE_JTAG_SECUREID_VALID;
|
||||
u32 FUSE_ODM_LOCK;
|
||||
u32 FUSE_OPT_OPENGL_EN;
|
||||
u32 FUSE_SKU_INFO;
|
||||
u32 FUSE_CPU_SPEEDO_0_CALIB;
|
||||
u32 FUSE_CPU_IDDQ_CALIB;
|
||||
u32 FUSE_DAC_CRT_CALIB;
|
||||
u32 FUSE_DAC_HDTV_CALIB;
|
||||
u32 FUSE_DAC_SDTV_CALIB;
|
||||
u32 FUSE_OPT_FT_REV;
|
||||
u32 FUSE_CPU_SPEEDO_1_CALIB;
|
||||
u32 FUSE_CPU_SPEEDO_2_CALIB;
|
||||
u32 FUSE_SOC_SPEEDO_0_CALIB;
|
||||
u32 FUSE_SOC_SPEEDO_1_CALIB;
|
||||
u32 FUSE_SOC_SPEEDO_2_CALIB;
|
||||
u32 FUSE_SOC_IDDQ_CALIB;
|
||||
u32 FUSE_RESERVED_PRODUCTION_WP;
|
||||
u32 FUSE_FA;
|
||||
u32 FUSE_RESERVED_PRODUCTION;
|
||||
u32 FUSE_HDMI_LANE0_CALIB;
|
||||
u32 FUSE_HDMI_LANE1_CALIB;
|
||||
u32 FUSE_HDMI_LANE2_CALIB;
|
||||
u32 FUSE_HDMI_LANE3_CALIB;
|
||||
u32 FUSE_ENCRYPTION_RATE;
|
||||
u32 FUSE_PUBLIC_KEY[0x8];
|
||||
u32 FUSE_TSENSOR1_CALIB;
|
||||
u32 FUSE_TSENSOR2_CALIB;
|
||||
u32 FUSE_VSENSOR_CALIB;
|
||||
u32 FUSE_OPT_CP_REV;
|
||||
u32 FUSE_OPT_PFG;
|
||||
u32 FUSE_TSENSOR0_CALIB;
|
||||
u32 FUSE_FIRST_BOOTROM_PATCH_SIZE;
|
||||
u32 FUSE_SECURITY_MODE;
|
||||
u32 FUSE_PRIVATE_KEY[0x5];
|
||||
u32 FUSE_ARM_JTAG_DIS;
|
||||
u32 FUSE_BOOT_DEVICE_INFO;
|
||||
u32 FUSE_RESERVED_SW;
|
||||
u32 FUSE_OPT_VP9_DISABLE;
|
||||
u32 FUSE_RESERVED_ODM[0x8];
|
||||
u32 FUSE_OBS_DIS;
|
||||
u32 FUSE_NOR_INFO;
|
||||
u32 FUSE_USB_CALIB;
|
||||
u32 FUSE_SKU_DIRECT_CONFIG;
|
||||
u32 FUSE_KFUSE_PRIVKEY_CTRL;
|
||||
u32 FUSE_PACKAGE_INFO;
|
||||
u32 FUSE_OPT_VENDOR_CODE;
|
||||
u32 FUSE_OPT_FAB_CODE;
|
||||
u32 FUSE_OPT_LOT_CODE_0;
|
||||
u32 FUSE_OPT_LOT_CODE_1;
|
||||
u32 FUSE_OPT_WAFER_ID;
|
||||
u32 FUSE_OPT_X_COORDINATE;
|
||||
u32 FUSE_OPT_Y_COORDINATE;
|
||||
u32 FUSE_OPT_SEC_DEBUG_EN;
|
||||
u32 FUSE_OPT_OPS_RESERVED;
|
||||
u32 FUSE_SATA_CALIB;
|
||||
u32 FUSE_GPU_IDDQ_CALIB;
|
||||
u32 FUSE_TSENSOR3_CALIB;
|
||||
u32 FUSE_SKU_BOND_OUT_L;
|
||||
u32 FUSE_SKU_BOND_OUT_H;
|
||||
u32 FUSE_SKU_BOND_OUT_U;
|
||||
u32 FUSE_SKU_BOND_OUT_V;
|
||||
u32 FUSE_SKU_BOND_OUT_W;
|
||||
u32 FUSE_OPT_SAMPLE_TYPE;
|
||||
u32 FUSE_OPT_SUBREVISION;
|
||||
u32 FUSE_OPT_SW_RESERVED_0;
|
||||
u32 FUSE_OPT_SW_RESERVED_1;
|
||||
u32 FUSE_TSENSOR4_CALIB;
|
||||
u32 FUSE_TSENSOR5_CALIB;
|
||||
u32 FUSE_TSENSOR6_CALIB;
|
||||
u32 FUSE_TSENSOR7_CALIB;
|
||||
u32 FUSE_OPT_PRIV_SEC_EN;
|
||||
u32 FUSE_PKC_DISABLE;
|
||||
u32 _0x16C;
|
||||
u32 _0x170;
|
||||
u32 _0x174;
|
||||
u32 _0x178;
|
||||
u32 FUSE_FUSE2TSEC_DEBUG_DISABLE;
|
||||
u32 FUSE_TSENSOR_COMMON;
|
||||
u32 FUSE_OPT_CP_BIN;
|
||||
u32 FUSE_OPT_GPU_DISABLE;
|
||||
u32 FUSE_OPT_FT_BIN;
|
||||
u32 FUSE_OPT_DONE_MAP;
|
||||
u32 _0x194;
|
||||
u32 FUSE_APB2JTAG_DISABLE;
|
||||
u32 FUSE_ODM_INFO;
|
||||
u32 _0x1A0;
|
||||
u32 _0x1A4;
|
||||
u32 FUSE_ARM_CRYPT_DE_FEATURE;
|
||||
u32 _0x1AC;
|
||||
u32 _0x1B0;
|
||||
u32 _0x1B4;
|
||||
u32 _0x1B8;
|
||||
u32 _0x1BC;
|
||||
u32 FUSE_WOA_SKU_FLAG;
|
||||
u32 FUSE_ECO_RESERVE_1;
|
||||
u32 FUSE_GCPLEX_CONFIG_FUSE;
|
||||
u32 FUSE_PRODUCTION_MONTH;
|
||||
u32 FUSE_RAM_REPAIR_INDICATOR;
|
||||
u32 FUSE_TSENSOR9_CALIB;
|
||||
u32 _0x1D8;
|
||||
u32 FUSE_VMIN_CALIBRATION;
|
||||
u32 FUSE_AGING_SENSOR_CALIBRATION;
|
||||
u32 FUSE_DEBUG_AUTHENTICATION;
|
||||
u32 FUSE_SECURE_PROVISION_INDEX;
|
||||
u32 FUSE_SECURE_PROVISION_INFO;
|
||||
u32 FUSE_OPT_GPU_DISABLE_CP1;
|
||||
u32 FUSE_SPARE_ENDIS;
|
||||
u32 FUSE_ECO_RESERVE_0;
|
||||
u32 _0x1FC;
|
||||
u32 _0x200;
|
||||
u32 FUSE_RESERVED_CALIB0;
|
||||
u32 FUSE_RESERVED_CALIB1;
|
||||
u32 FUSE_OPT_GPU_TPC0_DISABLE;
|
||||
u32 FUSE_OPT_GPU_TPC0_DISABLE_CP1;
|
||||
u32 FUSE_OPT_CPU_DISABLE;
|
||||
u32 FUSE_OPT_CPU_DISABLE_CP1;
|
||||
u32 FUSE_TSENSOR10_CALIB;
|
||||
u32 FUSE_TSENSOR10_CALIB_AUX;
|
||||
u32 FUSE_OPT_RAM_SVOP_DP;
|
||||
u32 FUSE_OPT_RAM_SVOP_PDP;
|
||||
u32 FUSE_OPT_RAM_SVOP_REG;
|
||||
u32 FUSE_OPT_RAM_SVOP_SP;
|
||||
u32 FUSE_OPT_RAM_SVOP_SMPDP;
|
||||
u32 FUSE_OPT_GPU_TPC0_DISABLE_CP2;
|
||||
u32 FUSE_OPT_GPU_TPC1_DISABLE;
|
||||
u32 FUSE_OPT_GPU_TPC1_DISABLE_CP1;
|
||||
u32 FUSE_OPT_GPU_TPC1_DISABLE_CP2;
|
||||
u32 FUSE_OPT_CPU_DISABLE_CP2;
|
||||
u32 FUSE_OPT_GPU_DISABLE_CP2;
|
||||
u32 FUSE_USB_CALIB_EXT;
|
||||
u32 FUSE_RESERVED_FIELD;
|
||||
u32 FUSE_OPT_ECC_EN;
|
||||
u32 _0x25C;
|
||||
u32 _0x260;
|
||||
u32 _0x264;
|
||||
u32 _0x268;
|
||||
u32 _0x26C;
|
||||
u32 _0x270;
|
||||
u32 _0x274;
|
||||
u32 _0x278;
|
||||
u32 FUSE_SPARE_REALIGNMENT_REG;
|
||||
u32 FUSE_SPARE_BIT[0x20];
|
||||
};
|
||||
static_assert(util::is_pod<FuseChipRegisters>::value);
|
||||
static_assert(sizeof(FuseChipRegisters) == 0x300);
|
||||
|
||||
struct FuseRegisterRegion {
|
||||
FuseRegisters fuse;
|
||||
FuseChipRegisters chip;
|
||||
};
|
||||
static_assert(util::is_pod<FuseRegisterRegion>::value);
|
||||
static_assert(sizeof(FuseRegisterRegion) == secmon::MemoryRegionPhysicalDeviceFuses.GetSize());
|
||||
|
||||
#define FUSE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (FUSE, NAME)
|
||||
#define FUSE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (FUSE, NAME, VALUE)
|
||||
#define FUSE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (FUSE, NAME, ENUM)
|
||||
#define FUSE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(FUSE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
|
||||
|
||||
#define DEFINE_FUSE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (FUSE, NAME, __OFFSET__, __WIDTH__)
|
||||
#define DEFINE_FUSE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE)
|
||||
#define DEFINE_FUSE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
|
||||
#define DEFINE_FUSE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
|
||||
#define DEFINE_FUSE_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 (FUSE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
|
||||
|
||||
DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, 4, KEY_VISIBLE, KEY_INVISIBLE);
|
||||
DEFINE_FUSE_REG_BIT_ENUM(PRIVATEKEYDISABLE_PRIVATEKEYDISABLE_VAL_KEY, 0, VISIBLE, INVISIBLE);
|
||||
|
||||
DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE);
|
||||
|
||||
}
|
217
libraries/libexosphere/source/gic/gic_api.cpp
Normal file
217
libraries/libexosphere/source/gic/gic_api.cpp
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::gic {
|
||||
|
||||
namespace {
|
||||
|
||||
struct GicDistributor {
|
||||
u32 ctlr;
|
||||
u32 typer;
|
||||
u32 iidr;
|
||||
u32 reserved_0x0c;
|
||||
u32 statusr;
|
||||
u32 reserved_0x14[3];
|
||||
u32 impldef_0x20[8];
|
||||
u32 setspi_nsr;
|
||||
u32 reserved_0x44;
|
||||
u32 clrspi_nsr;
|
||||
u32 reserved_0x4c;
|
||||
u32 setspi_sr;
|
||||
u32 reserved_0x54;
|
||||
u32 clrspi_sr;
|
||||
u32 reserved_0x5c[9];
|
||||
u32 igroupr[32];
|
||||
u32 isenabler[32];
|
||||
u32 icenabler[32];
|
||||
u32 ispendr[32];
|
||||
u32 icpendr[32];
|
||||
u32 isactiver[32];
|
||||
u32 icactiver[32];
|
||||
union {
|
||||
u8 bytes[1020];
|
||||
u32 words[255];
|
||||
} ipriorityr;
|
||||
u32 _0x7fc;
|
||||
union {
|
||||
u8 bytes[1020];
|
||||
u32 words[255];
|
||||
} itargetsr;
|
||||
u32 _0xbfc;
|
||||
u32 icfgr[64];
|
||||
u32 igrpmodr[32];
|
||||
u32 _0xd80[32];
|
||||
u32 nsacr[64];
|
||||
u32 sgir;
|
||||
u32 _0xf04[3];
|
||||
u32 cpendsgir[4];
|
||||
u32 spendsgir[4];
|
||||
u32 reserved_0xf30[52];
|
||||
|
||||
static constexpr size_t SgirCpuTargetListShift = 16;
|
||||
|
||||
enum SgirTargetListFilter : u32 {
|
||||
SgirTargetListFilter_CpuTargetList = (0 << 24),
|
||||
SgirTargetListFilter_Others = (1 << 24),
|
||||
SgirTargetListFilter_Self = (2 << 24),
|
||||
SgirTargetListFilter_Reserved = (3 << 24),
|
||||
};
|
||||
};
|
||||
static_assert(util::is_pod<GicDistributor>::value);
|
||||
static_assert(sizeof(GicDistributor) == 0x1000);
|
||||
static_assert(sizeof(GicDistributor) == secmon::MemoryRegionPhysicalDeviceGicDistributor.GetSize());
|
||||
|
||||
struct GicCpuInterface {
|
||||
u32 ctlr;
|
||||
u32 pmr;
|
||||
u32 bpr;
|
||||
u32 iar;
|
||||
u32 eoir;
|
||||
u32 rpr;
|
||||
u32 hppir;
|
||||
u32 abpr;
|
||||
u32 aiar;
|
||||
u32 aeoir;
|
||||
u32 ahppir;
|
||||
u32 statusr;
|
||||
u32 reserved_30[4];
|
||||
u32 impldef_40[36];
|
||||
u32 apr[4];
|
||||
u32 nsapr[4];
|
||||
u32 reserved_f0[3];
|
||||
u32 iidr;
|
||||
u32 reserved_100[960];
|
||||
u32 dir;
|
||||
u32 _0x1004[1023];
|
||||
};
|
||||
static_assert(util::is_pod<GicCpuInterface>::value);
|
||||
static_assert(sizeof(GicCpuInterface) == 0x2000);
|
||||
static_assert(sizeof(GicCpuInterface) == secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetSize());
|
||||
|
||||
constexpr inline int InterruptWords = InterruptCount / BITSIZEOF(u32);
|
||||
constexpr inline int SpiIndex = BITSIZEOF(u32);
|
||||
|
||||
constinit uintptr_t g_distributor_address = secmon::MemoryRegionPhysicalDeviceGicDistributor.GetAddress();
|
||||
constinit uintptr_t g_cpu_interface_address = secmon::MemoryRegionPhysicalDeviceGicCpuInterface.GetAddress();
|
||||
|
||||
volatile GicDistributor *GetDistributor() {
|
||||
return reinterpret_cast<volatile GicDistributor *>(g_distributor_address);
|
||||
}
|
||||
|
||||
volatile GicCpuInterface *GetCpuInterface() {
|
||||
return reinterpret_cast<volatile GicCpuInterface *>(g_cpu_interface_address);
|
||||
}
|
||||
|
||||
void ReadWrite(uintptr_t address, int width, int i, u32 value) {
|
||||
/* This code will never be invoked with a negative interrupt id. */
|
||||
AMS_ASSUME(i >= 0);
|
||||
|
||||
const int scale = BITSIZEOF(u32) / width;
|
||||
const int word = i / scale;
|
||||
const int bit = (i % scale) * width;
|
||||
|
||||
reg::ReadWrite(address + sizeof(u32) * word, REG_BITS_VALUE(bit, width, value));
|
||||
}
|
||||
|
||||
void Write(uintptr_t address, int width, int i, u32 value) {
|
||||
/* This code will never be invoked with a negative interrupt id. */
|
||||
AMS_ASSUME(i >= 0);
|
||||
|
||||
const int scale = BITSIZEOF(u32) / width;
|
||||
const int word = i / scale;
|
||||
const int bit = (i % scale) * width;
|
||||
|
||||
reg::Write(address + sizeof(u32) * word, value << bit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t distributor_address, uintptr_t cpu_interface_address) {
|
||||
g_distributor_address = distributor_address;
|
||||
g_cpu_interface_address = cpu_interface_address;
|
||||
}
|
||||
|
||||
void InitializeCommon() {
|
||||
/* Get the gicd registers. */
|
||||
auto *gicd = GetDistributor();
|
||||
|
||||
/* Set IGROUPR for to be FFs. */
|
||||
for (int i = SpiIndex / BITSIZEOF(u32); i < InterruptWords; ++i) {
|
||||
gicd->igroupr[i] = 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
/* Set IPRIORITYR for spi interrupts to be 0x80. */
|
||||
for (int i = SpiIndex; i < InterruptCount; ++i) {
|
||||
gicd->ipriorityr.bytes[i] = 0x80;
|
||||
}
|
||||
|
||||
/* Enable group 0. */
|
||||
gicd->ctlr = 1;
|
||||
}
|
||||
|
||||
void InitializeCoreUnique() {
|
||||
/* Get the registers. */
|
||||
auto *gicd = GetDistributor();
|
||||
auto *gicc = GetCpuInterface();
|
||||
|
||||
/* Set IGROUPR0 to be FFs. */
|
||||
gicd->igroupr[0] = 0xFFFFFFFFu;
|
||||
|
||||
/* Set IPRIORITYR for core local interrupts to be 0x80. */
|
||||
for (int i = 0; i < SpiIndex; ++i) {
|
||||
gicd->ipriorityr.bytes[i] = 0x80;
|
||||
}
|
||||
|
||||
/* Enable group 0 as FIQs. */
|
||||
gicc->ctlr = 0x1D9;
|
||||
|
||||
/* Set PMR. */
|
||||
gicc->pmr = 0x80;
|
||||
|
||||
/* Set BPR. */
|
||||
gicc->bpr = 7;
|
||||
}
|
||||
|
||||
void SetPriority(int interrupt_id, int priority) {
|
||||
ReadWrite(g_distributor_address + offsetof(GicDistributor, ipriorityr), BITSIZEOF(u8), interrupt_id, priority);
|
||||
}
|
||||
|
||||
void SetInterruptGroup(int interrupt_id, int group) {
|
||||
ReadWrite(g_distributor_address + offsetof(GicDistributor, igroupr), 1, interrupt_id, group);
|
||||
}
|
||||
|
||||
void SetEnable(int interrupt_id, bool enable) {
|
||||
Write(g_distributor_address + offsetof(GicDistributor, isenabler), 1, interrupt_id, enable);
|
||||
}
|
||||
|
||||
void SetSpiTargetCpu(int interrupt_id, u32 cpu_mask) {
|
||||
ReadWrite(g_distributor_address + offsetof(GicDistributor, itargetsr), BITSIZEOF(u8), interrupt_id, cpu_mask);
|
||||
}
|
||||
|
||||
void SetSpiMode(int interrupt_id, InterruptMode mode) {
|
||||
ReadWrite(g_distributor_address + offsetof(GicDistributor, icfgr), 2, interrupt_id, static_cast<u32>(mode) << 1);
|
||||
}
|
||||
|
||||
int GetInterruptRequestId() {
|
||||
return reg::Read(GetCpuInterface()->iar);
|
||||
}
|
||||
|
||||
void SetEndOfInterrupt(int interrupt_id) {
|
||||
reg::Write(GetCpuInterface()->eoir, interrupt_id);
|
||||
}
|
||||
|
||||
}
|
29
libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp
Normal file
29
libraries/libexosphere/source/hw/hw_cache.arch.arm64.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::hw::arch::arm64 {
|
||||
|
||||
void FlushDataCache(const void *ptr, size_t size) {
|
||||
const uintptr_t start = reinterpret_cast<uintptr_t>(ptr);
|
||||
const uintptr_t end = util::AlignUp(start + size, hw::DataCacheLineSize);
|
||||
|
||||
for (uintptr_t cur = start; cur < end; cur += hw::DataCacheLineSize) {
|
||||
FlushDataCacheLine(reinterpret_cast<void *>(cur));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
202
libraries/libexosphere/source/i2c/i2c_api.cpp
Normal file
202
libraries/libexosphere/source/i2c/i2c_api.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "i2c_registers.hpp"
|
||||
|
||||
namespace ams::i2c {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t MaxTransferSize = sizeof(u32);
|
||||
|
||||
constinit std::array<uintptr_t, Port_Count> g_register_addresses = [] {
|
||||
std::array<uintptr_t, Port_Count> arr = {};
|
||||
|
||||
arr[Port_1] = secmon::MemoryRegionPhysicalDeviceI2c1.GetAddress();
|
||||
arr[Port_5] = secmon::MemoryRegionPhysicalDeviceI2c5.GetAddress();
|
||||
|
||||
return arr;
|
||||
}();
|
||||
|
||||
void LoadConfig(uintptr_t address) {
|
||||
/* Configure for TIMEOUT and MSTR config load. */
|
||||
/* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */
|
||||
/* We will reproduce the write just in case it is undocumented. */
|
||||
reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
||||
|
||||
/* Wait up to 20 microseconds for the master config to be loaded. */
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) {
|
||||
return;
|
||||
}
|
||||
util::WaitMicroSeconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBus(uintptr_t address) {
|
||||
/* Configure the bus clear register. */
|
||||
reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE));
|
||||
|
||||
/* Load the config. */
|
||||
LoadConfig(address);
|
||||
|
||||
/* Wait up to 250us (in 25 us increments) until the bus clear is done. */
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) {
|
||||
break;
|
||||
}
|
||||
|
||||
util::WaitMicroSeconds(25);
|
||||
}
|
||||
|
||||
/* Read the bus clear status. */
|
||||
reg::Read(address + I2C_BUS_CLEAR_STATUS);
|
||||
}
|
||||
|
||||
void InitializePort(uintptr_t address) {
|
||||
/* Calculate the divisor. */
|
||||
constexpr int Divisor = util::DivideUp(19200, 8 * 400);
|
||||
|
||||
/* Set the divisor. */
|
||||
reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1),
|
||||
I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1));
|
||||
|
||||
/* Clear the bus. */
|
||||
ClearBus(address);
|
||||
|
||||
/* Clear the status. */
|
||||
reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER));
|
||||
}
|
||||
|
||||
bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) {
|
||||
/* Ensure we don't write too much. */
|
||||
u32 data = 0;
|
||||
if (src_size > MaxTransferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy the data to a transfer word. */
|
||||
std::memcpy(std::addressof(data), src, src_size);
|
||||
|
||||
|
||||
/* Configure the to write the 7-bit address. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address),
|
||||
I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE));
|
||||
|
||||
/* Configure to write the data. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_DATA1, data);
|
||||
|
||||
/* Configure to write the correct amount of data. */
|
||||
reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE),
|
||||
I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1));
|
||||
|
||||
/* Load the configuration. */
|
||||
LoadConfig(base_address);
|
||||
|
||||
/* Start the command. */
|
||||
reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO));
|
||||
|
||||
/* Wait for the command to be done. */
|
||||
while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ }
|
||||
|
||||
/* Check if the transfer was successful. */
|
||||
return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL));
|
||||
}
|
||||
|
||||
bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) {
|
||||
/* Ensure we don't read too much. */
|
||||
if (dst_size > MaxTransferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Configure the to read the 7-bit address. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address),
|
||||
I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ));
|
||||
|
||||
/* Configure to read the correct amount of data. */
|
||||
reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ),
|
||||
I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1));
|
||||
|
||||
/* Load the configuration. */
|
||||
LoadConfig(base_address);
|
||||
|
||||
/* Start the command. */
|
||||
reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO));
|
||||
|
||||
/* Wait for the command to be done. */
|
||||
while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ }
|
||||
|
||||
/* Check that the transfer was successful. */
|
||||
if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read and copy out the data. */
|
||||
u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1);
|
||||
std::memcpy(dst, std::addressof(data), dst_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(Port port, uintptr_t address) {
|
||||
g_register_addresses[port] = address;
|
||||
}
|
||||
|
||||
void Initialize(Port port) {
|
||||
InitializePort(g_register_addresses[port]);
|
||||
}
|
||||
|
||||
bool Query(void *dst, size_t dst_size, Port port, int address, int r) {
|
||||
const uintptr_t base_address = g_register_addresses[port];
|
||||
|
||||
/* Select the register we want to read. */
|
||||
bool success = Write(base_address, port, address, std::addressof(r), 1, false);
|
||||
if (success) {
|
||||
/* If we successfully selected, read data from the register. */
|
||||
success = Read(base_address, port, dst, dst_size, address, true);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Send(Port port, int address, int r, const void *src, size_t src_size) {
|
||||
const uintptr_t base_address = g_register_addresses[port];
|
||||
|
||||
/* Create a transfer buffer, make sure we can use it. */
|
||||
u8 buffer[MaxTransferSize];
|
||||
if (src_size > sizeof(buffer) - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy data into the buffer. */
|
||||
buffer[0] = static_cast<u8>(r);
|
||||
std::memcpy(buffer + 1, src, src_size);
|
||||
|
||||
return Write(base_address, port, address, buffer, src_size + 1, false);
|
||||
}
|
||||
|
||||
}
|
77
libraries/libexosphere/source/i2c/i2c_registers.hpp
Normal file
77
libraries/libexosphere/source/i2c/i2c_registers.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::i2c {
|
||||
|
||||
#define I2C_I2C_CNFG (0x000)
|
||||
#define I2C_I2C_CMD_ADDR0 (0x004)
|
||||
#define I2C_I2C_CMD_DATA1 (0x00C)
|
||||
#define I2C_I2C_STATUS (0x01C)
|
||||
#define I2C_INTERRUPT_STATUS_REGISTER (0x068)
|
||||
#define I2C_CLK_DIVISOR_REGISTER (0x06C)
|
||||
#define I2C_BUS_CLEAR_CONFIG (0x084)
|
||||
#define I2C_BUS_CLEAR_STATUS (0x088)
|
||||
#define I2C_CONFIG_LOAD (0x08C)
|
||||
|
||||
#define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME)
|
||||
#define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE)
|
||||
#define I2C_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (I2C, NAME, ENUM)
|
||||
#define I2C_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(I2C, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
|
||||
|
||||
#define DEFINE_I2C_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (I2C, NAME, __OFFSET__, __WIDTH__)
|
||||
#define DEFINE_I2C_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE)
|
||||
#define DEFINE_I2C_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
|
||||
#define DEFINE_I2C_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
|
||||
#define DEFINE_I2C_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 (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
|
||||
|
||||
/* I2C_CNFG */
|
||||
DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3);
|
||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ);
|
||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO);
|
||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE);
|
||||
DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T);
|
||||
|
||||
/* I2C_CMD_ADDR0 */
|
||||
DEFINE_I2C_REG_BIT_ENUM(I2C_CMD_ADDR0_7BIT_RW, 0, WRITE, READ);
|
||||
DEFINE_I2C_REG(I2C_CMD_ADDR0_7BIT_ADDR, 1, 7);
|
||||
|
||||
/* I2C_STATUS */
|
||||
DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_NOACK_FOR_BYTE1, SL1_NOACK_FOR_BYTE2, SL1_NOACK_FOR_BYTE3, SL1_NOACK_FOR_BYTE4, SL1_NOACK_FOR_BYTE5, SL1_NOACK_FOR_BYTE6, SL1_NOACK_FOR_BYTE7, SL1_NOACK_FOR_BYTE8, SL1_NOACK_FOR_BYTE9, SL1_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15);
|
||||
DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15);
|
||||
DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY);
|
||||
|
||||
/* INTERRUPT_STATUS_REGISTER */
|
||||
DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET);
|
||||
|
||||
/* CLK_DIVISOR_REGISTER */
|
||||
DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16);
|
||||
DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_STD_FAST_MODE, 16, 16);
|
||||
|
||||
/* BUS_CLEAR_CONFIG */
|
||||
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, 0, DISABLE, ENABLE);
|
||||
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE);
|
||||
DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP);
|
||||
DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8);
|
||||
|
||||
/* CONFIG_LOAD */
|
||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE);
|
||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE);
|
||||
DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE);
|
||||
DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1);
|
||||
|
||||
|
||||
}
|
1141
libraries/libexosphere/source/libc/libc.c
Normal file
1141
libraries/libexosphere/source/libc/libc.c
Normal file
File diff suppressed because it is too large
Load diff
53
libraries/libexosphere/source/log/log_api.cpp
Normal file
53
libraries/libexosphere/source/log/log_api.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::log {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug;
|
||||
constinit bool g_initialized_uart = false;
|
||||
|
||||
constexpr inline u32 UartPortFlags = [] {
|
||||
if constexpr (UartLogPort == uart::Port_ReservedDebug) {
|
||||
/* Logging to the debug port. */
|
||||
/* Don't invert transactions. */
|
||||
return uart::Flag_None;
|
||||
} else if constexpr (UartLogPort == uart::Port_LeftJoyCon) {
|
||||
/* Logging to left joy-con (e.g. with Joyless). */
|
||||
/* Invert transactions. */
|
||||
return uart::Flag_Inverted;
|
||||
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) {
|
||||
/* Logging to right joy-con (e.g. with Joyless). */
|
||||
/* Invert transactions. */
|
||||
return uart::Flag_Inverted;
|
||||
} else {
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}();
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
/* Initialize the target uart port. */
|
||||
uart::Initialize(UartLogPort, 115200, UartPortFlags);
|
||||
|
||||
/* Note that we've initialized. */
|
||||
g_initialized_uart = true;
|
||||
}
|
||||
|
||||
}
|
275
libraries/libexosphere/source/pmc/pmc_api.cpp
Normal file
275
libraries/libexosphere/source/pmc/pmc_api.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::pmc {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
|
||||
|
||||
constexpr inline u32 WriteMask = 0x1;
|
||||
constexpr inline u32 ReadMask = 0x2;
|
||||
|
||||
enum class LockMode {
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite,
|
||||
};
|
||||
|
||||
template<LockMode Mode>
|
||||
constexpr inline u32 LockMask = [] {
|
||||
switch (Mode) {
|
||||
case LockMode::Read: return ReadMask;
|
||||
case LockMode::Write: return WriteMask;
|
||||
case LockMode::ReadWrite: return ReadMask | WriteMask;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
}();
|
||||
|
||||
constexpr inline size_t NumSecureScratchRegisters = 120;
|
||||
constexpr inline size_t NumSecureDisableRegisters = 8;
|
||||
|
||||
template<size_t SecureScratch> requires (SecureScratch < NumSecureScratchRegisters)
|
||||
constexpr inline std::pair<size_t, size_t> DisableRegisterIndex = [] {
|
||||
if constexpr (SecureScratch < 8) {
|
||||
return std::pair<size_t, size_t>{0, 4 + 2 * SecureScratch};
|
||||
} else {
|
||||
constexpr size_t Relative = SecureScratch - 8;
|
||||
return std::pair<size_t, size_t>{1 + (Relative / 16), 2 * (Relative % 16)};
|
||||
}
|
||||
}();
|
||||
|
||||
struct LockInfo {
|
||||
size_t scratch;
|
||||
LockMode mode;
|
||||
};
|
||||
|
||||
template<LockInfo Info>
|
||||
constexpr ALWAYS_INLINE void SetSecureScratchMask(std::array<u32, NumSecureDisableRegisters> &disables) {
|
||||
constexpr std::pair<size_t, size_t> Location = DisableRegisterIndex<Info.scratch>;
|
||||
disables[Location.first] |= LockMask<Info.mode> << Location.second;
|
||||
}
|
||||
|
||||
template<LockInfo... Info>
|
||||
constexpr ALWAYS_INLINE void SetSecureScratchMasks(std::array<u32, NumSecureDisableRegisters> &disables) {
|
||||
(SetSecureScratchMask<Info>(disables), ...);
|
||||
}
|
||||
|
||||
template<size_t... Ix>
|
||||
constexpr ALWAYS_INLINE void SetSecureScratchReadWriteMasks(std::array<u32, NumSecureDisableRegisters> &disables) {
|
||||
(SetSecureScratchMask<LockInfo{Ix, LockMode::ReadWrite}>(disables), ...);
|
||||
}
|
||||
|
||||
template<size_t... Ix>
|
||||
constexpr ALWAYS_INLINE void SetSecureScratchReadMasks(std::array<u32, NumSecureDisableRegisters> &disables) {
|
||||
(SetSecureScratchMask<LockInfo{Ix, LockMode::Read}>(disables), ...);
|
||||
}
|
||||
|
||||
template<size_t... Ix>
|
||||
constexpr ALWAYS_INLINE void SetSecureScratchWriteMasks(std::array<u32, NumSecureDisableRegisters> &disables) {
|
||||
(SetSecureScratchMask<LockInfo{Ix, LockMode::Write}>(disables), ...);
|
||||
}
|
||||
|
||||
template<SecureRegister Register>
|
||||
constexpr ALWAYS_INLINE std::array<u32, NumSecureDisableRegisters> GetSecureScratchMasks() {
|
||||
std::array<u32, NumSecureDisableRegisters> disables = {};
|
||||
|
||||
if constexpr ((Register & SecureRegister_Other) != 0) {
|
||||
constexpr std::array<u32, NumSecureDisableRegisters> NonOtherDisables = GetSecureScratchMasks<static_cast<SecureRegister>(~SecureRegister_Other)>();
|
||||
for (size_t i = 0; i < NumSecureDisableRegisters; i++) {
|
||||
disables[i] |= ~NonOtherDisables[i];
|
||||
}
|
||||
disables[0] &= 0x007FFFF0;
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_DramParameters) != 0) {
|
||||
SetSecureScratchReadWriteMasks< 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
17, 18, 19, 20,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
52, 53, 54,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
79, 80, 81, 82, 83, 84,
|
||||
86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
|
||||
104, 105, 106, 107
|
||||
>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_ResetVector) != 0) {
|
||||
SetSecureScratchReadWriteMasks<34, 35>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_Carveout) != 0) {
|
||||
SetSecureScratchReadWriteMasks<16, 39, 51, 55, 74, 75, 76, 77, 78, 99, 100, 101, 102, 103>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_CmacWrite) != 0) {
|
||||
SetSecureScratchWriteMasks<112, 113, 114, 115>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_CmacRead) != 0) {
|
||||
SetSecureScratchReadMasks<112, 113, 114, 115>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_KeySourceWrite) != 0) {
|
||||
SetSecureScratchWriteMasks<24, 25, 26, 27>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_KeySourceRead) != 0) {
|
||||
SetSecureScratchReadMasks<24, 25, 26, 27>(disables);
|
||||
}
|
||||
if constexpr ((Register & SecureRegister_Srk) != 0) {
|
||||
SetSecureScratchReadWriteMasks<4, 5, 6, 7>(disables);
|
||||
}
|
||||
|
||||
return disables;
|
||||
}
|
||||
|
||||
/* Validate that the secure scratch masks produced are correct. */
|
||||
#include "pmc_secure_scratch_test.inc"
|
||||
|
||||
ALWAYS_INLINE void LockBits(uintptr_t address, u32 mask) {
|
||||
reg::Write(address, reg::Read(address) | mask);
|
||||
}
|
||||
|
||||
template<SecureRegister Register>
|
||||
ALWAYS_INLINE void SetSecureScratchMasks(uintptr_t address) {
|
||||
constexpr auto Masks = GetSecureScratchMasks<Register>();
|
||||
|
||||
if constexpr (Masks[0] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE , Masks[0]); }
|
||||
if constexpr (Masks[1] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE2, Masks[1]); }
|
||||
if constexpr (Masks[2] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE3, Masks[2]); }
|
||||
if constexpr (Masks[3] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE4, Masks[3]); }
|
||||
if constexpr (Masks[4] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE5, Masks[4]); }
|
||||
if constexpr (Masks[5] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE6, Masks[5]); }
|
||||
if constexpr (Masks[6] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE7, Masks[6]); }
|
||||
if constexpr (Masks[7] != 0) { LockBits(address + APBDEV_PMC_SEC_DISABLE8, Masks[7]); }
|
||||
|
||||
static_assert(Masks.size() == 8);
|
||||
}
|
||||
|
||||
template<SecureRegister Register>
|
||||
ALWAYS_INLINE bool TestSecureScratchMasks(uintptr_t address) {
|
||||
constexpr auto Masks = GetSecureScratchMasks<Register>();
|
||||
|
||||
if constexpr (Masks[0] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE ) & Masks[0]) != Masks[0]) { return false; } }
|
||||
if constexpr (Masks[1] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE2) & Masks[1]) != Masks[1]) { return false; } }
|
||||
if constexpr (Masks[2] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE3) & Masks[2]) != Masks[2]) { return false; } }
|
||||
if constexpr (Masks[3] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE4) & Masks[3]) != Masks[3]) { return false; } }
|
||||
if constexpr (Masks[4] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE5) & Masks[4]) != Masks[4]) { return false; } }
|
||||
if constexpr (Masks[5] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE6) & Masks[5]) != Masks[5]) { return false; } }
|
||||
if constexpr (Masks[6] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE7) & Masks[6]) != Masks[6]) { return false; } }
|
||||
if constexpr (Masks[7] != 0) { if ((reg::Read(address + APBDEV_PMC_SEC_DISABLE8) & Masks[7]) != Masks[7]) { return false; } }
|
||||
static_assert(Masks.size() == 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void WriteRandomValueToRegister(uintptr_t offset) {
|
||||
/* Create an aligned buffer. */
|
||||
util::AlignedBuffer<hw::DataCacheLineSize, sizeof(u32)> buf;
|
||||
|
||||
/* Generate random bytes into it. */
|
||||
se::GenerateRandomBytes(buf, sizeof(u32));
|
||||
|
||||
/* Read the random value. */
|
||||
const u32 random = *reinterpret_cast<const u32 *>(static_cast<u8 *>(buf));
|
||||
|
||||
/* Get the address. */
|
||||
const uintptr_t address = g_register_address + offset;
|
||||
|
||||
/* Write the value. */
|
||||
reg::Write(address, random);
|
||||
|
||||
/* Verify it was written. */
|
||||
AMS_ABORT_UNLESS(reg::Read(address) == random);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void InitializeRandomScratch() {
|
||||
/* Write random data to the scratch that contains the SRK. */
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH4);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH5);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH6);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH7);
|
||||
|
||||
/* Lock the SRK scratch. */
|
||||
LockSecureRegister(SecureRegister_Srk);
|
||||
|
||||
/* Write random data to the scratch used for tzram cmac. */
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH112);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH113);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH114);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH115);
|
||||
|
||||
/* Write random data to the scratch used for tzram key source. */
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH24);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH25);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH26);
|
||||
WriteRandomValueToRegister(APBDEV_PMC_SECURE_SCRATCH27);
|
||||
|
||||
/* Here, Nintendo locks the SRK scratch a second time. */
|
||||
/* This may just be "to be sure". */
|
||||
LockSecureRegister(SecureRegister_Srk);
|
||||
}
|
||||
|
||||
void LockSecureRegister(SecureRegister reg) {
|
||||
/* Get the address. */
|
||||
const uintptr_t address = g_register_address;
|
||||
|
||||
/* Apply each mask. */
|
||||
#define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { SetSecureScratchMasks<SecureRegister_##REG>(address); } } while (0)
|
||||
PMC_PROCESS_REG(Other);
|
||||
PMC_PROCESS_REG(DramParameters);
|
||||
PMC_PROCESS_REG(ResetVector);
|
||||
PMC_PROCESS_REG(Carveout);
|
||||
PMC_PROCESS_REG(CmacWrite);
|
||||
PMC_PROCESS_REG(CmacRead);
|
||||
PMC_PROCESS_REG(KeySourceWrite);
|
||||
PMC_PROCESS_REG(KeySourceRead);
|
||||
PMC_PROCESS_REG(Srk);
|
||||
#undef PMC_PROCESS_REG
|
||||
|
||||
}
|
||||
|
||||
LockState GetSecureRegisterLockState(SecureRegister reg) {
|
||||
bool all_valid = true;
|
||||
bool any_valid = false;
|
||||
|
||||
/* Get the address. */
|
||||
const uintptr_t address = g_register_address;
|
||||
|
||||
/* Test each mask. */
|
||||
#define PMC_PROCESS_REG(REG) do { if ((reg & SecureRegister_##REG) != 0) { const bool test = TestSecureScratchMasks<SecureRegister_##REG>(address); all_valid &= test; any_valid |= test; } } while (0)
|
||||
PMC_PROCESS_REG(Other);
|
||||
PMC_PROCESS_REG(DramParameters);
|
||||
PMC_PROCESS_REG(ResetVector);
|
||||
PMC_PROCESS_REG(Carveout);
|
||||
PMC_PROCESS_REG(CmacWrite);
|
||||
PMC_PROCESS_REG(CmacRead);
|
||||
PMC_PROCESS_REG(KeySourceWrite);
|
||||
PMC_PROCESS_REG(KeySourceRead);
|
||||
PMC_PROCESS_REG(Srk);
|
||||
#undef PMC_PROCESS_REG
|
||||
|
||||
if (all_valid) {
|
||||
return LockState::Locked;
|
||||
} else if (any_valid) {
|
||||
return LockState::PartiallyLocked;
|
||||
} else {
|
||||
return LockState::NotLocked;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
100
libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc
Normal file
100
libraries/libexosphere/source/pmc/pmc_secure_scratch_test.inc
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace test {
|
||||
|
||||
constexpr inline auto Other = GetSecureScratchMasks<SecureRegister_Other>();
|
||||
static_assert(Other[0] == 0x00700FF0u);
|
||||
static_assert(Other[1] == 0xFC000000u);
|
||||
static_assert(Other[2] == 0x3F0FFF00u);
|
||||
static_assert(Other[3] == 0x00000000u);
|
||||
static_assert(Other[4] == 0x00000000u);
|
||||
static_assert(Other[5] == 0x0C000000u);
|
||||
static_assert(Other[6] == 0x00000000u);
|
||||
static_assert(Other[7] == 0xFF00FF00u);
|
||||
|
||||
constexpr inline auto DramParameters = GetSecureScratchMasks<SecureRegister_DramParameters>();
|
||||
static_assert(DramParameters[0] == 0x00000000u);
|
||||
static_assert(DramParameters[1] == 0x03FCFFFFu);
|
||||
static_assert(DramParameters[2] == 0x00000000u);
|
||||
static_assert(DramParameters[3] == 0x3F3FFFFFu);
|
||||
static_assert(DramParameters[4] == 0xFFFFFFFFu);
|
||||
static_assert(DramParameters[5] == 0xF3FFC00Fu);
|
||||
static_assert(DramParameters[6] == 0x003FFFFFu);
|
||||
static_assert(DramParameters[7] == 0x000000FFu);
|
||||
|
||||
constexpr inline auto ResetVector = GetSecureScratchMasks<SecureRegister_ResetVector>();
|
||||
static_assert(ResetVector[0] == 0x00000000u);
|
||||
static_assert(ResetVector[1] == 0x00000000u);
|
||||
static_assert(ResetVector[2] == 0x00F00000u);
|
||||
static_assert(ResetVector[3] == 0x00000000u);
|
||||
static_assert(ResetVector[4] == 0x00000000u);
|
||||
static_assert(ResetVector[5] == 0x00000000u);
|
||||
static_assert(ResetVector[6] == 0x00000000u);
|
||||
static_assert(ResetVector[7] == 0x00000000u);
|
||||
|
||||
constexpr inline auto CmacWrite = GetSecureScratchMasks<SecureRegister_CmacWrite>();
|
||||
static_assert(CmacWrite[0] == 0x00000000u);
|
||||
static_assert(CmacWrite[1] == 0x00000000u);
|
||||
static_assert(CmacWrite[2] == 0x00000000u);
|
||||
static_assert(CmacWrite[3] == 0x00000000u);
|
||||
static_assert(CmacWrite[4] == 0x00000000u);
|
||||
static_assert(CmacWrite[5] == 0x00000000u);
|
||||
static_assert(CmacWrite[6] == 0x00000000u);
|
||||
static_assert(CmacWrite[7] == 0x00550000u);
|
||||
|
||||
constexpr inline auto CmacRead = GetSecureScratchMasks<SecureRegister_CmacRead>();
|
||||
static_assert(CmacRead[0] == 0x00000000u);
|
||||
static_assert(CmacRead[1] == 0x00000000u);
|
||||
static_assert(CmacRead[2] == 0x00000000u);
|
||||
static_assert(CmacRead[3] == 0x00000000u);
|
||||
static_assert(CmacRead[4] == 0x00000000u);
|
||||
static_assert(CmacRead[5] == 0x00000000u);
|
||||
static_assert(CmacRead[6] == 0x00000000u);
|
||||
static_assert(CmacRead[7] == 0x00AA0000u);
|
||||
|
||||
constexpr inline auto KeySourceWrite = GetSecureScratchMasks<SecureRegister_KeySourceWrite>();
|
||||
static_assert(KeySourceWrite[0] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[1] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[2] == 0x00000055u);
|
||||
static_assert(KeySourceWrite[3] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[4] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[5] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[6] == 0x00000000u);
|
||||
static_assert(KeySourceWrite[7] == 0x00000000u);
|
||||
|
||||
constexpr inline auto KeySourceRead = GetSecureScratchMasks<SecureRegister_KeySourceRead>();
|
||||
static_assert(KeySourceRead[0] == 0x00000000u);
|
||||
static_assert(KeySourceRead[1] == 0x00000000u);
|
||||
static_assert(KeySourceRead[2] == 0x000000AAu);
|
||||
static_assert(KeySourceRead[3] == 0x00000000u);
|
||||
static_assert(KeySourceRead[4] == 0x00000000u);
|
||||
static_assert(KeySourceRead[5] == 0x00000000u);
|
||||
static_assert(KeySourceRead[6] == 0x00000000u);
|
||||
static_assert(KeySourceRead[7] == 0x00000000u);
|
||||
|
||||
constexpr inline auto Srk = GetSecureScratchMasks<SecureRegister_Srk>();
|
||||
static_assert(Srk[0] == 0x000FF000u);
|
||||
static_assert(Srk[1] == 0x00000000u);
|
||||
static_assert(Srk[2] == 0x00000000u);
|
||||
static_assert(Srk[3] == 0x00000000u);
|
||||
static_assert(Srk[4] == 0x00000000u);
|
||||
static_assert(Srk[5] == 0x00000000u);
|
||||
static_assert(Srk[6] == 0x00000000u);
|
||||
static_assert(Srk[7] == 0x00000000u);
|
||||
|
||||
|
||||
}
|
340
libraries/libexosphere/source/pmic/max77620.h
Normal file
340
libraries/libexosphere/source/pmic/max77620.h
Normal file
|
@ -0,0 +1,340 @@
|
|||
/*
|
||||
* Defining registers address and its bit definitions of MAX77620 and MAX20024
|
||||
*
|
||||
* Copyright (c) 2016 NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MFD_MAX77620_H_
|
||||
#define _MFD_MAX77620_H_
|
||||
|
||||
#define MAX77620_I2C_ADDR 0x3C
|
||||
|
||||
/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */
|
||||
#define MAX77620_REG_CNFGGLBL1 0x00
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7)
|
||||
#define MAX77620_CNFGGLBL1_MPPLD (1 << 6)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4))
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_100 (0 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_200 (1 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_300 (2 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBHYST_400 (3 << 4)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_MASK 0x0E
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2700 (0 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2800 (1 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_2900 (2 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3000 (3 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3100 (4 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3200 (5 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3300 (6 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBDAC_3400 (7 << 1)
|
||||
#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0)
|
||||
|
||||
#define MAX77620_REG_CNFGGLBL2 0x01
|
||||
#define MAX77620_REG_CNFGGLBL3 0x02
|
||||
#define MAX77620_WDTC_MASK 0x3
|
||||
#define MAX77620_WDTOFFC (1 << 4)
|
||||
#define MAX77620_WDTSLPC (1 << 3)
|
||||
#define MAX77620_WDTEN (1 << 2)
|
||||
#define MAX77620_TWD_MASK 0x3
|
||||
#define MAX77620_TWD_2s 0x0
|
||||
#define MAX77620_TWD_16s 0x1
|
||||
#define MAX77620_TWD_64s 0x2
|
||||
#define MAX77620_TWD_128s 0x3
|
||||
|
||||
#define MAX77620_REG_CNFG1_32K 0x03
|
||||
#define MAX77620_CNFG1_32K_OUT0_EN (1 << 2)
|
||||
|
||||
#define MAX77620_REG_CNFGBBC 0x04
|
||||
#define MAX77620_CNFGBBC_ENABLE (1 << 0)
|
||||
#define MAX77620_CNFGBBC_CURRENT_MASK 0x06
|
||||
#define MAX77620_CNFGBBC_CURRENT_SHIFT 1
|
||||
#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18
|
||||
#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3
|
||||
#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE (1 << 5)
|
||||
#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0
|
||||
#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6
|
||||
#define MAX77620_CNFGBBC_RESISTOR_100 (0 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
|
||||
#define MAX77620_CNFGBBC_RESISTOR_1K (1 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
|
||||
#define MAX77620_CNFGBBC_RESISTOR_3K (2 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
|
||||
#define MAX77620_CNFGBBC_RESISTOR_6K (3 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
|
||||
|
||||
#define MAX77620_REG_IRQTOP 0x05
|
||||
#define MAX77620_IRQ_TOP_GLBL_MASK (1 << 7)
|
||||
#define MAX77620_IRQ_TOP_SD_MASK (1 << 6)
|
||||
#define MAX77620_IRQ_TOP_LDO_MASK (1 << 5)
|
||||
#define MAX77620_IRQ_TOP_GPIO_MASK (1 << 4)
|
||||
#define MAX77620_IRQ_TOP_RTC_MASK (1 << 3)
|
||||
#define MAX77620_IRQ_TOP_32K_MASK (1 << 2)
|
||||
#define MAX77620_IRQ_TOP_ONOFF_MASK (1 << 1)
|
||||
|
||||
#define MAX77620_REG_INTLBT 0x06
|
||||
#define MAX77620_REG_IRQTOPM 0x0D
|
||||
#define MAX77620_IRQ_LBM_MASK (1 << 3)
|
||||
#define MAX77620_IRQ_TJALRM1_MASK (1 << 2)
|
||||
#define MAX77620_IRQ_TJALRM2_MASK (1 << 1)
|
||||
|
||||
#define MAX77620_REG_IRQSD 0x07
|
||||
#define MAX77620_REG_IRQ_LVL2_L0_7 0x08
|
||||
#define MAX77620_REG_IRQ_LVL2_L8 0x09
|
||||
#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A
|
||||
#define MAX77620_REG_ONOFFIRQ 0x0B
|
||||
#define MAX77620_REG_NVERC 0x0C
|
||||
|
||||
#define MAX77620_REG_INTENLBT 0x0E
|
||||
#define MAX77620_GLBLM_MASK (1 << 0)
|
||||
|
||||
#define MAX77620_REG_IRQMASKSD 0x0F
|
||||
#define MAX77620_REG_IRQ_MSK_L0_7 0x10
|
||||
#define MAX77620_REG_IRQ_MSK_L8 0x11
|
||||
#define MAX77620_REG_ONOFFIRQM 0x12
|
||||
#define MAX77620_REG_STATLBT 0x13
|
||||
#define MAX77620_REG_STATSD 0x14
|
||||
#define MAX77620_REG_ONOFFSTAT 0x15
|
||||
|
||||
/* SD and LDO Registers */
|
||||
#define MAX77620_REG_SD0 0x16
|
||||
#define MAX77620_REG_SD1 0x17
|
||||
#define MAX77620_REG_SD2 0x18
|
||||
#define MAX77620_REG_SD3 0x19
|
||||
#define MAX77620_REG_SD4 0x1A
|
||||
#define MAX77620_SDX_VOLT_MASK 0xFF
|
||||
#define MAX77620_SD0_VOLT_MASK 0x3F
|
||||
#define MAX77620_SD1_VOLT_MASK 0x7F
|
||||
#define MAX77620_LDO_VOLT_MASK 0x3F
|
||||
#define MAX77620_REG_DVSSD0 0x1B
|
||||
#define MAX77620_REG_DVSSD1 0x1C
|
||||
#define MAX77620_REG_SD0_CFG 0x1D
|
||||
#define MAX77620_REG_SD1_CFG 0x1E
|
||||
#define MAX77620_REG_SD2_CFG 0x1F
|
||||
#define MAX77620_REG_SD3_CFG 0x20
|
||||
#define MAX77620_REG_SD4_CFG 0x21
|
||||
#define MAX77620_REG_SD_CFG2 0x22
|
||||
#define MAX77620_REG_LDO0_CFG 0x23
|
||||
#define MAX77620_REG_LDO0_CFG2 0x24
|
||||
#define MAX77620_REG_LDO1_CFG 0x25
|
||||
#define MAX77620_REG_LDO1_CFG2 0x26
|
||||
#define MAX77620_REG_LDO2_CFG 0x27
|
||||
#define MAX77620_REG_LDO2_CFG2 0x28
|
||||
#define MAX77620_REG_LDO3_CFG 0x29
|
||||
#define MAX77620_REG_LDO3_CFG2 0x2A
|
||||
#define MAX77620_REG_LDO4_CFG 0x2B
|
||||
#define MAX77620_REG_LDO4_CFG2 0x2C
|
||||
#define MAX77620_REG_LDO5_CFG 0x2D
|
||||
#define MAX77620_REG_LDO5_CFG2 0x2E
|
||||
#define MAX77620_REG_LDO6_CFG 0x2F
|
||||
#define MAX77620_REG_LDO6_CFG2 0x30
|
||||
#define MAX77620_REG_LDO7_CFG 0x31
|
||||
#define MAX77620_REG_LDO7_CFG2 0x32
|
||||
#define MAX77620_REG_LDO8_CFG 0x33
|
||||
#define MAX77620_REG_LDO8_CFG2 0x34
|
||||
#define MAX77620_LDO_POWER_MODE_MASK 0xC0
|
||||
#define MAX77620_LDO_POWER_MODE_SHIFT 6
|
||||
#define MAX77620_POWER_MODE_NORMAL 3
|
||||
#define MAX77620_POWER_MODE_LPM 2
|
||||
#define MAX77620_POWER_MODE_GLPM 1
|
||||
#define MAX77620_POWER_MODE_DISABLE 0
|
||||
#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2)
|
||||
#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1)
|
||||
#define MAX77620_LDO_CFG2_ADE_DISABLE (0 << 1)
|
||||
#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1)
|
||||
#define MAX77620_LDO_CFG2_SS_MASK (1 << 0)
|
||||
#define MAX77620_LDO_CFG2_SS_FAST (1 << 0)
|
||||
#define MAX77620_LDO_CFG2_SS_SLOW 0
|
||||
|
||||
#define MAX77620_REG_LDO_CFG3 0x35
|
||||
#define MAX77620_TRACK4_MASK (1 << 5)
|
||||
#define MAX77620_TRACK4_SHIFT 5
|
||||
|
||||
#define MAX77620_LDO_SLEW_RATE_MASK 0x1
|
||||
|
||||
#define MAX77620_REG_GPIO0 0x36
|
||||
#define MAX77620_REG_GPIO1 0x37
|
||||
#define MAX77620_REG_GPIO2 0x38
|
||||
#define MAX77620_REG_GPIO3 0x39
|
||||
#define MAX77620_REG_GPIO4 0x3A
|
||||
#define MAX77620_REG_GPIO5 0x3B
|
||||
#define MAX77620_REG_GPIO6 0x3C
|
||||
#define MAX77620_REG_GPIO7 0x3D
|
||||
#define MAX77620_REG_PUE_GPIO 0x3E
|
||||
#define MAX77620_REG_PDE_GPIO 0x3F
|
||||
#define MAX77620_REG_AME_GPIO 0x40
|
||||
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN (0 << 0)
|
||||
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
|
||||
#define MAX77620_CNFG_GPIO_DIR_OUTPUT (0 << 1)
|
||||
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
|
||||
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW (0 << 3)
|
||||
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
|
||||
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
|
||||
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
|
||||
|
||||
#define MAX77620_REG_ONOFFCNFG1 0x41
|
||||
#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7)
|
||||
#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38
|
||||
#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3
|
||||
#define MAX77620_ONOFFCNFG1_SLPEN (1 << 2)
|
||||
#define MAX77620_ONOFFCNFG1_PWR_OFF (1 << 1)
|
||||
#define MAX20024_ONOFFCNFG1_CLRSE 0x18
|
||||
|
||||
#define MAX77620_REG_ONOFFCNFG2 0x42
|
||||
#define MAX77620_ONOFFCNFG2_SFT_RST_WK (1 << 7)
|
||||
#define MAX77620_ONOFFCNFG2_WD_RST_WK (1 << 6)
|
||||
#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK (1 << 5)
|
||||
#define MAX77620_ONOFFCNFG2_WK_ALARM1 (1 << 2)
|
||||
#define MAX77620_ONOFFCNFG2_WK_EN0 (1 << 0)
|
||||
|
||||
/* FPS Registers */
|
||||
#define MAX77620_REG_FPS_CFG0 0x43
|
||||
#define MAX77620_REG_FPS_CFG1 0x44
|
||||
#define MAX77620_REG_FPS_CFG2 0x45
|
||||
#define MAX77620_REG_FPS_LDO0 0x46
|
||||
#define MAX77620_REG_FPS_LDO1 0x47
|
||||
#define MAX77620_REG_FPS_LDO2 0x48
|
||||
#define MAX77620_REG_FPS_LDO3 0x49
|
||||
#define MAX77620_REG_FPS_LDO4 0x4A
|
||||
#define MAX77620_REG_FPS_LDO5 0x4B
|
||||
#define MAX77620_REG_FPS_LDO6 0x4C
|
||||
#define MAX77620_REG_FPS_LDO7 0x4D
|
||||
#define MAX77620_REG_FPS_LDO8 0x4E
|
||||
#define MAX77620_REG_FPS_SD0 0x4F
|
||||
#define MAX77620_REG_FPS_SD1 0x50
|
||||
#define MAX77620_REG_FPS_SD2 0x51
|
||||
#define MAX77620_REG_FPS_SD3 0x52
|
||||
#define MAX77620_REG_FPS_SD4 0x53
|
||||
#define MAX77620_REG_FPS_NONE 0
|
||||
#define MAX77620_FPS_SRC_MASK 0xC0
|
||||
#define MAX77620_FPS_SRC_SHIFT 6
|
||||
#define MAX77620_FPS_PU_PERIOD_MASK 0x38
|
||||
#define MAX77620_FPS_PU_PERIOD_SHIFT 3
|
||||
#define MAX77620_FPS_PD_PERIOD_MASK 0x07
|
||||
#define MAX77620_FPS_PD_PERIOD_SHIFT 0
|
||||
|
||||
/* Minimum and maximum FPS period time (in microseconds) are
|
||||
* different for MAX77620 and Max20024.
|
||||
*/
|
||||
#define MAX77620_FPS_COUNT 3
|
||||
|
||||
#define MAX77620_FPS_PERIOD_MIN_US 40
|
||||
#define MAX20024_FPS_PERIOD_MIN_US 20
|
||||
|
||||
#define MAX77620_FPS_PERIOD_MAX_US 2560
|
||||
#define MAX20024_FPS_PERIOD_MAX_US 5120
|
||||
|
||||
#define MAX77620_REG_FPS_GPIO1 0x54
|
||||
#define MAX77620_REG_FPS_GPIO2 0x55
|
||||
#define MAX77620_REG_FPS_GPIO3 0x56
|
||||
#define MAX77620_FPS_TIME_PERIOD_MASK 0x38
|
||||
#define MAX77620_FPS_TIME_PERIOD_SHIFT 3
|
||||
#define MAX77620_FPS_EN_SRC_MASK 0x06
|
||||
#define MAX77620_FPS_EN_SRC_SHIFT 1
|
||||
#define MAX77620_FPS_ENFPS_SW_MASK 0x01
|
||||
#define MAX77620_FPS_ENFPS_SW 0x01
|
||||
|
||||
#define MAX77620_REG_FPS_RSO 0x57
|
||||
#define MAX77620_REG_CID0 0x58
|
||||
#define MAX77620_REG_CID1 0x59
|
||||
#define MAX77620_REG_CID2 0x5A
|
||||
#define MAX77620_REG_CID3 0x5B
|
||||
#define MAX77620_REG_CID4 0x5C
|
||||
#define MAX77620_REG_CID5 0x5D
|
||||
|
||||
#define MAX77620_REG_DVSSD4 0x5E
|
||||
#define MAX20024_REG_MAX_ADD 0x70
|
||||
|
||||
#define MAX77620_CID_DIDM_MASK 0xF0
|
||||
#define MAX77620_CID_DIDM_SHIFT 4
|
||||
|
||||
/* CNCG2SD */
|
||||
#define MAX77620_SD_CNF2_ROVS_EN_SD1 (1 << 1)
|
||||
#define MAX77620_SD_CNF2_ROVS_EN_SD0 (1 << 2)
|
||||
|
||||
/* Device Identification Metal */
|
||||
#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF)
|
||||
/* Device Indentification OTP */
|
||||
#define MAX77620_CID5_DIDO(n) ((n) & 0xF)
|
||||
|
||||
/* SD CNFG1 */
|
||||
#define MAX77620_SD_SR_MASK 0xC0
|
||||
#define MAX77620_SD_SR_SHIFT 6
|
||||
#define MAX77620_SD_POWER_MODE_MASK 0x30
|
||||
#define MAX77620_SD_POWER_MODE_SHIFT 4
|
||||
#define MAX77620_SD_CFG1_ADE_MASK (1 << 3)
|
||||
#define MAX77620_SD_CFG1_ADE_DISABLE 0
|
||||
#define MAX77620_SD_CFG1_ADE_ENABLE (1 << 3)
|
||||
#define MAX77620_SD_FPWM_MASK 0x04
|
||||
#define MAX77620_SD_FPWM_SHIFT 2
|
||||
#define MAX77620_SD_FSRADE_MASK 0x01
|
||||
#define MAX77620_SD_FSRADE_SHIFT 0
|
||||
#define MAX77620_SD_CFG1_FPWM_SD_MASK (1 << 2)
|
||||
#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0
|
||||
#define MAX77620_SD_CFG1_FPWM_SD_FPWM (1 << 2)
|
||||
#define MAX20024_SD_CFG1_MPOK_MASK (1 << 1)
|
||||
#define MAX77620_SD_CFG1_FSRADE_SD_MASK (1 << 0)
|
||||
#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
|
||||
#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0)
|
||||
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE3 (1 << 3)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE4 (1 << 4)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE5 (1 << 5)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE6 (1 << 6)
|
||||
#define MAX77620_IRQ_LVL2_GPIO_EDGE7 (1 << 7)
|
||||
|
||||
/* Interrupts */
|
||||
enum {
|
||||
MAX77620_IRQ_TOP_GLBL, /* Low-Battery */
|
||||
MAX77620_IRQ_TOP_SD, /* SD power fail */
|
||||
MAX77620_IRQ_TOP_LDO, /* LDO power fail */
|
||||
MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */
|
||||
MAX77620_IRQ_TOP_RTC, /* RTC */
|
||||
MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */
|
||||
MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */
|
||||
MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */
|
||||
MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */
|
||||
MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */
|
||||
};
|
||||
|
||||
/* GPIOs */
|
||||
enum {
|
||||
MAX77620_GPIO0,
|
||||
MAX77620_GPIO1,
|
||||
MAX77620_GPIO2,
|
||||
MAX77620_GPIO3,
|
||||
MAX77620_GPIO4,
|
||||
MAX77620_GPIO5,
|
||||
MAX77620_GPIO6,
|
||||
MAX77620_GPIO7,
|
||||
MAX77620_GPIO_NR,
|
||||
};
|
||||
|
||||
/* FPS Source */
|
||||
enum max77620_fps_src {
|
||||
MAX77620_FPS_SRC_0,
|
||||
MAX77620_FPS_SRC_1,
|
||||
MAX77620_FPS_SRC_2,
|
||||
MAX77620_FPS_SRC_NONE,
|
||||
MAX77620_FPS_SRC_DEF,
|
||||
};
|
||||
|
||||
enum max77620_chip_id {
|
||||
MAX77620,
|
||||
MAX20024,
|
||||
};
|
||||
|
||||
#endif /* _MFD_MAX77620_H_ */
|
109
libraries/libexosphere/source/pmic/max7762x.h
Normal file
109
libraries/libexosphere/source/pmic/max7762x.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2019 CTCaer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MAX7762X_H_
|
||||
#define _MAX7762X_H_
|
||||
|
||||
/*
|
||||
* Switch Power domains (max77620):
|
||||
* Name | Usage | uV step | uV min | uV default | uV max | Init
|
||||
*-------+---------------+---------+--------+------------+---------+------------------
|
||||
* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
|
||||
* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1)
|
||||
* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv)
|
||||
* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 |
|
||||
* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1)
|
||||
* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
|
||||
* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 |
|
||||
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
|
||||
* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 |
|
||||
* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
|
||||
* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V
|
||||
* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 |
|
||||
* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 |
|
||||
*/
|
||||
|
||||
/*
|
||||
* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode
|
||||
* MAX77620_REG_GPIOx: 0x9 sets output and enable
|
||||
*/
|
||||
|
||||
/*! MAX77620 partitions. */
|
||||
#define REGULATOR_SD0 0
|
||||
#define REGULATOR_SD1 1
|
||||
#define REGULATOR_SD2 2
|
||||
#define REGULATOR_SD3 3
|
||||
#define REGULATOR_LDO0 4
|
||||
#define REGULATOR_LDO1 5
|
||||
#define REGULATOR_LDO2 6
|
||||
#define REGULATOR_LDO3 7
|
||||
#define REGULATOR_LDO4 8
|
||||
#define REGULATOR_LDO5 9
|
||||
#define REGULATOR_LDO6 10
|
||||
#define REGULATOR_LDO7 11
|
||||
#define REGULATOR_LDO8 12
|
||||
#define REGULATOR_MAX 12
|
||||
|
||||
#define MAX77621_CPU_I2C_ADDR 0x1B
|
||||
#define MAX77621_GPU_I2C_ADDR 0x1C
|
||||
|
||||
#define MAX77621_VOUT_REG 0
|
||||
#define MAX77621_VOUT_DVC_REG 1
|
||||
#define MAX77621_CONTROL1_REG 2
|
||||
#define MAX77621_CONTROL2_REG 3
|
||||
|
||||
/* MAX77621_VOUT */
|
||||
#define MAX77621_VOUT_DISABLE (0 << 7)
|
||||
#define MAX77621_VOUT_ENABLE (1 << 7)
|
||||
#define MAX77621_VOUT_MASK 0x7F
|
||||
#define MAX77621_VOUT_0_95V 0x37
|
||||
#define MAX77621_VOUT_1_09V 0x4F
|
||||
|
||||
/* MAX77621_VOUT_DVC_DVS */
|
||||
#define MAX77621_DVS_VOUT_MASK 0x7F
|
||||
|
||||
/* MAX77621_CONTROL1 */
|
||||
#define MAX77621_SNS_ENABLE (1 << 7)
|
||||
#define MAX77621_FPWM_EN_M (1 << 6)
|
||||
#define MAX77621_NFSR_ENABLE (1 << 5)
|
||||
#define MAX77621_AD_ENABLE (1 << 4)
|
||||
#define MAX77621_BIAS_ENABLE (1 << 3)
|
||||
#define MAX77621_FREQSHIFT_9PER (1 << 2)
|
||||
|
||||
#define MAX77621_RAMP_12mV_PER_US 0x0
|
||||
#define MAX77621_RAMP_25mV_PER_US 0x1
|
||||
#define MAX77621_RAMP_50mV_PER_US 0x2
|
||||
#define MAX77621_RAMP_200mV_PER_US 0x3
|
||||
#define MAX77621_RAMP_MASK 0x3
|
||||
|
||||
/* MAX77621_CONTROL2 */
|
||||
#define MAX77621_WDTMR_ENABLE (1 << 6)
|
||||
#define MAX77621_DISCH_ENBABLE (1 << 5)
|
||||
#define MAX77621_FT_ENABLE (1 << 4)
|
||||
#define MAX77621_T_JUNCTION_120 (1 << 7)
|
||||
|
||||
#define MAX77621_CKKADV_TRIP_DISABLE 0xC
|
||||
#define MAX77621_CKKADV_TRIP_75mV_PER_US 0x0
|
||||
#define MAX77621_CKKADV_TRIP_150mV_PER_US 0x4
|
||||
#define MAX77621_CKKADV_TRIP_75mV_PER_US_HIST_DIS 0x8
|
||||
|
||||
#define MAX77621_INDUCTOR_MIN_30_PER 0x0
|
||||
#define MAX77621_INDUCTOR_NOMINAL 0x1
|
||||
#define MAX77621_INDUCTOR_PLUS_30_PER 0x2
|
||||
#define MAX77621_INDUCTOR_PLUS_60_PER 0x3
|
||||
|
||||
#endif
|
135
libraries/libexosphere/source/pmic/pmic_api.cpp
Normal file
135
libraries/libexosphere/source/pmic/pmic_api.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "max77620.h"
|
||||
#include "max7762x.h"
|
||||
|
||||
namespace ams::pmic {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int I2cAddressEristaMax77621 = 0x1B;
|
||||
constexpr inline int I2cAddressMarikoMax77812_A = 0x31;
|
||||
constexpr inline int I2cAddressMarikoMax77812_B = 0x33;
|
||||
|
||||
|
||||
/* https://github.com/Atmosphere-NX/Atmosphere/blob/master/emummc/source/power/max7762x.h */
|
||||
/* TODO: Find datasheet, link to it instead. */
|
||||
/* NOTE: Tentatively, Max77620 "mostly" matches https://datasheets.maximintegrated.com/en/ds/MAX77863.pdf. */
|
||||
/* This does not contain Max77621 documentation, though. */
|
||||
constexpr inline int Max77620RegisterGpio0 = 0x36;
|
||||
constexpr inline int Max77620RegisterAmeGpio = 0x40;
|
||||
|
||||
constexpr inline int Max77621RegisterVOut = 0x00;
|
||||
constexpr inline int Max77621RegisterVOutDvc = 0x01;
|
||||
constexpr inline int Max77621RegisterControl1 = 0x02;
|
||||
constexpr inline int Max77621RegisterControl2 = 0x03;
|
||||
|
||||
|
||||
/* https://datasheets.maximintegrated.com/en/ds/MAX77812.pdf */
|
||||
constexpr inline int Max77812RegisterEnCtrl = 0x06;
|
||||
constexpr inline int Max77812RegisterM4VOut = 0x26;
|
||||
|
||||
void Max77620EnableGpio(int gpio) {
|
||||
u8 val;
|
||||
|
||||
/* Clear the AE for the GPIO */
|
||||
if (i2c::Query(std::addressof(val), sizeof(val), i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio)) {
|
||||
val &= ~(1 << gpio);
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterAmeGpio, val);
|
||||
}
|
||||
|
||||
/* Set GPIO_DRV_PUSHPULL (bit 0), GPIO_OUTPUT_VAL_HIGH (bit 3). */
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterGpio0 + gpio, MAX77620_CNFG_GPIO_DRV_PUSHPULL | MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH);
|
||||
}
|
||||
|
||||
void EnableVddCpuErista() {
|
||||
/* Enable GPIO 5. */
|
||||
/* TODO: What does this control? */
|
||||
Max77620EnableGpio(5);
|
||||
|
||||
/* Configure Max77621 control registers. */
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl1, MAX77621_AD_ENABLE | MAX77621_NFSR_ENABLE | MAX77621_SNS_ENABLE | MAX77621_RAMP_12mV_PER_US);
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterControl2, MAX77621_T_JUNCTION_120 | MAX77621_WDTMR_ENABLE | MAX77621_CKKADV_TRIP_75mV_PER_US| MAX77621_INDUCTOR_NOMINAL);
|
||||
|
||||
/* Configure Max77621 VOut to 0.95v */
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V);
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOutDvc, MAX77621_VOUT_ENABLE | MAX77621_VOUT_0_95V);
|
||||
}
|
||||
|
||||
void DisableVddCpuErista() {
|
||||
/* Disable Max77621 VOut. */
|
||||
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_DISABLE);
|
||||
}
|
||||
|
||||
int GetI2cAddressForMarikoMax77812(Regulator regulator) {
|
||||
switch (regulator) {
|
||||
case Regulator_Mariko_Max77812_A: return I2cAddressMarikoMax77812_A;
|
||||
case Regulator_Mariko_Max77812_B: return I2cAddressMarikoMax77812_B;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void EnableVddCpuMariko(Regulator regulator) {
|
||||
const int address = GetI2cAddressForMarikoMax77812(regulator);
|
||||
|
||||
/* Set EN_M3_LPM to enable BUCK Master 3 low power mode. */
|
||||
u8 ctrl;
|
||||
if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) {
|
||||
ctrl |= 0x40;
|
||||
i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl);
|
||||
}
|
||||
|
||||
/* Set BUCK Master 4 output voltage to 110. */
|
||||
i2c::SendByte(i2c::Port_5, address, Max77812RegisterM4VOut, 110);
|
||||
}
|
||||
|
||||
void DisableVddCpuMariko(Regulator regulator) {
|
||||
const int address = GetI2cAddressForMarikoMax77812(regulator);
|
||||
|
||||
/* Clear EN_M3_LPM to disable BUCK Master 3 low power mode. */
|
||||
u8 ctrl;
|
||||
if (i2c::Query(std::addressof(ctrl), sizeof(ctrl), i2c::Port_5, address, Max77812RegisterEnCtrl)) {
|
||||
ctrl &= ~0x40;
|
||||
i2c::SendByte(i2c::Port_5, address, Max77812RegisterEnCtrl, ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EnableVddCpu(Regulator regulator) {
|
||||
switch (regulator) {
|
||||
case Regulator_Erista_Max77621:
|
||||
return EnableVddCpuErista();
|
||||
case Regulator_Mariko_Max77812_A:
|
||||
case Regulator_Mariko_Max77812_B:
|
||||
return EnableVddCpuMariko(regulator);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void DisableVddCpu(Regulator regulator) {
|
||||
switch (regulator) {
|
||||
case Regulator_Erista_Max77621:
|
||||
return DisableVddCpuErista();
|
||||
case Regulator_Mariko_Max77812_A:
|
||||
case Regulator_Mariko_Max77812_B:
|
||||
return DisableVddCpuMariko(regulator);
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
209
libraries/libexosphere/source/se/se_aes.cpp
Normal file
209
libraries/libexosphere/source/se/se_aes.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int AesKeySizeMax = 256 / BITSIZEOF(u8);
|
||||
|
||||
enum AesMode {
|
||||
AesMode_Aes128 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY128 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY128 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET,
|
||||
AesMode_Aes192 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY192 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY192 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET,
|
||||
AesMode_Aes256 = ((SE_CONFIG_ENC_MODE_AESMODE_KEY256 << SE_CONFIG_ENC_MODE_OFFSET) | (SE_CONFIG_DEC_MODE_AESMODE_KEY256 << SE_CONFIG_DEC_MODE_OFFSET)) >> SE_CONFIG_DEC_MODE_OFFSET,
|
||||
};
|
||||
|
||||
enum MemoryInterface {
|
||||
MemoryInterface_Ahb = SE_CRYPTO_CONFIG_MEMIF_AHB,
|
||||
MemoryInterface_Mc = SE_CRYPTO_CONFIG_MEMIF_MCCIF,
|
||||
};
|
||||
|
||||
constexpr inline u32 AesConfigEcb = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE));
|
||||
|
||||
void SetConfig(volatile SecurityEngineRegisters *SE, bool encrypt, SE_CONFIG_DST dst) {
|
||||
reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128),
|
||||
SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128),
|
||||
SE_REG_BITS_ENUM_SEL(CONFIG_ENC_ALG, encrypt, AES_ENC, NOP),
|
||||
SE_REG_BITS_ENUM_SEL(CONFIG_DEC_ALG, encrypt, NOP, AES_DEC),
|
||||
SE_REG_BITS_VALUE (CONFIG_DST, dst));
|
||||
}
|
||||
|
||||
void SetAesConfig(volatile SecurityEngineRegisters *SE, int slot, bool encrypt, u32 config) {
|
||||
const u32 encoded = reg::Encode(SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB),
|
||||
SE_REG_BITS_VALUE (CRYPTO_CONFIG_KEY_INDEX, slot),
|
||||
SE_REG_BITS_ENUM_SEL(CRYPTO_CONFIG_CORE_SEL, encrypt, ENCRYPT, DECRYPT));
|
||||
|
||||
reg::Write(SE->SE_CRYPTO_CONFIG, (config | encoded));
|
||||
}
|
||||
|
||||
void SetBlockCount(volatile SecurityEngineRegisters *SE, int count) {
|
||||
reg::Write(SE->SE_CRYPTO_LAST_BLOCK, count - 1);
|
||||
}
|
||||
|
||||
void UpdateAesMode(volatile SecurityEngineRegisters *SE, AesMode mode) {
|
||||
reg::ReadWrite(SE->SE_CONFIG, REG_BITS_VALUE(16, 16, mode));
|
||||
}
|
||||
|
||||
// void UpdateMemoryInterface(volatile SecurityEngineRegisters *SE, MemoryInterface memif) {
|
||||
// reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_VALUE(CRYPTO_CONFIG_MEMIF, memif));
|
||||
// }
|
||||
|
||||
void SetEncryptedAesKey(int dst_slot, int kek_slot, const void *key, size_t key_size, AesMode mode) {
|
||||
AMS_ABORT_UNLESS(key_size <= AesKeySizeMax);
|
||||
AMS_ABORT_UNLESS(0 <= dst_slot && dst_slot < AesKeySlotCount);
|
||||
AMS_ABORT_UNLESS(0 <= kek_slot && kek_slot < AesKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Configure for single AES ECB decryption to key table. */
|
||||
SetConfig(SE, false, SE_CONFIG_DST_KEYTABLE);
|
||||
SetAesConfig(SE, kek_slot, false, AesConfigEcb);
|
||||
UpdateAesMode(SE, mode);
|
||||
SetBlockCount(SE, 1);
|
||||
|
||||
/* Select the destination keyslot. */
|
||||
reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, dst_slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3));
|
||||
|
||||
/* Ensure that the se sees the keydata we want it to. */
|
||||
hw::FlushDataCache(key, key_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, key, key_size);
|
||||
}
|
||||
|
||||
void EncryptAes(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, AesMode mode) {
|
||||
/* If nothing to decrypt, succeed. */
|
||||
if (src_size == 0) { return; }
|
||||
|
||||
/* Validate input. */
|
||||
AMS_ABORT_UNLESS(dst_size == AesBlockSize);
|
||||
AMS_ABORT_UNLESS(src_size == AesBlockSize);
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Configure for AES-ECB encryption to memory. */
|
||||
SetConfig(SE, true, SE_CONFIG_DST_MEMORY);
|
||||
SetAesConfig(SE, slot, true, AesConfigEcb);
|
||||
UpdateAesMode(SE, mode);
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClearAesKeySlot(int slot) {
|
||||
/* Validate the key slot. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
/* Select the keyslot. */
|
||||
reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, i));
|
||||
|
||||
/* Write the data. */
|
||||
SE->SE_CRYPTO_KEYTABLE_DATA = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void LockAesKeySlot(int slot, u32 flags) {
|
||||
/* Validate the key slot. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Set non per-key flags. */
|
||||
if ((flags & ~KeySlotLockFlags_PerKey) != 0) {
|
||||
/* TODO: KeySlotLockFlags_DstKeyTableOnly is Mariko-only. How should we handle this? */
|
||||
/* TODO: Mariko bit support. */
|
||||
reg::ReadWrite(SE->SE_CRYPTO_KEYTABLE_ACCESS[slot], REG_BITS_VALUE(0, 7, ~flags));
|
||||
}
|
||||
|
||||
/* Set per-key flag. */
|
||||
if ((flags & KeySlotLockFlags_PerKey) != 0) {
|
||||
reg::ReadWrite(SE->SE_CRYPTO_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void SetAesKey(int slot, const void *key, size_t key_size) {
|
||||
/* Validate the key slot and key size. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
|
||||
AMS_ABORT_UNLESS(key_size <= AesKeySizeMax);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Set each key word in order. */
|
||||
const u32 *key_u32 = static_cast<const u32 *>(key);
|
||||
const int num_words = key_size / sizeof(u32);
|
||||
for (int i = 0; i < num_words; ++i) {
|
||||
/* Select the keyslot. */
|
||||
reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot),
|
||||
SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, KEY),
|
||||
SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i));
|
||||
|
||||
/* Set the key word. */
|
||||
SE->SE_CRYPTO_KEYTABLE_DATA = *(key_u32++);
|
||||
}
|
||||
}
|
||||
|
||||
void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size) {
|
||||
return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes128);
|
||||
}
|
||||
|
||||
void SetEncryptedAesKey256(int dst_slot, int kek_slot, const void *key, size_t key_size) {
|
||||
return SetEncryptedAesKey(dst_slot, kek_slot, key, key_size, AesMode_Aes256);
|
||||
}
|
||||
|
||||
void EncryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) {
|
||||
return EncryptAes(dst, dst_size, slot, src, src_size, AesMode_Aes128);
|
||||
}
|
||||
|
||||
void DecryptAes128(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) {
|
||||
/* If nothing to decrypt, succeed. */
|
||||
if (src_size == 0) { return; }
|
||||
|
||||
/* Validate input. */
|
||||
AMS_ABORT_UNLESS(dst_size == AesBlockSize);
|
||||
AMS_ABORT_UNLESS(src_size == AesBlockSize);
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Configure for AES-ECB decryption to memory. */
|
||||
SetConfig(SE, false, SE_CONFIG_DST_MEMORY);
|
||||
SetAesConfig(SE, slot, false, AesConfigEcb);
|
||||
|
||||
ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
}
|
138
libraries/libexosphere/source/se/se_execute.cpp
Normal file
138
libraries/libexosphere/source/se/se_execute.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
struct LinkedListEntry {
|
||||
u32 zero;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
static_assert(util::is_pod<LinkedListEntry>::value);
|
||||
|
||||
uintptr_t GetPhysicalAddress(const void *ptr) {
|
||||
const uintptr_t virt_address = reinterpret_cast<uintptr_t>(ptr);
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
u64 phys_address;
|
||||
__asm__ __volatile__("at s1e3r, %[virt]; mrs %[phys], par_el1" : [phys]"=r"(phys_address) : [virt]"r"(virt_address) : "memory", "cc");
|
||||
return (phys_address & 0x0000FFFFFFFFF000ul) | (virt_address & 0x0000000000000FFFul);
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
return virt_address;
|
||||
#else
|
||||
#error "Unknown architecture for Tegra Security Engine physical address translation"
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr void SetLinkedListEntry(LinkedListEntry *entry, const void *ptr, size_t size) {
|
||||
/* Clear the zero field. */
|
||||
entry->zero = 0;
|
||||
|
||||
/* Set the address. */
|
||||
if (ptr != nullptr) {
|
||||
entry->address = GetPhysicalAddress(ptr);
|
||||
entry->size = static_cast<u32>(size);
|
||||
} else {
|
||||
entry->address = 0;
|
||||
entry->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void StartOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op) {
|
||||
/* Write back the current values of the error and interrupt status. */
|
||||
reg::Write(SE->SE_ERR_STATUS, reg::Read(SE->SE_ERR_STATUS));
|
||||
reg::Write(SE->SE_INT_STATUS, reg::Read(SE->SE_INT_STATUS));
|
||||
|
||||
/* Write the operation. */
|
||||
reg::Write(SE->SE_OPERATION, SE_REG_BITS_VALUE(OPERATION_OP, op));
|
||||
}
|
||||
|
||||
void WaitForOperationComplete(volatile SecurityEngineRegisters *SE) {
|
||||
/* Spin until the operation is done. */
|
||||
while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_SE_OP_DONE, CLEAR))) { /* ... */ }
|
||||
|
||||
/* Check for operation success. */
|
||||
ValidateAesOperationResult(SE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
/* Set the linked list entries. */
|
||||
LinkedListEntry src_entry;
|
||||
LinkedListEntry dst_entry;
|
||||
|
||||
SetLinkedListEntry(std::addressof(src_entry), src, src_size);
|
||||
SetLinkedListEntry(std::addressof(dst_entry), dst, dst_size);
|
||||
|
||||
/* Ensure the linked list entry data is seen correctly. */
|
||||
hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry));
|
||||
hw::FlushDataCache(std::addressof(dst_entry), sizeof(dst_entry));
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Configure the linked list addresses. */
|
||||
reg::Write(SE->SE_IN_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(src_entry))));
|
||||
reg::Write(SE->SE_OUT_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(dst_entry))));
|
||||
|
||||
/* Start the operation. */
|
||||
StartOperation(SE, op);
|
||||
|
||||
/* Wait for the operation to complete. */
|
||||
WaitForOperationComplete(SE);
|
||||
}
|
||||
|
||||
void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
/* Validate sizes. */
|
||||
AMS_ABORT_UNLESS(dst_size <= AesBlockSize);
|
||||
AMS_ABORT_UNLESS(src_size == AesBlockSize);
|
||||
|
||||
/* Set the block count to 1. */
|
||||
reg::Write(SE->SE_CRYPTO_LAST_BLOCK, 0);
|
||||
|
||||
/* Create an aligned buffer. */
|
||||
util::AlignedBuffer<hw::DataCacheLineSize, AesBlockSize> aligned;
|
||||
std::memcpy(aligned, src, AesBlockSize);
|
||||
hw::FlushDataCache(aligned, AesBlockSize);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, aligned, AesBlockSize, aligned, AesBlockSize);
|
||||
|
||||
/* Ensure that the CPU will see the correct output. */
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
hw::FlushDataCache(aligned, AesBlockSize);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Copy the output to the destination. */
|
||||
std::memcpy(dst, aligned, dst_size);
|
||||
}
|
||||
|
||||
void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE) {
|
||||
/* Ensure no error occurred. */
|
||||
AMS_ABORT_UNLESS(reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_ERR_STAT, CLEAR)));
|
||||
|
||||
/* Ensure the security engine is idle. */
|
||||
AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE)));
|
||||
|
||||
/* Ensure there is no error status. */
|
||||
AMS_ABORT_UNLESS(reg::Read(SE->SE_ERR_STATUS) == 0);
|
||||
}
|
||||
|
||||
}
|
28
libraries/libexosphere/source/se/se_execute.hpp
Normal file
28
libraries/libexosphere/source/se/se_execute.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_registers.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
volatile SecurityEngineRegisters *GetRegisters();
|
||||
|
||||
void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
void ValidateAesOperationResult(volatile SecurityEngineRegisters *SE);
|
||||
|
||||
}
|
114
libraries/libexosphere/source/se/se_management.cpp
Normal file
114
libraries/libexosphere/source/se/se_management.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetAddress();
|
||||
constinit DoneHandler g_done_handler = nullptr;
|
||||
|
||||
}
|
||||
|
||||
volatile SecurityEngineRegisters *GetRegisters() {
|
||||
return reinterpret_cast<volatile SecurityEngineRegisters *>(g_register_address);
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
auto *SE = GetRegisters();
|
||||
AMS_ABORT_UNLESS(reg::HasValue(SE->SE_STATUS, SE_REG_BITS_ENUM(STATUS_STATE, IDLE)));
|
||||
}
|
||||
|
||||
void SetSecure(bool secure) {
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Set the security software setting. */
|
||||
if (secure) {
|
||||
reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE));
|
||||
} else {
|
||||
reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, NONSECURE));
|
||||
}
|
||||
|
||||
/* Read the status register to force an update. */
|
||||
reg::Read(SE->SE_SE_SECURITY);
|
||||
}
|
||||
|
||||
void SetTzramSecure() {
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Set the TZRAM setting to secure. */
|
||||
SE->SE_TZRAM_SECURITY = SE_TZRAM_SETTING_SECURE;
|
||||
}
|
||||
|
||||
void SetPerKeySecure() {
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Clear AES PerKey security. */
|
||||
SE->SE_CRYPTO_SECURITY_PERKEY = 0;
|
||||
|
||||
/* Clear RSA PerKey security. */
|
||||
SE->SE_RSA_SECURITY_PERKEY = 0;
|
||||
|
||||
/* Update PERKEY_SETTING to secure. */
|
||||
reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE));
|
||||
}
|
||||
|
||||
void Lockout() {
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Lock access to the AES keyslots. */
|
||||
for (int i = 0; i < AesKeySlotCount; ++i) {
|
||||
SE->SE_CRYPTO_KEYTABLE_ACCESS[i] = 0;
|
||||
}
|
||||
|
||||
/* Lock access to the RSA keyslots. */
|
||||
for (int i = 0; i < RsaKeySlotCount; ++i) {
|
||||
SE->SE_RSA_KEYTABLE_ACCESS[i] = 0;
|
||||
}
|
||||
|
||||
/* Set Per Key secure. */
|
||||
SetPerKeySecure();
|
||||
|
||||
/* Configure SE_SECURITY. */
|
||||
{
|
||||
reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_HARD_SETTING, SECURE),
|
||||
SE_REG_BITS_ENUM(SECURITY_ENG_DIS, DISABLE),
|
||||
SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE),
|
||||
SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE));
|
||||
}
|
||||
}
|
||||
|
||||
void HandleInterrupt() {
|
||||
/* Get the registers. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Disable the SE interrupt. */
|
||||
reg::Write(SE->SE_INT_ENABLE, 0);
|
||||
|
||||
/* Execute the handler if we have one. */
|
||||
if (const auto handler = g_done_handler; handler != nullptr) {
|
||||
g_done_handler = nullptr;
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
249
libraries/libexosphere/source/se/se_registers.hpp
Normal file
249
libraries/libexosphere/source/se/se_registers.hpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
struct SecurityEngineRegisters {
|
||||
u32 SE_SE_SECURITY;
|
||||
u32 SE_TZRAM_SECURITY;
|
||||
u32 SE_OPERATION;
|
||||
u32 SE_INT_ENABLE;
|
||||
u32 SE_INT_STATUS;
|
||||
u32 SE_CONFIG;
|
||||
u32 SE_IN_LL_ADDR;
|
||||
u32 SE_IN_CUR_BYTE_ADDR;
|
||||
u32 SE_IN_CUR_LL_ID;
|
||||
u32 SE_OUT_LL_ADDR;
|
||||
u32 SE_OUT_CUR_BYTE_ADDR;
|
||||
u32 SE_OUT_CUR_LL_ID;
|
||||
u32 SE_HASH_RESULT[0x10];
|
||||
u32 SE_CTX_SAVE_CONFIG;
|
||||
u32 _0x74[0x63];
|
||||
u32 SE_SHA_CONFIG;
|
||||
u32 SE_SHA_MSG_LENGTH[0x4];
|
||||
u32 SE_SHA_MSG_LEFT[0x4];
|
||||
u32 _0x224[0x17];
|
||||
u32 SE_CRYPTO_SECURITY_PERKEY;
|
||||
u32 SE_CRYPTO_KEYTABLE_ACCESS[0x10];
|
||||
u32 _0x2C4[0x10];
|
||||
u32 SE_CRYPTO_CONFIG;
|
||||
u32 SE_CRYPTO_LINEAR_CTR[0x4];
|
||||
u32 SE_CRYPTO_LAST_BLOCK;
|
||||
u32 SE_CRYPTO_KEYTABLE_ADDR;
|
||||
u32 SE_CRYPTO_KEYTABLE_DATA;
|
||||
u32 _0x324[0x3];
|
||||
u32 SE_CRYPTO_KEYTABLE_DST;
|
||||
u32 _0x334[0x3];
|
||||
u32 SE_RNG_CONFIG;
|
||||
u32 SE_RNG_SRC_CONFIG;
|
||||
u32 SE_RNG_RESEED_INTERVAL;
|
||||
u32 _0x34C[0x2D];
|
||||
u32 SE_RSA_CONFIG;
|
||||
u32 SE_RSA_KEY_SIZE;
|
||||
u32 SE_RSA_EXP_SIZE;
|
||||
u32 SE_RSA_SECURITY_PERKEY;
|
||||
u32 SE_RSA_KEYTABLE_ACCESS[0x2];
|
||||
u32 _0x418[0x2];
|
||||
u32 SE_RSA_KEYTABLE_ADDR;
|
||||
u32 SE_RSA_KEYTABLE_DATA;
|
||||
u32 SE_RSA_OUTPUT[0x40];
|
||||
u32 _0x528[0xB6];
|
||||
u32 SE_STATUS;
|
||||
u32 SE_ERR_STATUS;
|
||||
u32 SE_MISC;
|
||||
u32 SE_SPARE;
|
||||
u32 SE_ENTROPY_DEBUG_COUNTER;
|
||||
u32 _0x814;
|
||||
u32 _0x818;
|
||||
u32 _0x81C;
|
||||
u32 _0x820[0x5F8];
|
||||
};
|
||||
static_assert(util::is_pod<SecurityEngineRegisters>::value);
|
||||
static_assert(sizeof(SecurityEngineRegisters) == secmon::MemoryRegionPhysicalDeviceSecurityEngine.GetSize());
|
||||
|
||||
static_assert(AesKeySlotCount == util::size(SecurityEngineRegisters{}.SE_CRYPTO_KEYTABLE_ACCESS));
|
||||
static_assert(RsaKeySlotCount == util::size(SecurityEngineRegisters{}.SE_RSA_KEYTABLE_ACCESS));
|
||||
|
||||
#define SE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SE, NAME)
|
||||
#define SE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SE, NAME, VALUE)
|
||||
#define SE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SE, NAME, ENUM)
|
||||
#define SE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
|
||||
|
||||
#define DEFINE_SE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SE, NAME, __OFFSET__, __WIDTH__)
|
||||
#define DEFINE_SE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE)
|
||||
#define DEFINE_SE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
|
||||
#define DEFINE_SE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
|
||||
#define DEFINE_SE_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 (SE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
|
||||
|
||||
#define DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(NAME, __OFFSET__) \
|
||||
REG_DEFINE_NAMED_REG(SE, NAME, __OFFSET__, 1); \
|
||||
\
|
||||
enum SE_##NAME { \
|
||||
SE_##NAME##_##CLEAR = 0, \
|
||||
SE_##NAME##_##ACTIVE = 1, \
|
||||
SE_##NAME##_##SW_CLEAR = 1, \
|
||||
};
|
||||
|
||||
/* SE_STATUS. */
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(STATUS_STATE, 0, IDLE, BUSY, WAIT_OUT, WAIT_IN);
|
||||
|
||||
/* SE_SECURITY */
|
||||
DEFINE_SE_REG_BIT_ENUM(SECURITY_HARD_SETTING, 0, SECURE, NONSECURE);
|
||||
DEFINE_SE_REG_BIT_ENUM(SECURITY_ENG_DIS, 1, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(SECURITY_PERKEY_SETTING, 2, SECURE, NONSECURE);
|
||||
DEFINE_SE_REG_BIT_ENUM(SECURITY_SOFT_SETTING, 16, SECURE, NONSECURE);
|
||||
|
||||
/* SE_TZRAM_SECURITY */
|
||||
DEFINE_SE_REG(TZRAM_SETTING, 0, BITSIZEOF(u32));
|
||||
constexpr inline u32 SE_TZRAM_SETTING_SECURE = 0;
|
||||
|
||||
/* SE_OPERATION */
|
||||
DEFINE_SE_REG_THREE_BIT_ENUM(OPERATION_OP, 0, ABORT, START, RESTART_OUT, CTX_SAVE, RESTART_IN, RESERVED_5, RESERVED_6, RESERVED_7);
|
||||
|
||||
/* SE_INT_ENABLE */
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_LL_BUF_RD, 0, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_IN_DONE, 1, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_LL_BUF_WR, 2, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_OUT_DONE, 3, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_SE_OP_DONE, 4, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_RESEED_CNTR_EXHAUSTED, 5, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(INT_ENABLE_ERR_STAT, 16, DISABLE, ENABLE);
|
||||
|
||||
/* SE_INT_STATUS */
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_LL_BUF_RD, 0);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_IN_DONE, 1);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_LL_BUF_WR, 2);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_OUT_DONE, 3);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_SE_OP_DONE, 4);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_RESEED_CNTR_EXHAUSTED, 5);
|
||||
DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_ERR_STAT, 16);
|
||||
|
||||
/* SE_CONFIG */
|
||||
DEFINE_SE_REG(CONFIG_DST, 2, 3);
|
||||
DEFINE_SE_REG(CONFIG_DEC_ALG, 8, 4);
|
||||
DEFINE_SE_REG(CONFIG_ENC_ALG, 12, 4);
|
||||
DEFINE_SE_REG(CONFIG_DEC_MODE, 16, 8);
|
||||
DEFINE_SE_REG(CONFIG_ENC_MODE, 24, 8);
|
||||
|
||||
enum SE_CONFIG_DST {
|
||||
SE_CONFIG_DST_MEMORY = 0,
|
||||
SE_CONFIG_DST_HASH_REG = 1,
|
||||
SE_CONFIG_DST_KEYTABLE = 2,
|
||||
SE_CONFIG_DST_SRK = 3,
|
||||
SE_CONFIG_DST_RSA_REG = 4,
|
||||
};
|
||||
|
||||
enum SE_CONFIG_DEC_ALG {
|
||||
SE_CONFIG_DEC_ALG_NOP = 0,
|
||||
SE_CONFIG_DEC_ALG_AES_DEC = 1,
|
||||
};
|
||||
|
||||
enum SE_CONFIG_ENC_ALG {
|
||||
SE_CONFIG_ENC_ALG_NOP = 0,
|
||||
SE_CONFIG_ENC_ALG_AES_ENC = 1,
|
||||
SE_CONFIG_ENC_ALG_RNG = 2,
|
||||
SE_CONFIG_ENC_ALG_SHA = 3,
|
||||
SE_CONFIG_ENC_ALG_RSA = 4,
|
||||
};
|
||||
|
||||
enum SE_CONFIG_DEC_MODE {
|
||||
SE_CONFIG_DEC_MODE_AESMODE_KEY128 = 0,
|
||||
SE_CONFIG_DEC_MODE_AESMODE_KEY192 = 1,
|
||||
SE_CONFIG_DEC_MODE_AESMODE_KEY256 = 2,
|
||||
};
|
||||
|
||||
enum SE_CONFIG_ENC_MODE {
|
||||
SE_CONFIG_ENC_MODE_AESMODE_KEY128 = 0,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_KEY192 = 1,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_KEY256 = 2,
|
||||
|
||||
SE_CONFIG_ENC_MODE_AESMODE_SHA1 = 1,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_SHA224 = 4,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_SHA256 = 5,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_SHA384 = 6,
|
||||
SE_CONFIG_ENC_MODE_AESMODE_SHA512 = 7,
|
||||
};
|
||||
|
||||
|
||||
/* SE_CRYPTO_KEYTABLE_ADDR */
|
||||
DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, 0, 4);
|
||||
DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_WORD, 0, 2);
|
||||
DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, 0, 3);
|
||||
|
||||
enum SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD {
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_0 = 0u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_1 = 1u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_2 = 2u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_3 = 3u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_4 = 4u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_5 = 5u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_6 = 6u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_KEY_7 = 7u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_0 = 8u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_1 = 9u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_2 = 10u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_OIV_3 = 11u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_0 = 12u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_1 = 13u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_2 = 14u,
|
||||
SE_CRYPTO_KEYTABLE_ADDR_KEYIV_WORD_UIV_3 = 15u,
|
||||
};
|
||||
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, 2, ORIGINAL_IV, UPDATED_IV);
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, 3, KEY, IV);
|
||||
|
||||
DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, 4, 4);
|
||||
|
||||
/* SE_RSA_KEYTABLE_ADDR */
|
||||
DEFINE_SE_REG(RSA_KEYTABLE_ADDR_WORD_ADDR, 0, 6);
|
||||
DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_EXPMOD_SEL, 6, EXPONENT, MODULUS);
|
||||
DEFINE_SE_REG(RSA_KEYTABLE_ADDR_KEY_SLOT, 7, 1);
|
||||
DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_INPUT_MODE, 8, REGISTER, MEMORY);
|
||||
|
||||
/* SE_RSA_KEYTABLE_ACCESS */
|
||||
DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYREAD, 0, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUPDATE, 1, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ACCESS_KEYUSE, 2, DISABLE, ENABLE);
|
||||
|
||||
/* SE_CRYPTO_CONFIG */
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_HASH_ENB, 0, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_XOR_POS, 1, BYPASS, RESERVED, TOP, BOTTOM);
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_INPUT_SEL, 3, MEMORY, RANDOM, INIT_AESOUT, LINEAR_CTR);
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_CONFIG_VCTRAM_SEL, 5, MEMORY, RESERVED, INIT_AESOUT, INIT_PREV_MEMORY);
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_IV_SELECT, 7, ORIGINAL, UPDATED);
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_CORE_SEL, 8, DECRYPT, ENCRYPT);
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_KEYSCH_BYPASS, 10, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG(CRYPTO_CONFIG_CTR_CNTN, 11, 8);
|
||||
DEFINE_SE_REG(CRYPTO_CONFIG_KEY_INDEX, 24, 4);
|
||||
DEFINE_SE_REG_BIT_ENUM(CRYPTO_CONFIG_MEMIF, 31, AHB, MCCIF);
|
||||
|
||||
/* SE_CRYPTO_KEYTABLE_DST */
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, 0, KEYS_0_3, KEYS_4_7, ORIGINAL_IV, UPDATED_IV);
|
||||
DEFINE_SE_REG(CRYPTO_KEYTABLE_DST_KEY_INDEX, 8, 4);
|
||||
|
||||
/* SE_RNG_CONFIG */
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_MODE, 0, NORMAL, FORCE_INSTANTIATION, FORCE_RESEED, RESERVED3);
|
||||
DEFINE_SE_REG_TWO_BIT_ENUM(RNG_CONFIG_SRC, 2, NONE, ENTROPY, LFSR, RESERVED3);
|
||||
|
||||
/* SE_RNG_SRC_CONFIG */
|
||||
DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, 0, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, 1, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG_BIT_ENUM(RNG_SRC_CONFIG_HW_DISABLE_CYA, 2, DISABLE, ENABLE);
|
||||
DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_SUBSAMPLE, 4, 3);
|
||||
DEFINE_SE_REG(RNG_SRC_CONFIG_RO_ENTROPY_DATA_FLUSH, 8, 1);
|
||||
|
||||
}
|
146
libraries/libexosphere/source/se/se_rng.cpp
Normal file
146
libraries/libexosphere/source/se/se_rng.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int RngReseedInterval = 70001;
|
||||
|
||||
void ConfigRng(volatile SecurityEngineRegisters *SE, SE_CONFIG_DST dst, SE_RNG_CONFIG_MODE mode) {
|
||||
/* Configure the engine to do RNG encryption. */
|
||||
reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM (CONFIG_ENC_MODE, AESMODE_KEY128),
|
||||
SE_REG_BITS_ENUM (CONFIG_DEC_MODE, AESMODE_KEY128),
|
||||
SE_REG_BITS_ENUM (CONFIG_ENC_ALG, RNG),
|
||||
SE_REG_BITS_ENUM (CONFIG_DEC_ALG, NOP),
|
||||
SE_REG_BITS_VALUE(CONFIG_DST, dst));
|
||||
|
||||
reg::Write(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_ENUM (CRYPTO_CONFIG_MEMIF, AHB),
|
||||
SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_CORE_SEL, ENCRYPT),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, MEMORY),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, RANDOM),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BYPASS),
|
||||
SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE));
|
||||
|
||||
/* Configure the RNG to use Entropy as source. */
|
||||
reg::Write(SE->SE_RNG_CONFIG, SE_REG_BITS_ENUM(RNG_CONFIG_SRC, ENTROPY), SE_REG_BITS_VALUE(RNG_CONFIG_MODE, mode));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeRandom() {
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Lock the entropy source. */
|
||||
reg::Write(SE->SE_RNG_SRC_CONFIG, SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, ENABLE),
|
||||
SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, ENABLE));
|
||||
|
||||
/* Set the reseed interval to force a reseed every 70000 blocks. */
|
||||
SE->SE_RNG_RESEED_INTERVAL = RngReseedInterval;
|
||||
|
||||
/* Initialize the DRBG. */
|
||||
{
|
||||
u8 dummy_buf[AesBlockSize];
|
||||
|
||||
/* Configure the engine to force drbg instantiation by writing random to memory. */
|
||||
ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_FORCE_INSTANTIATION);
|
||||
|
||||
/* Configure to do a single RNG block operation to trigger DRBG init. */
|
||||
SE->SE_CRYPTO_LAST_BLOCK = 0;
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, dummy_buf, sizeof(dummy_buf), nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateRandomBytes(void *dst, size_t size) {
|
||||
/* If we're not generating any bytes, there's nothing to do. */
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Determine how many blocks to generate. */
|
||||
const size_t num_blocks = size / AesBlockSize;
|
||||
const size_t aligned_size = num_blocks * AesBlockSize;
|
||||
const size_t fractional = size - aligned_size;
|
||||
|
||||
/* Configure the RNG to generate random to memory. */
|
||||
ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_NORMAL);
|
||||
|
||||
/* Generate as many aligned blocks as we can. */
|
||||
if (aligned_size > 0) {
|
||||
/* Configure the engine to generate the right number of blocks. */
|
||||
SE->SE_CRYPTO_LAST_BLOCK = num_blocks - 1;
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, dst, aligned_size, nullptr, 0);
|
||||
}
|
||||
|
||||
/* Generate a single block to output. */
|
||||
if (fractional > 0) {
|
||||
ExecuteOperationSingleBlock(SE, static_cast<u8 *>(dst) + aligned_size, fractional, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SetRandomKey(int slot) {
|
||||
/* NOTE: Nintendo does not validate the destination keyslot here. */
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Configure the RNG to output to the keytable. */
|
||||
ConfigRng(SE, SE_CONFIG_DST_KEYTABLE, SE_RNG_CONFIG_MODE_NORMAL);
|
||||
|
||||
/* Configure the keytable destination to be the low part of the key. */
|
||||
reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_0_3));
|
||||
|
||||
/* Configure a single block operation. */
|
||||
SE->SE_CRYPTO_LAST_BLOCK = 0;
|
||||
|
||||
/* Execute the operation to generate a random low-part of the key. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0);
|
||||
|
||||
/* Configure the keytable destination to be the high part of the key. */
|
||||
reg::Write(SE->SE_CRYPTO_KEYTABLE_DST, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_DST_KEY_INDEX, slot), SE_REG_BITS_ENUM(CRYPTO_KEYTABLE_DST_WORD_QUAD, KEYS_4_7));
|
||||
|
||||
/* Execute the operation to generate a random high-part of the key. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
void GenerateSrk() {
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Configure the RNG to output to SRK and force a reseed. */
|
||||
ConfigRng(SE, SE_CONFIG_DST_SRK, SE_RNG_CONFIG_MODE_FORCE_RESEED);
|
||||
|
||||
/* Configure a single block operation. */
|
||||
SE->SE_CRYPTO_LAST_BLOCK = 0;
|
||||
|
||||
/* Execute the operation. */
|
||||
ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
}
|
125
libraries/libexosphere/source/se/se_rsa.cpp
Normal file
125
libraries/libexosphere/source/se/se_rsa.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
struct RsaKeyInfo {
|
||||
int modulus_size_val;
|
||||
int exponent_size_val;
|
||||
};
|
||||
|
||||
constinit RsaKeyInfo g_rsa_key_infos[RsaKeySlotCount] = {};
|
||||
|
||||
void ClearRsaKeySlot(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) {
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
constexpr int NumWords = se::RsaSize / sizeof(u32);
|
||||
for (int i = 0; i < NumWords; ++i) {
|
||||
/* Select the keyslot word. */
|
||||
reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i));
|
||||
|
||||
/* Clear the keyslot word. */
|
||||
SE->SE_RSA_KEYTABLE_DATA = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetRsaKey(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) {
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
const int num_words = key_size / sizeof(u32);
|
||||
for (int i = 0; i < num_words; ++i) {
|
||||
/* Select the keyslot word. */
|
||||
reg::Write(SE->SE_RSA_KEYTABLE_ADDR, SE_REG_BITS_ENUM (RSA_KEYTABLE_ADDR_INPUT_MODE, REGISTER),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_KEY_SLOT, slot),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_EXPMOD_SEL, expmod),
|
||||
SE_REG_BITS_VALUE(RSA_KEYTABLE_ADDR_WORD_ADDR, i));
|
||||
|
||||
/* Get the word. */
|
||||
const u32 word = util::LoadBigEndian(static_cast<const u32 *>(key) + (num_words - 1 - i));
|
||||
|
||||
/* Write the keyslot word. */
|
||||
SE->SE_RSA_KEYTABLE_DATA = word;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClearRsaKeySlot(int slot) {
|
||||
/* Validate the key slot. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount);
|
||||
|
||||
/* Clear the info. */
|
||||
g_rsa_key_infos[slot] = {};
|
||||
|
||||
/* Clear the modulus. */
|
||||
ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS);
|
||||
|
||||
/* Clear the exponent. */
|
||||
ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT);
|
||||
}
|
||||
|
||||
void LockRsaKeySlot(int slot, u32 flags) {
|
||||
/* Validate the key slot. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount);
|
||||
|
||||
/* Get the engine. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Set non per-key flags. */
|
||||
if ((flags & ~KeySlotLockFlags_PerKey) != 0) {
|
||||
/* Pack the flags into the expected format. */
|
||||
u32 value = 0;
|
||||
value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 0) : 0;
|
||||
value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 1) : 0;
|
||||
value |= ((flags & KeySlotLockFlags_KeyRead) == 0) ? (1u << 2) : 0;
|
||||
|
||||
reg::Write(SE->SE_RSA_KEYTABLE_ACCESS[slot], SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYREAD, (flags & KeySlotLockFlags_KeyRead) != 0, DISABLE, ENABLE),
|
||||
SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUPDATE, (flags & KeySlotLockFlags_KeyWrite) != 0, DISABLE, ENABLE),
|
||||
SE_REG_BITS_ENUM_SEL(RSA_KEYTABLE_ACCESS_KEYUSE, (flags & KeySlotLockFlags_KeyUse) != 0, DISABLE, ENABLE));
|
||||
}
|
||||
|
||||
/* Set per-key flag. */
|
||||
if ((flags & KeySlotLockFlags_PerKey) != 0) {
|
||||
reg::ReadWrite(SE->SE_RSA_SECURITY_PERKEY, REG_BITS_VALUE(slot, 1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size) {
|
||||
/* Validate the key slot and sizes. */
|
||||
AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount);
|
||||
AMS_ABORT_UNLESS(mod_size <= RsaSize);
|
||||
AMS_ABORT_UNLESS(exp_size <= RsaSize);
|
||||
|
||||
/* Set the sizes in the info. */
|
||||
auto &info = g_rsa_key_infos[slot];
|
||||
info.modulus_size_val = (mod_size / 64) - 1;
|
||||
info.exponent_size_val = (exp_size / 4);
|
||||
|
||||
/* Set the modulus and exponent. */
|
||||
SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size);
|
||||
SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size);
|
||||
}
|
||||
|
||||
}
|
59
libraries/libexosphere/source/se/se_suspend.cpp
Normal file
59
libraries/libexosphere/source/se/se_suspend.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "se_execute.hpp"
|
||||
|
||||
namespace ams::se {
|
||||
|
||||
namespace {
|
||||
|
||||
bool TestRegister(volatile u32 &r, u16 v) {
|
||||
return (static_cast<u16>(reg::Read(r))) == v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ValidateStickyBits(const StickyBits &bits) {
|
||||
/* Get the registers. */
|
||||
auto *SE = GetRegisters();
|
||||
|
||||
/* Check SE_SECURITY. */
|
||||
if (!TestRegister(SE->SE_SE_SECURITY, bits.se_security)) { return false; }
|
||||
|
||||
/* Check TZRAM_SECURITY. */
|
||||
if (!TestRegister(SE->SE_TZRAM_SECURITY, bits.tzram_security)) { return false; }
|
||||
|
||||
/* Check CRYPTO_SECURITY_PERKEY. */
|
||||
if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.crypto_security_perkey)) { return false; }
|
||||
|
||||
/* Check CRYPTO_KEYTABLE_ACCESS. */
|
||||
for (int i = 0; i < AesKeySlotCount; ++i) {
|
||||
if (!TestRegister(SE->SE_CRYPTO_KEYTABLE_ACCESS[i], bits.crypto_keytable_access[i])) { return false; }
|
||||
}
|
||||
|
||||
/* Test RSA_SCEURITY_PERKEY */
|
||||
if (!TestRegister(SE->SE_CRYPTO_SECURITY_PERKEY, bits.rsa_security_perkey)) { return false; }
|
||||
|
||||
/* Check RSA_KEYTABLE_ACCESS. */
|
||||
for (int i = 0; i < RsaKeySlotCount; ++i) {
|
||||
if (!TestRegister(SE->SE_RSA_KEYTABLE_ACCESS[i], bits.rsa_keytable_access[i])) { return false; }
|
||||
}
|
||||
|
||||
/* All sticky bits are valid. */
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
35
libraries/libexosphere/source/tsec/tsec_api.cpp
Normal file
35
libraries/libexosphere/source/tsec/tsec_api.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::tsec {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
/* Set the tsec host1x syncpoint (160) to be secure. */
|
||||
/* TODO: constexpr value. */
|
||||
reg::ReadWrite(0x500038F8, REG_BITS_VALUE(0, 1, 0));
|
||||
|
||||
/* Clear the tsec host1x syncpoint. */
|
||||
reg::Write(0x50003300, 0);
|
||||
}
|
||||
|
||||
}
|
113
libraries/libexosphere/source/uart/uart_api.cpp
Normal file
113
libraries/libexosphere/source/uart/uart_api.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "uart_registers.hpp"
|
||||
|
||||
namespace ams::uart {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const u16 UartRegisterOffsets[Port_Count] = {
|
||||
secmon::MemoryRegionPhysicalDeviceUartA.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(),
|
||||
secmon::MemoryRegionPhysicalDeviceUartB.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(),
|
||||
secmon::MemoryRegionPhysicalDeviceUartC.GetAddress() - secmon::MemoryRegionPhysicalDeviceUart.GetAddress(),
|
||||
};
|
||||
|
||||
constinit uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceUart.GetAddress();
|
||||
|
||||
volatile UartRegisters *GetRegisters(Port port) {
|
||||
return reinterpret_cast<volatile UartRegisters *>(g_register_address + UartRegisterOffsets[port]);
|
||||
}
|
||||
|
||||
void WaitSymbols(int baud, u32 num) {
|
||||
util::WaitMicroSeconds(util::DivideUp(1'000'000, baud) * num);
|
||||
}
|
||||
|
||||
void WaitCycles(int baud, u32 num) {
|
||||
util::WaitMicroSeconds(util::DivideUp(1'000'000, 16 * baud) * num);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WaitFifoNotFull(volatile UartRegisters *uart) {
|
||||
while ((uart->lsr & UART_LSR_TX_FIFO_FULL) != 0) { /* ... */ }
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WaitFifoNotEmpty(volatile UartRegisters *uart) {
|
||||
while ((uart->lsr & UART_LSR_RX_FIFO_EMPTY) != 0) { /* ... */ }
|
||||
}
|
||||
|
||||
void WaitIdle(volatile UartRegisters *uart, u32 vendor_state) {
|
||||
if (vendor_state & UART_VENDOR_STATE_TX_IDLE) {
|
||||
while ((uart->lsr & UART_LSR_TMTY) == 0) { /* ... */ }
|
||||
}
|
||||
|
||||
if (vendor_state & UART_VENDOR_STATE_RX_IDLE) {
|
||||
while ((uart->lsr & UART_LSR_RDR) != 0) { /* ... */ }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
void Initialize(Port port, int baud_rate, u32 flags) {
|
||||
/* Get the registers. */
|
||||
auto *uart = GetRegisters(port);
|
||||
|
||||
/* Parse flags. */
|
||||
const bool inverted = (flags & Flag_Inverted) != 0;
|
||||
|
||||
/* Calculate the baud rate divisor. */
|
||||
constexpr u32 UartClock = 408000000;
|
||||
const u32 divisor = (UartClock + (baud_rate * 16) / 2) / (baud_rate * 16);
|
||||
|
||||
/* Disable DLAB and all interrupts. */
|
||||
uart->lcr = uart->lcr & ~UART_LCR_DLAB;
|
||||
uart->ier = 0;
|
||||
uart->mcr = 0;
|
||||
|
||||
/* Setup the uart in FIFO mode. */
|
||||
uart->lcr = UART_LCR_DLAB | UART_LCR_WD_LENGTH_8;
|
||||
uart->dll = static_cast<u8>(divisor);
|
||||
uart->dlh = static_cast<u8>(divisor >> 8);
|
||||
uart->lcr = uart->lcr & ~UART_LCR_DLAB;
|
||||
reg::Read(std::addressof(uart->spr));
|
||||
|
||||
/* Wait three symbols. */
|
||||
WaitSymbols(baud_rate, 3);
|
||||
|
||||
/* Enable FIFO with default settings. */
|
||||
uart->fcr = UART_FCR_FCR_EN_FIFO;
|
||||
uart->irda_csr = inverted ? UART_IRDA_CSR_INVERT_TXD : 0;
|
||||
reg::Read(std::addressof(uart->spr));
|
||||
|
||||
/* Wait three cycles. */
|
||||
WaitCycles(baud_rate, 3);
|
||||
|
||||
/* Flush the FIFO. */
|
||||
WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE);
|
||||
uart->fcr = uart->fcr | UART_FCR_RX_CLR | UART_FCR_TX_CLR;
|
||||
WaitCycles(baud_rate, 32);
|
||||
|
||||
/* Wait for idle state. */
|
||||
WaitIdle(uart, UART_VENDOR_STATE_TX_IDLE | UART_VENDOR_STATE_RX_IDLE);
|
||||
|
||||
/* Set scratch register to 0. */
|
||||
uart->spr = 0;
|
||||
}
|
||||
|
||||
}
|
180
libraries/libexosphere/source/uart/uart_registers.hpp
Normal file
180
libraries/libexosphere/source/uart/uart_registers.hpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::uart {
|
||||
|
||||
struct UartRegisters {
|
||||
union {
|
||||
u32 thr;
|
||||
u32 rbr;
|
||||
u32 dll;
|
||||
};
|
||||
union {
|
||||
u32 ier;
|
||||
u32 dlh;
|
||||
};
|
||||
union {
|
||||
u32 iir;
|
||||
u32 fcr;
|
||||
};
|
||||
u32 lcr;
|
||||
u32 mcr;
|
||||
u32 lsr;
|
||||
u32 msr;
|
||||
u32 spr;
|
||||
u32 irda_csr;
|
||||
u32 rx_fifo_cfg;
|
||||
u32 mie;
|
||||
u32 vendor_status;
|
||||
u32 reserved_30;
|
||||
u32 reserved_34;
|
||||
u32 reserved_38;
|
||||
u32 asr;
|
||||
};
|
||||
static_assert(util::is_pod<UartRegisters>::value);
|
||||
static_assert(sizeof(UartRegisters) == 0x40);
|
||||
|
||||
/* 36.3.12 UART_VENDOR_STATUS_0_0 */
|
||||
enum UartVendorStatus {
|
||||
UART_VENDOR_STATE_TX_IDLE = 1 << 0,
|
||||
UART_VENDOR_STATE_RX_IDLE = 1 << 1,
|
||||
|
||||
/* This bit is set to 1 when a read is issued to an empty FIFO and gets cleared on register read (sticky bit until read)
|
||||
0 = NO_UNDERRUN
|
||||
1 = UNDERRUN
|
||||
*/
|
||||
UART_VENDOR_STATE_RX_UNDERRUN = 1 << 2,
|
||||
|
||||
/* This bit is set to 1 when write data is issued to the TX FIFO when it is already full and gets cleared on register read (sticky bit until read)
|
||||
0 = NO_OVERRUN
|
||||
1 = OVERRUN
|
||||
*/
|
||||
UART_VENDOR_STATE_TX_OVERRUN = 1 << 3,
|
||||
|
||||
UART_VENDOR_STATE_RX_FIFO_COUNTER = 0b111111 << 16, /* reflects number of current entries in RX FIFO */
|
||||
UART_VENDOR_STATE_TX_FIFO_COUNTER = 0b111111 << 24 /* reflects number of current entries in TX FIFO */
|
||||
};
|
||||
|
||||
/* 36.3.6 UART_LSR_0 */
|
||||
enum UartLineStatus {
|
||||
UART_LSR_RDR = 1 << 0, /* Receiver Data Ready */
|
||||
UART_LSR_OVRF = 1 << 1, /* Receiver Overrun Error */
|
||||
UART_LSR_PERR = 1 << 2, /* Parity Error */
|
||||
UART_LSR_FERR = 1 << 3, /* Framing Error */
|
||||
UART_LSR_BRK = 1 << 4, /* BREAK condition detected on line */
|
||||
UART_LSR_THRE = 1 << 5, /* Transmit Holding Register is Empty -- OK to write data */
|
||||
UART_LSR_TMTY = 1 << 6, /* Transmit Shift Register empty status */
|
||||
UART_LSR_FIFOE = 1 << 7, /* Receive FIFO Error */
|
||||
UART_LSR_TX_FIFO_FULL = 1 << 8, /* Transmitter FIFO full status */
|
||||
UART_LSR_RX_FIFO_EMPTY = 1 << 9, /* Receiver FIFO empty status */
|
||||
};
|
||||
|
||||
/* 36.3.4 UART_LCR_0 */
|
||||
enum UartLineControl {
|
||||
UART_LCR_WD_LENGTH_5 = 0, /* word length 5 */
|
||||
UART_LCR_WD_LENGTH_6 = 1, /* word length 6 */
|
||||
UART_LCR_WD_LENGTH_7 = 2, /* word length 7 */
|
||||
UART_LCR_WD_LENGTH_8 = 3, /* word length 8 */
|
||||
|
||||
/* STOP:
|
||||
0 = Transmit 1 stop bit
|
||||
1 = Transmit 2 stop bits (receiver always checks for 1 stop bit)
|
||||
*/
|
||||
UART_LCR_STOP = 1 << 2,
|
||||
UART_LCR_PAR = 1 << 3, /* Parity enabled */
|
||||
UART_LCR_EVEN = 1 << 4, /* Even parity format. There will always be an even number of 1s in the binary representation (PAR = 1) */
|
||||
UART_LCR_SET_P = 1 << 5, /* Set (force) parity to value in LCR[4] */
|
||||
UART_LCR_SET_B = 1 << 6, /* Set BREAK condition -- Transmitter sends all zeroes to indicate BREAK */
|
||||
UART_LCR_DLAB = 1 << 7, /* Divisor Latch Access Bit (set to allow programming of the DLH, DLM Divisors) */
|
||||
};
|
||||
|
||||
/* 36.3.3 UART_IIR_FCR_0 */
|
||||
enum UartFifoControl {
|
||||
UART_FCR_FCR_EN_FIFO = 1 << 0, /* Enable the transmit and receive FIFOs. This bit should be enabled */
|
||||
UART_FCR_RX_CLR = 1 << 1, /* Clears the contents of the receive FIFO and resets its counter logic to 0 (the receive shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */
|
||||
UART_FCR_TX_CLR = 1 << 2, /* Clears the contents of the transmit FIFO and resets its counter logic to 0 (the transmit shift register is not cleared or altered). This bit returns to 0 after clearing the FIFOs */
|
||||
|
||||
/* DMA:
|
||||
0 = DMA_MODE_0
|
||||
1 = DMA_MODE_1
|
||||
*/
|
||||
UART_FCR_DMA = 1 << 3,
|
||||
|
||||
/* TX_TRIG
|
||||
0 = FIFO_COUNT_GREATER_16
|
||||
1 = FIFO_COUNT_GREATER_8
|
||||
2 = FIFO_COUNT_GREATER_4
|
||||
3 = FIFO_COUNT_GREATER_1
|
||||
*/
|
||||
UART_FCR_TX_TRIG = 3 << 4,
|
||||
UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_16 = 0 << 4,
|
||||
UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_8 = 1 << 4,
|
||||
UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_4 = 2 << 4,
|
||||
UART_FCR_TX_TRIG_FIFO_COUNT_GREATER_1 = 3 << 4,
|
||||
|
||||
/* RX_TRIG
|
||||
0 = FIFO_COUNT_GREATER_1
|
||||
1 = FIFO_COUNT_GREATER_4
|
||||
2 = FIFO_COUNT_GREATER_8
|
||||
3 = FIFO_COUNT_GREATER_16
|
||||
*/
|
||||
UART_FCR_RX_TRIG = 3 << 6,
|
||||
UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_1 = 0 << 6,
|
||||
UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_4 = 1 << 6,
|
||||
UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_8 = 2 << 6,
|
||||
UART_FCR_RX_TRIG_FIFO_COUNT_GREATER_16 = 3 << 6,
|
||||
};
|
||||
|
||||
/* 36.3.2 UART_IER_DLAB_0_0 */
|
||||
enum UartInterruptEnable {
|
||||
UART_IER_IE_RHR = 1 << 0, /* Interrupt enable for Received Data Interrupt */
|
||||
UART_IER_IE_THR = 1 << 1, /* Interrupt enable for Transmitter Holding Register Empty interrupt */
|
||||
UART_IER_IE_RXS = 1 << 2, /* Interrupt enable for Receiver Line Status Interrupt */
|
||||
UART_IER_IE_MSI = 1 << 3, /* Interrupt enable for Modem Status Interrupt */
|
||||
UART_IER_IE_RX_TIMEOUT = 1 << 4, /* Interrupt enable for RX FIFO timeout */
|
||||
UART_IER_IE_EORD = 1 << 5, /* Interrupt enable for Interrupt Enable for End of Received Data */
|
||||
};
|
||||
|
||||
/* 36.3.3 UART_IIR_FCR_0 */
|
||||
enum UartInterruptIdentification {
|
||||
UART_IIR_IS_STA = 1 << 0, /* Interrupt Pending if ZERO */
|
||||
UART_IIR_IS_PRI0 = 1 << 1, /* Encoded Interrupt ID Refer to IIR[3:0] table [36.3.3] */
|
||||
UART_IIR_IS_PRI1 = 1 << 2, /* Encoded Interrupt ID Refer to IIR[3:0] table */
|
||||
UART_IIR_IS_PRI2 = 1 << 3, /* Encoded Interrupt ID Refer to IIR[3:0] table */
|
||||
|
||||
/* FIFO Mode Status
|
||||
0 = 16450 mode (no FIFO)
|
||||
1 = 16550 mode (FIFO)
|
||||
*/
|
||||
UART_IIR_EN_FIFO = 3 << 6,
|
||||
UART_IIR_MODE_16450 = 0 << 6,
|
||||
UART_IIR_MODE_16550 = 1 << 6,
|
||||
};
|
||||
|
||||
/* 36.3.9 UART_IRDA_CSR_0 */
|
||||
enum UartIrDAPulseCodingCSR {
|
||||
UART_IRDA_CSR_INVERT_RXD = 1 << 0,
|
||||
UART_IRDA_CSR_INVERT_TXD = 1 << 1,
|
||||
UART_IRDA_CSR_INVERT_CTS = 1 << 2,
|
||||
UART_IRDA_CSR_INVERT_RTS = 1 << 3,
|
||||
|
||||
UART_IRDA_CSR_PWT_A_BAUD_PULSE_3_14 = 0 << 6,
|
||||
UART_IRDA_CSR_PWT_A_BAUD_PULSE_4_14 = 1 << 6,
|
||||
UART_IRDA_CSR_SIR_A = 1 << 7,
|
||||
};
|
||||
|
||||
}
|
50
libraries/libexosphere/source/util/util_api.cpp
Normal file
50
libraries/libexosphere/source/util/util_api.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit uintptr_t g_timer_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress();
|
||||
|
||||
ALWAYS_INLINE uintptr_t GetCurrentTimeRegisterAddress() {
|
||||
return g_timer_register_address + 0x10;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_timer_register_address = address;
|
||||
}
|
||||
|
||||
u32 GetMicroSeconds() {
|
||||
return reg::Read(GetCurrentTimeRegisterAddress());
|
||||
}
|
||||
|
||||
void WaitMicroSeconds(int us) {
|
||||
const u32 start = reg::Read(GetCurrentTimeRegisterAddress());
|
||||
u32 cur = start;
|
||||
while ((cur - start) <= static_cast<u32>(us)) {
|
||||
cur = reg::Read(GetCurrentTimeRegisterAddress());
|
||||
}
|
||||
}
|
||||
|
||||
void ClearMemory(void *ptr, size_t size) {
|
||||
std::memset(ptr, 0, size);
|
||||
}
|
||||
|
||||
}
|
99
libraries/libexosphere/source/wdt/wdt_api.cpp
Normal file
99
libraries/libexosphere/source/wdt/wdt_api.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::wdt {
|
||||
|
||||
namespace {
|
||||
|
||||
volatile uintptr_t g_register_address = secmon::MemoryRegionPhysicalDeviceTimer.GetAddress();
|
||||
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
NOINLINE void Reboot(uintptr_t registers) {
|
||||
__asm__ __volatile__(
|
||||
/* Get the current core. */
|
||||
"mrs x12, mpidr_el1\n"
|
||||
"and x12, x12, #0xFF\n"
|
||||
|
||||
/* Get the offsets of the registers we want to write */
|
||||
"mov x10, #0x8\n"
|
||||
"mov x11, #0x20\n"
|
||||
"madd x10, x10, x12, %[registers]\n"
|
||||
"madd x11, x11, x12, %[registers]\n"
|
||||
"add x10, x10, #0x60\n"
|
||||
"add x11, x11, #0x100\n"
|
||||
|
||||
/* Write the magic unlock pattern. */
|
||||
"mov w9, #0xC45A\n"
|
||||
"str w9, [x11, #0xC]\n"
|
||||
|
||||
/* Disable the counters. */
|
||||
"mov w9, #0x2\n"
|
||||
"str w9, [x11, #0x8]\n"
|
||||
|
||||
/* Start periodic timer. */
|
||||
"mov w9, #0xC0000000\n"
|
||||
"str w9, [x10]\n"
|
||||
|
||||
/* Set reboot source to the timer we started. */
|
||||
"mov w9, #0x8015\n"
|
||||
"add w9, w9, w12\n"
|
||||
"str w9, [x11]\n"
|
||||
|
||||
/* Enable the counters. */
|
||||
"mov w9, #0x1\n"
|
||||
"str w9, [x11, #0x8]\n"
|
||||
|
||||
/* Wait forever. */
|
||||
"1: b 1b"
|
||||
: [registers]"=&r"(registers)
|
||||
:
|
||||
: "x9", "x10", "x11", "x12", "memory"
|
||||
);
|
||||
}
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
NOINLINE void Reboot(uintptr_t registers) {
|
||||
/* Write the magic unlock pattern. */
|
||||
reg::Write(registers + 0x18C, 0xC45A);
|
||||
|
||||
/* Disable the counters. */
|
||||
reg::Write(registers + 0x188, 0x2);
|
||||
|
||||
/* Start periodic timer. */
|
||||
reg::Write(registers + 0x080, 0xC0000000);
|
||||
|
||||
/* Set reboot source to the timer we started. */
|
||||
reg::Write(registers + 0x180, 0x8019);
|
||||
|
||||
/* Enable the counters. */
|
||||
reg::Write(registers + 0x188, 0x1);
|
||||
|
||||
while (true) { /* ... */ }
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(uintptr_t address) {
|
||||
g_register_address = address;
|
||||
}
|
||||
|
||||
NOINLINE void Reboot() {
|
||||
const uintptr_t registers = g_register_address;
|
||||
Reboot(registers);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue