exo2: implement warmboot firmware

This commit is contained in:
Michael Scire
2020-06-11 01:30:30 -07:00
committed by SciresM
parent ff87ff2592
commit 293c213bf2
43 changed files with 1996 additions and 103 deletions

View File

@@ -20,6 +20,11 @@ namespace ams::fuse {
namespace {
struct BypassEntry {
u32 offset;
u32 value;
};
struct OdmWord2 {
using DeviceUniqueKeyGeneration = util::BitPack32::Field<0, 5, int>;
using Reserved = util::BitPack32::Field<5, 27, int>;
@@ -135,6 +140,12 @@ namespace ams::fuse {
constexpr inline int NumFuseIncrements = util::size(FuseVersionIncrementFirmwares);
constexpr const BypassEntry FuseBypassEntries[] = {
/* Don't configure any fuse bypass entries. */
};
constexpr inline int NumFuseBypassEntries = util::size(FuseBypassEntries);
/* Verify that the fuse version increment list is sorted. */
static_assert([] {
for (size_t i = 0; i < util::size(FuseVersionIncrementFirmwares) - 1; ++i) {
@@ -169,7 +180,7 @@ namespace ams::fuse {
}
void Lockout() {
reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_DISABLEREGPROGRAM_VAL, ENABLE));
reg::Write(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, ENABLE));
}
u32 ReadWord(int address) {
@@ -367,4 +378,49 @@ namespace ams::fuse {
return g_has_rcm_bug_patch;
}
bool IsOdmProductionMode() {
return reg::HasValue(GetChipRegisters().FUSE_SECURITY_MODE, FUSE_REG_BITS_ENUM(SECURITY_MODE_SECURITY_MODE, ENABLED));
}
void ConfigureFuseBypass() {
/* Make the fuse registers visible. */
clkrst::SetFuseVisibility(true);
/* Only perform bypass configuration if fuse programming is allowed. */
if (!reg::HasValue(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, DISABLE))) {
return;
}
/* Enable software writes to fuses. */
reg::ReadWrite(GetRegisters().FUSE_WRITE_ACCESS_SW, FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_CTRL, READWRITE),
FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_STATUS, WRITE));
/* Enable fuse bypass. */
reg::Write(GetRegisters().FUSE_FUSEBYPASS, FUSE_REG_BITS_ENUM(FUSEBYPASS_VAL, ENABLE));
/* Override fuses. */
for (const auto &entry : FuseBypassEntries) {
reg::Write(g_register_address + entry.offset, entry.value);
}
/* Disable software writes to fuses. */
reg::ReadWrite(GetRegisters().FUSE_WRITE_ACCESS_SW, FUSE_REG_BITS_ENUM(WRITE_ACCESS_SW_CTRL, READONLY));
/* NOTE: Here, NVidia almost certainly intends to *disable* fuse bypass, but they write enable instead... */
reg::Write(GetRegisters().FUSE_FUSEBYPASS, FUSE_REG_BITS_ENUM(FUSEBYPASS_VAL, ENABLE));
/* NOTE: Here, NVidia intends to disable fuse programming. However, they fuck up -- and *clear* the disable bit. */
/* It should be noted that this is a sticky bit, and thus software clears have no effect. */
reg::ReadWrite(GetRegisters().FUSE_DISABLEREGPROGRAM, FUSE_REG_BITS_ENUM(DISABLEREGPROGRAM_VAL, DISABLE));
/* Configure FUSE_PRIVATEKEYDISABLE_TZ_STICKY_BIT. */
constexpr const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
const bool key_invisible = reg::HasValue(PMC + APBDEV_PMC_SECURE_SCRATCH21, FUSE_REG_BITS_ENUM(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, KEY_INVISIBLE));
reg::ReadWrite(GetRegisters().FUSE_PRIVATEKEYDISABLE, FUSE_REG_BITS_ENUM_SEL(PRIVATEKEYDISABLE_TZ_STICKY_BIT_VAL, key_invisible, KEY_INVISIBLE, KEY_VISIBLE));
/* Write-lock PMC_SECURE_SCRATCH21. */
reg::ReadWrite(PMC + APBDEV_PMC_SEC_DISABLE2, PMC_REG_BITS_ENUM(SEC_DISABLE2_WRITE21, ON));
}
}

View File

@@ -241,6 +241,13 @@ namespace ams::fuse {
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);
DEFINE_FUSE_REG_BIT_ENUM(FUSEBYPASS_VAL, 0, DISABLE, ENABLE);
DEFINE_FUSE_REG_BIT_ENUM(DISABLEREGPROGRAM_VAL, 0, DISABLE, ENABLE);
DEFINE_FUSE_REG_BIT_ENUM(WRITE_ACCESS_SW_CTRL, 0, READWRITE, READONLY);
DEFINE_FUSE_REG_BIT_ENUM(WRITE_ACCESS_SW_STATUS, 16, NOWRITE, WRITE);
DEFINE_FUSE_REG_BIT_ENUM(SECURITY_MODE_SECURITY_MODE, 0, DISABLED, ENABLED);
}

View File

@@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "pinmux_registers.hpp"
namespace ams::pinmux {

View File

@@ -1,65 +0,0 @@
/*
* 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::pinmux {
#define PINMUX_AUX_GEN1_I2C_SCL (0x30BC)
#define PINMUX_AUX_GEN1_I2C_SDA (0x30C0)
#define PINMUX_AUX_PWR_I2C_SCL (0x30DC)
#define PINMUX_AUX_PWR_I2C_SDA (0x30E0)
#define PINMUX_AUX_UART1_TX (0x30E4)
#define PINMUX_AUX_UART1_RX (0x30E8)
#define PINMUX_AUX_UART1_RTS (0x30EC)
#define PINMUX_AUX_UART1_CTS (0x30F0)
#define PINMUX_AUX_UART2_TX (0x30F4)
#define PINMUX_AUX_UART2_RX (0x30F8)
#define PINMUX_AUX_UART2_RTS (0x30FC)
#define PINMUX_AUX_UART2_CTS (0x3100)
#define PINMUX_AUX_UART3_TX (0x3104)
#define PINMUX_AUX_UART3_RX (0x3108)
#define PINMUX_AUX_UART3_RTS (0x310C)
#define PINMUX_AUX_UART3_CTS (0x3110)
#define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME)
#define PINMUX_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PINMUX, NAME, VALUE)
#define PINMUX_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PINMUX, NAME, ENUM)
#define PINMUX_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PINMUX, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_PINMUX_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PINMUX, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_PINMUX_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_PINMUX_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_PINMUX_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_PINMUX_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PINMUX, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PUPD, 2, NONE, PULL_DOWN, PULL_UP, RSVD);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_TRISTATE, 4, PASSTHROUGH, TRISTATE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_PARK, 5, NORMAL, PARKED);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_INPUT, 6, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_LOCK, 7, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE);
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART1_PM, 0, UARTA, RSVD1, RSVD2, RSVD3);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART2_PM, 0, UARTB, I2S4A, RSVD2, UART);
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3);
}

View File

@@ -60,6 +60,10 @@ namespace ams::pmic {
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77620RegisterGpio0 + gpio, MAX77620_CNFG_GPIO_DRV_PUSHPULL | MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH);
}
void SetEnBitErista() {
i2c::SendByte(i2c::Port_5, I2cAddressEristaMax77621, Max77621RegisterVOut, MAX77621_VOUT_ENABLE);
}
void EnableVddCpuErista() {
/* Enable GPIO 5. */
/* TODO: What does this control? */
@@ -87,6 +91,11 @@ namespace ams::pmic {
}
}
void SetEnBitMariko(Regulator regulator) {
/* Set EN_M3_LPM to enable BUCK Master 3 low power mode. */
i2c::SendByte(i2c::Port_5, GetI2cAddressForMarikoMax77812(regulator), Max77812RegisterEnCtrl, 0x40);
}
void EnableVddCpuMariko(Regulator regulator) {
const int address = GetI2cAddressForMarikoMax77812(regulator);
@@ -118,6 +127,17 @@ namespace ams::pmic {
}
void SetEnBit(Regulator regulator) {
switch (regulator) {
case Regulator_Erista_Max77621:
return SetEnBitErista();
case Regulator_Mariko_Max77812_A:
case Regulator_Mariko_Max77812_B:
return SetEnBitMariko(regulator);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void EnableVddCpu(Regulator regulator) {
switch (regulator) {
case Regulator_Erista_Max77621:

View File

@@ -308,6 +308,37 @@ namespace ams::se {
ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size);
}
void DecryptAesCbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size, AesMode mode) {
/* If nothing to decrypt, succeed. */
if (src_size == 0) { return; }
/* Validate input. */
AMS_ABORT_UNLESS(iv_size == AesBlockSize);
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
/* Get the engine. */
auto *SE = GetRegisters();
/* Determine extents. */
const size_t num_blocks = src_size / AesBlockSize;
const size_t aligned_size = num_blocks * AesBlockSize;
AMS_ABORT_UNLESS(src_size == aligned_size);
/* Configure for aes-cbc encryption. */
SetConfig(SE, false, SE_CONFIG_DST_MEMORY);
SetAesConfig(SE, slot, false, AesConfigCbcDecrypt);
UpdateAesMode(SE, mode);
/* Set the iv. */
SetAesKeyIv(SE, slot, iv, iv_size);
/* Set the block count. */
SetBlockCount(SE, num_blocks);
/* Execute the operation. */
ExecuteOperation(SE, SE_OPERATION_OP_START, dst, dst_size, src, aligned_size);
}
void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) {
/* If nothing to decrypt, succeed. */
if (size == 0) { return; }
@@ -349,6 +380,35 @@ namespace ams::se {
}
}
void ClearAesKeyIv(int slot) {
/* Validate the key slot. */
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
/* Get the engine. */
auto *SE = GetRegisters();
/* Set each iv word in order. */
for (int i = 0; i < 4; ++i) {
/* Select the keyslot original iv. */
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, IV),
SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV),
SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i));
/* Set the iv word. */
SE->SE_CRYPTO_KEYTABLE_DATA = 0;
/* Select the keyslot updated iv. */
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, IV),
SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, UPDATED_IV),
SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i));
/* Set the iv word. */
SE->SE_CRYPTO_KEYTABLE_DATA = 0;
}
}
void LockAesKeySlot(int slot, u32 flags) {
/* Validate the key slot. */
AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount);
@@ -487,6 +547,14 @@ namespace ams::se {
return EncryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256);
}
void DecryptAes128Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) {
return DecryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes128);
}
void DecryptAes256Cbc(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) {
return DecryptAesCbc(dst, dst_size, slot, src, src_size, iv, iv_size, AesMode_Aes256);
}
void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) {
/* Validate the iv. */
AMS_ABORT_UNLESS(iv_size == AesBlockSize);