You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
9.0 KiB
274 lines
9.0 KiB
/*
|
|
* This file is part of the Trezor project, https://trezor.io/
|
|
*
|
|
* Copyright (c) SatoshiLabs
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that 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 <irq.h>
|
|
#include <supervise.h>
|
|
#include <tamper.h>
|
|
#include STM32_HAL_H
|
|
|
|
// Fixes a typo in CMSIS Device library for STM32U5
|
|
#undef TAMP_CR3_ITAMP7NOER_Msk
|
|
#undef TAMP_CR3_ITAMP7NOER
|
|
#define TAMP_CR3_ITAMP7NOER_Msk (0x1UL << TAMP_CR3_ITAMP7NOER_Pos)
|
|
#define TAMP_CR3_ITAMP7NOER TAMP_CR3_ITAMP7NOER_Msk
|
|
|
|
/*
|
|
* This function replaces calls to universal, but flash-wasting
|
|
* functions HAL_RCC_OscConfig and HAL_RCCEx_PeriphCLKConfig.
|
|
*
|
|
* This is the configuration before the optimization:
|
|
* osc_init_def.OscillatorType = RCC_OSCILLATORTYPE_LSI;
|
|
* osc_init_def.LSIState = RCC_LSI_ON;
|
|
* HAL_RCC_OscConfig(&osc_init_def);
|
|
*
|
|
* clk_init_def.PeriphClockSelection = RCC_PERIPHCLK_RTC;
|
|
* clk_init_def.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
|
|
* HAL_RCCEx_PeriphCLKConfig(&clk_init_def);
|
|
*/
|
|
HAL_StatusTypeDef lsi_init(void) {
|
|
uint32_t tickstart = 0U;
|
|
|
|
FlagStatus pwrclkchanged = RESET;
|
|
|
|
/* Update LSI configuration in Backup Domain control register */
|
|
/* Requires to enable write access to Backup Domain of necessary */
|
|
if (__HAL_RCC_PWR_IS_CLK_DISABLED()) {
|
|
__HAL_RCC_PWR_CLK_ENABLE();
|
|
pwrclkchanged = SET;
|
|
}
|
|
|
|
if (HAL_IS_BIT_CLR(PWR->DBPR, PWR_DBPR_DBP)) {
|
|
/* Enable write access to Backup domain */
|
|
SET_BIT(PWR->DBPR, PWR_DBPR_DBP);
|
|
|
|
/* Wait for Backup domain Write protection disable */
|
|
tickstart = HAL_GetTick();
|
|
|
|
while (HAL_IS_BIT_CLR(PWR->DBPR, PWR_DBPR_DBP)) {
|
|
if ((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) {
|
|
/* Restore clock configuration if changed */
|
|
if (pwrclkchanged == SET) {
|
|
__HAL_RCC_PWR_CLK_DISABLE();
|
|
}
|
|
return HAL_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t bdcr_temp = RCC->BDCR;
|
|
|
|
if (RCC_LSI_DIV1 != (bdcr_temp & RCC_BDCR_LSIPREDIV)) {
|
|
if (((bdcr_temp & RCC_BDCR_LSIRDY) == RCC_BDCR_LSIRDY) &&
|
|
((bdcr_temp & RCC_BDCR_LSION) != RCC_BDCR_LSION)) {
|
|
/* If LSIRDY is set while LSION is not enabled, LSIPREDIV can't be updated
|
|
*/
|
|
/* The LSIPREDIV cannot be changed if the LSI is used by the IWDG or by
|
|
* the RTC */
|
|
/* Restore clock configuration if changed */
|
|
if (pwrclkchanged == SET) {
|
|
__HAL_RCC_PWR_CLK_DISABLE();
|
|
}
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Turn off LSI before changing RCC_BDCR_LSIPREDIV */
|
|
if ((bdcr_temp & RCC_BDCR_LSION) == RCC_BDCR_LSION) {
|
|
__HAL_RCC_LSI_DISABLE();
|
|
|
|
tickstart = HAL_GetTick();
|
|
|
|
/* Wait till LSI is disabled */
|
|
while (READ_BIT(RCC->BDCR, RCC_BDCR_LSIRDY) != 0U)
|
|
;
|
|
}
|
|
|
|
/* Set LSI division factor */
|
|
MODIFY_REG(RCC->BDCR, RCC_BDCR_LSIPREDIV, 0);
|
|
}
|
|
|
|
/* Enable the Internal Low Speed oscillator (LSI) */
|
|
__HAL_RCC_LSI_ENABLE();
|
|
|
|
/* Wait till LSI is ready */
|
|
while (READ_BIT(RCC->BDCR, RCC_BDCR_LSIRDY) == 0U)
|
|
;
|
|
|
|
/* Check for RTC Parameters used to output RTCCLK */
|
|
assert_param(IS_RCC_RTCCLKSOURCE(pPeriphClkInit->RTCClockSelection));
|
|
/* Enable Power Clock */
|
|
if (__HAL_RCC_PWR_IS_CLK_DISABLED()) {
|
|
__HAL_RCC_PWR_CLK_ENABLE();
|
|
pwrclkchanged = SET;
|
|
}
|
|
/* Enable write access to Backup domain */
|
|
SET_BIT(PWR->DBPR, PWR_DBPR_DBP);
|
|
|
|
/* Wait for Backup domain Write protection disable */
|
|
tickstart = HAL_GetTick();
|
|
|
|
while (HAL_IS_BIT_CLR(PWR->DBPR, PWR_DBPR_DBP)) {
|
|
if ((HAL_GetTick() - tickstart) > RCC_DBP_TIMEOUT_VALUE) {
|
|
return HAL_TIMEOUT;
|
|
}
|
|
}
|
|
/* Reset the Backup domain only if the RTC Clock source selection is modified
|
|
* from default */
|
|
bdcr_temp = READ_BIT(RCC->BDCR, RCC_BDCR_RTCSEL);
|
|
|
|
if ((bdcr_temp != RCC_RTCCLKSOURCE_NO_CLK) &&
|
|
(bdcr_temp != RCC_RTCCLKSOURCE_LSI)) {
|
|
/* Store the content of BDCR register before the reset of Backup Domain */
|
|
bdcr_temp = READ_BIT(RCC->BDCR, ~(RCC_BDCR_RTCSEL));
|
|
/* RTC Clock selection can be changed only if the Backup Domain is reset */
|
|
__HAL_RCC_BACKUPRESET_FORCE();
|
|
__HAL_RCC_BACKUPRESET_RELEASE();
|
|
/* Restore the Content of BDCR register */
|
|
RCC->BDCR = bdcr_temp;
|
|
}
|
|
|
|
/* Wait for LSE reactivation if LSE was enable prior to Backup Domain reset */
|
|
if (HAL_IS_BIT_SET(bdcr_temp, RCC_BDCR_LSEON)) {
|
|
/* Get Start Tick*/
|
|
tickstart = HAL_GetTick();
|
|
|
|
/* Wait till LSE is ready */
|
|
while (READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0U) {
|
|
if ((HAL_GetTick() - tickstart) > RCC_LSE_TIMEOUT_VALUE) {
|
|
return HAL_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Apply new RTC clock source selection */
|
|
__HAL_RCC_RTC_CONFIG(RCC_PERIPHCLK_RTC);
|
|
|
|
/* Restore clock configuration if changed */
|
|
if (pwrclkchanged == SET) {
|
|
__HAL_RCC_PWR_CLK_DISABLE();
|
|
}
|
|
return HAL_OK;
|
|
}
|
|
|
|
void tamper_init(void) {
|
|
// Enable LSI clock
|
|
lsi_init();
|
|
|
|
// Enable RTC peripheral (tampers are part of it)
|
|
__HAL_RCC_RTC_ENABLE();
|
|
__HAL_RCC_RTCAPB_CLK_ENABLE();
|
|
|
|
// Clear all pending interrupts
|
|
// They may be some as RTC/TAMP peripherals resides inside the
|
|
// backup voltage domain
|
|
TAMP->SCR = TAMP_SCR_CTAMP2F | TAMP_SCR_CITAMP1F | TAMP_SCR_CITAMP2F |
|
|
TAMP_SCR_CITAMP3F | TAMP_SCR_CITAMP5F | TAMP_SCR_CITAMP6F |
|
|
TAMP_SCR_CITAMP7F | TAMP_SCR_CITAMP8F | TAMP_SCR_CITAMP9F |
|
|
TAMP_SCR_CITAMP11F | TAMP_SCR_CITAMP12F | TAMP_SCR_CITAMP13F;
|
|
|
|
NVIC_ClearPendingIRQ(TAMP_IRQn);
|
|
|
|
// Enable battery and power monitoring (!@# rework it)
|
|
RCC->AHB3ENR |= RCC_AHB3ENR_PWREN;
|
|
// HAL_PWR_EnableBkUpAccess();
|
|
PWR->BDCR1 |= PWR_BDCR1_MONEN;
|
|
// HAL_PWR_DisableBkUpAccess();
|
|
|
|
// Enable all internal tampers (4th and 10th are intentionally skipped)
|
|
// We select all of them despite some of them are never triggered
|
|
TAMP->CR1 =
|
|
TAMP_CR1_ITAMP1E | // backup domain voltage monitoring
|
|
TAMP_CR1_ITAMP2E | // temperature monitoring
|
|
TAMP_CR1_ITAMP3E | // LSE monitoring (LSECSS)
|
|
TAMP_CR1_ITAMP5E | // RTC calendar overflow
|
|
TAMP_CR1_ITAMP6E | // JTAG/SWD access when RDP > 0
|
|
TAMP_CR1_ITAMP7E | // ADC4 analog watchdog monitoring 1
|
|
TAMP_CR1_ITAMP8E | // Monotonic counter 1 overflow
|
|
TAMP_CR1_ITAMP9E | // Crypto periherals fault (SAES, AES, PKA, TRNG)
|
|
TAMP_CR1_ITAMP11E | // IWDG reset when tamper flag is set
|
|
TAMP_CR1_ITAMP12E | // ADC4 analog watchdog monitoring 2
|
|
TAMP_CR1_ITAMP13E; // ADC4 analog watchdog monitoring 3
|
|
|
|
// Switch all internal tampers to the "confirmed" mode
|
|
// => all secrets all deleted when any tamper event is triggered
|
|
TAMP->CR3 = 0;
|
|
|
|
// Setup external tampers
|
|
// TAMP_IN2 active low, "confirmed" mode
|
|
TAMP->CR2 = 0;
|
|
// TAMP_CR2_TAMP2TRG;
|
|
|
|
// Set external tamper input filter
|
|
TAMP->FLTCR =
|
|
// TAMP_FLTCR_TAMPPUDIS | // disable pre-charge of TAMP_INx pins
|
|
(3 << TAMP_FLTCR_TAMPPRCH_Pos) | // pre-charge 8 RTCCLK cycles
|
|
(2 << TAMP_FLTCR_TAMPFLT_Pos) | // activated after 4 same samples
|
|
(7 << TAMP_FLTCR_TAMPFREQ_Pos); // sampling period RTCCLK / 256 (128Hz)
|
|
|
|
// Enable all interrupts for all internal tampers
|
|
TAMP->IER = TAMP_IER_TAMP2IE | TAMP_IER_ITAMP1IE | TAMP_IER_ITAMP2IE |
|
|
TAMP_IER_ITAMP3IE | TAMP_IER_ITAMP5IE | TAMP_IER_ITAMP6IE |
|
|
TAMP_IER_ITAMP7IE | TAMP_IER_ITAMP8IE | TAMP_IER_ITAMP9IE |
|
|
TAMP_IER_ITAMP11IE | TAMP_IER_ITAMP12IE | TAMP_IER_ITAMP13IE;
|
|
|
|
// Enable TAMP interrupt at NVIC controller
|
|
NVIC_SetPriority(TAMP_IRQn, IRQ_PRI_TAMP);
|
|
NVIC_EnableIRQ(TAMP_IRQn);
|
|
}
|
|
|
|
// Interrupt handle for all tamper events
|
|
// It displays an error message
|
|
void TAMP_IRQHandler(void) {
|
|
uint32_t sr = TAMP->SR;
|
|
TAMP->SCR = sr;
|
|
|
|
#ifdef BOARDLOADER
|
|
error_shutdown("INTERNAL TAMPER", "");
|
|
#else
|
|
const char* reason = "UNKNOWN";
|
|
if (sr & TAMP_SR_TAMP1F) {
|
|
reason = "INPUT1";
|
|
} else if (sr & TAMP_SR_TAMP2F) {
|
|
reason = "INPUT2";
|
|
} else if (sr & TAMP_SR_ITAMP1F) {
|
|
reason = "VOLTAGE";
|
|
} else if (sr & TAMP_SR_ITAMP2F) {
|
|
reason = "TEMPERATURE";
|
|
} else if (sr & TAMP_SR_ITAMP3F) {
|
|
reason = "LSE CLOCK";
|
|
} else if (sr & TAMP_SR_ITAMP5F) {
|
|
reason = "RTC OVERFLOW";
|
|
} else if (sr & TAMP_SR_ITAMP6F) {
|
|
reason = "SWD ACCESS";
|
|
} else if (sr & TAMP_SR_ITAMP7F) {
|
|
reason = "ANALOG WDG1";
|
|
} else if (sr & TAMP_SR_ITAMP8F) {
|
|
reason = "MONO COUNTER";
|
|
} else if (sr & TAMP_SR_ITAMP9F) {
|
|
reason = "CRYPTO ERROR";
|
|
} else if (sr & TAMP_SR_ITAMP11F) {
|
|
reason = "IWDG";
|
|
} else if (sr & TAMP_SR_ITAMP12F) {
|
|
reason = "ANALOG WDG2";
|
|
} else if (sr & TAMP_SR_ITAMP13F) {
|
|
reason = "ANALOG WDG3";
|
|
}
|
|
error_shutdown("INTERNAL TAMPER", reason);
|
|
#endif
|
|
}
|