mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 01:24:27 +00:00

Rename driver file to BSEC2. Split header file in IP and feature parts. Add functions to access BSEC scratch register. Several corrections and improvements. Probe the driver earlier, especially to check debug features. Change-Id: I1981536398d598d67a19d2d7766dacc18de72ec1 Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com> Signed-off-by: Yann Gautier <yann.gautier@st.com>
961 lines
19 KiB
C
961 lines
19 KiB
C
/*
|
|
* Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
|
|
#include <arch_helpers.h>
|
|
#include <common/debug.h>
|
|
#include <drivers/st/bsec.h>
|
|
#include <drivers/st/bsec2_reg.h>
|
|
#include <lib/mmio.h>
|
|
#include <lib/spinlock.h>
|
|
#include <libfdt.h>
|
|
|
|
#include <platform_def.h>
|
|
|
|
#define BSEC_IP_VERSION_1_1 U(0x11)
|
|
#define BSEC_IP_VERSION_2_0 U(0x20)
|
|
#define BSEC_IP_ID_2 U(0x100032)
|
|
|
|
#define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT)
|
|
|
|
static uint32_t otp_nsec_access[OTP_ACCESS_SIZE] __unused;
|
|
|
|
static uint32_t bsec_power_safmem(bool power);
|
|
|
|
/* BSEC access protection */
|
|
static spinlock_t bsec_spinlock;
|
|
static uintptr_t bsec_base;
|
|
|
|
static void bsec_lock(void)
|
|
{
|
|
if (stm32mp_lock_available()) {
|
|
spin_lock(&bsec_spinlock);
|
|
}
|
|
}
|
|
|
|
static void bsec_unlock(void)
|
|
{
|
|
if (stm32mp_lock_available()) {
|
|
spin_unlock(&bsec_spinlock);
|
|
}
|
|
}
|
|
|
|
static bool is_otp_invalid_mode(void)
|
|
{
|
|
bool ret = ((bsec_get_status() & BSEC_MODE_INVALID) == BSEC_MODE_INVALID);
|
|
|
|
if (ret) {
|
|
ERROR("OTP mode is OTP-INVALID\n");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(IMAGE_BL32)
|
|
static int bsec_get_dt_node(struct dt_node_info *info)
|
|
{
|
|
int node;
|
|
|
|
node = dt_get_node(info, -1, DT_BSEC_COMPAT);
|
|
if (node < 0) {
|
|
return -FDT_ERR_NOTFOUND;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static void enable_non_secure_access(uint32_t otp)
|
|
{
|
|
otp_nsec_access[otp / __WORD_BIT] |= BIT(otp % __WORD_BIT);
|
|
|
|
if (bsec_shadow_register(otp) != BSEC_OK) {
|
|
panic();
|
|
}
|
|
}
|
|
|
|
static bool non_secure_can_access(uint32_t otp)
|
|
{
|
|
return (otp_nsec_access[otp / __WORD_BIT] &
|
|
BIT(otp % __WORD_BIT)) != 0U;
|
|
}
|
|
|
|
static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
|
|
{
|
|
int bsec_subnode;
|
|
|
|
fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
|
|
const fdt32_t *cuint;
|
|
uint32_t otp;
|
|
uint32_t i;
|
|
uint32_t size;
|
|
uint32_t offset;
|
|
uint32_t length;
|
|
|
|
cuint = fdt_getprop(fdt, bsec_subnode, "reg", NULL);
|
|
if (cuint == NULL) {
|
|
panic();
|
|
}
|
|
|
|
offset = fdt32_to_cpu(*cuint);
|
|
cuint++;
|
|
length = fdt32_to_cpu(*cuint);
|
|
|
|
otp = offset / sizeof(uint32_t);
|
|
|
|
if (otp < STM32MP1_UPPER_OTP_START) {
|
|
unsigned int otp_end = round_up(offset + length,
|
|
sizeof(uint32_t)) /
|
|
sizeof(uint32_t);
|
|
|
|
if (otp_end > STM32MP1_UPPER_OTP_START) {
|
|
/*
|
|
* OTP crosses Lower/Upper boundary, consider
|
|
* only the upper part.
|
|
*/
|
|
otp = STM32MP1_UPPER_OTP_START;
|
|
length -= (STM32MP1_UPPER_OTP_START *
|
|
sizeof(uint32_t)) - offset;
|
|
offset = STM32MP1_UPPER_OTP_START *
|
|
sizeof(uint32_t);
|
|
|
|
WARN("OTP crosses Lower/Upper boundary\n");
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((fdt_getprop(fdt, bsec_subnode,
|
|
"st,non-secure-otp", NULL)) == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (((offset % sizeof(uint32_t)) != 0U) ||
|
|
((length % sizeof(uint32_t)) != 0U)) {
|
|
ERROR("Unaligned non-secure OTP\n");
|
|
panic();
|
|
}
|
|
|
|
size = length / sizeof(uint32_t);
|
|
|
|
for (i = otp; i < (otp + size); i++) {
|
|
enable_non_secure_access(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bsec_late_init(void)
|
|
{
|
|
void *fdt;
|
|
int node;
|
|
struct dt_node_info bsec_info;
|
|
|
|
if (fdt_get_address(&fdt) == 0) {
|
|
panic();
|
|
}
|
|
|
|
node = bsec_get_dt_node(&bsec_info);
|
|
if (node < 0) {
|
|
panic();
|
|
}
|
|
|
|
assert(bsec_base == bsec_info.base);
|
|
|
|
bsec_dt_otp_nsec_access(fdt, node);
|
|
}
|
|
#endif
|
|
|
|
static uint32_t otp_bank_offset(uint32_t otp)
|
|
{
|
|
assert(otp <= STM32MP1_OTP_MAX_ID);
|
|
|
|
return ((otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
|
|
sizeof(uint32_t);
|
|
}
|
|
|
|
/*
|
|
* bsec_check_error: check BSEC error status.
|
|
* otp: OTP number.
|
|
* check_disturbed: check only error (false),
|
|
* or error and disturbed status (true).
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
static uint32_t bsec_check_error(uint32_t otp, bool check_disturbed)
|
|
{
|
|
uint32_t bit = BIT(otp & BSEC_OTP_MASK);
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
|
|
if ((mmio_read_32(bsec_base + BSEC_ERROR_OFF + bank) & bit) != 0U) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (!check_disturbed) {
|
|
return BSEC_OK;
|
|
}
|
|
|
|
if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) {
|
|
return BSEC_DISTURBED;
|
|
}
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_probe: initialize BSEC driver.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_probe(void)
|
|
{
|
|
bsec_base = BSEC_BASE;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if ((((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_1_1) &&
|
|
((bsec_get_version() & BSEC_IPVR_MSK) != BSEC_IP_VERSION_2_0)) ||
|
|
(bsec_get_id() != BSEC_IP_ID_2)) {
|
|
panic();
|
|
}
|
|
|
|
#if defined(IMAGE_BL32)
|
|
bsec_late_init();
|
|
#endif
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_get_base: return BSEC base address.
|
|
*/
|
|
uint32_t bsec_get_base(void)
|
|
{
|
|
return bsec_base;
|
|
}
|
|
|
|
/*
|
|
* bsec_set_config: enable and configure BSEC.
|
|
* cfg: pointer to param structure used to set register.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_set_config(struct bsec_config *cfg)
|
|
{
|
|
uint32_t value;
|
|
uint32_t result;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
value = ((((uint32_t)cfg->freq << BSEC_CONF_FRQ_SHIFT) &
|
|
BSEC_CONF_FRQ_MASK) |
|
|
(((uint32_t)cfg->pulse_width << BSEC_CONF_PRG_WIDTH_SHIFT) &
|
|
BSEC_CONF_PRG_WIDTH_MASK) |
|
|
(((uint32_t)cfg->tread << BSEC_CONF_TREAD_SHIFT) &
|
|
BSEC_CONF_TREAD_MASK));
|
|
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, value);
|
|
|
|
bsec_unlock();
|
|
|
|
result = bsec_power_safmem((bool)cfg->power &
|
|
BSEC_CONF_POWER_UP_MASK);
|
|
if (result != BSEC_OK) {
|
|
return result;
|
|
}
|
|
|
|
value = ((((uint32_t)cfg->upper_otp_lock << UPPER_OTP_LOCK_SHIFT) &
|
|
UPPER_OTP_LOCK_MASK) |
|
|
(((uint32_t)cfg->den_lock << DENREG_LOCK_SHIFT) &
|
|
DENREG_LOCK_MASK) |
|
|
(((uint32_t)cfg->prog_lock << GPLOCK_LOCK_SHIFT) &
|
|
GPLOCK_LOCK_MASK));
|
|
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_LOCK_OFF, value);
|
|
|
|
bsec_unlock();
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_get_config: return config parameters set in BSEC registers.
|
|
* cfg: config param return.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_get_config(struct bsec_config *cfg)
|
|
{
|
|
uint32_t value;
|
|
|
|
if (cfg == NULL) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
value = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF);
|
|
cfg->power = (uint8_t)((value & BSEC_CONF_POWER_UP_MASK) >>
|
|
BSEC_CONF_POWER_UP_SHIFT);
|
|
cfg->freq = (uint8_t)((value & BSEC_CONF_FRQ_MASK) >>
|
|
BSEC_CONF_FRQ_SHIFT);
|
|
cfg->pulse_width = (uint8_t)((value & BSEC_CONF_PRG_WIDTH_MASK) >>
|
|
BSEC_CONF_PRG_WIDTH_SHIFT);
|
|
cfg->tread = (uint8_t)((value & BSEC_CONF_TREAD_MASK) >>
|
|
BSEC_CONF_TREAD_SHIFT);
|
|
|
|
value = mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF);
|
|
cfg->upper_otp_lock = (uint8_t)((value & UPPER_OTP_LOCK_MASK) >>
|
|
UPPER_OTP_LOCK_SHIFT);
|
|
cfg->den_lock = (uint8_t)((value & DENREG_LOCK_MASK) >>
|
|
DENREG_LOCK_SHIFT);
|
|
cfg->prog_lock = (uint8_t)((value & GPLOCK_LOCK_MASK) >>
|
|
GPLOCK_LOCK_SHIFT);
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_shadow_register: copy SAFMEM OTP to BSEC data.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_shadow_register(uint32_t otp)
|
|
{
|
|
uint32_t result;
|
|
bool value;
|
|
bool power_up = false;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
result = bsec_read_sr_lock(otp, &value);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u Sticky-read bit read Error %u\n", otp, result);
|
|
return result;
|
|
}
|
|
|
|
if (value) {
|
|
VERBOSE("BSEC: OTP %u is locked and will not be refreshed\n",
|
|
otp);
|
|
}
|
|
|
|
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
|
result = bsec_power_safmem(true);
|
|
|
|
if (result != BSEC_OK) {
|
|
return result;
|
|
}
|
|
|
|
power_up = true;
|
|
}
|
|
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_READ);
|
|
|
|
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
;
|
|
}
|
|
|
|
result = bsec_check_error(otp, true);
|
|
|
|
bsec_unlock();
|
|
|
|
if (power_up) {
|
|
if (bsec_power_safmem(false) != BSEC_OK) {
|
|
panic();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* bsec_read_otp: read an OTP data value.
|
|
* val: read value.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_read_otp(uint32_t *val, uint32_t otp)
|
|
{
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
*val = mmio_read_32(bsec_base + BSEC_OTP_DATA_OFF +
|
|
(otp * sizeof(uint32_t)));
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_write_otp: write value in BSEC data register.
|
|
* val: value to write.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_write_otp(uint32_t val, uint32_t otp)
|
|
{
|
|
uint32_t result;
|
|
bool value;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
result = bsec_read_sw_lock(otp, &value);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u Sticky-write bit read Error %u\n", otp, result);
|
|
return result;
|
|
}
|
|
|
|
if (value) {
|
|
VERBOSE("BSEC: OTP %u is locked and write will be ignored\n",
|
|
otp);
|
|
}
|
|
|
|
/* Ensure integrity of each register access sequence */
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_DATA_OFF +
|
|
(otp * sizeof(uint32_t)), val);
|
|
|
|
bsec_unlock();
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* bsec_program_otp: program a bit in SAFMEM after the prog.
|
|
* The OTP data is not refreshed.
|
|
* val: value to program.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_program_otp(uint32_t val, uint32_t otp)
|
|
{
|
|
uint32_t result;
|
|
bool power_up = false;
|
|
bool sp_lock;
|
|
bool perm_lock;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
result = bsec_read_sp_lock(otp, &sp_lock);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u Sticky-prog bit read Error %u\n", otp, result);
|
|
return result;
|
|
}
|
|
|
|
result = bsec_read_permanent_lock(otp, &perm_lock);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u permanent bit read Error %u\n", otp, result);
|
|
return result;
|
|
}
|
|
|
|
if (sp_lock || perm_lock) {
|
|
WARN("BSEC: OTP locked, prog will be ignored\n");
|
|
return BSEC_PROG_FAIL;
|
|
}
|
|
|
|
if ((mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF) &
|
|
BIT(BSEC_LOCK_PROGRAM)) != 0U) {
|
|
WARN("BSEC: GPLOCK activated, prog will be ignored\n");
|
|
}
|
|
|
|
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
|
result = bsec_power_safmem(true);
|
|
|
|
if (result != BSEC_OK) {
|
|
return result;
|
|
}
|
|
|
|
power_up = true;
|
|
}
|
|
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, val);
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE);
|
|
|
|
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
;
|
|
}
|
|
|
|
if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
|
result = BSEC_PROG_FAIL;
|
|
} else {
|
|
result = bsec_check_error(otp, true);
|
|
}
|
|
|
|
bsec_unlock();
|
|
|
|
if (power_up) {
|
|
if (bsec_power_safmem(false) != BSEC_OK) {
|
|
panic();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* bsec_permanent_lock_otp: permanent lock of OTP in SAFMEM.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_permanent_lock_otp(uint32_t otp)
|
|
{
|
|
uint32_t result;
|
|
bool power_up = false;
|
|
uint32_t data;
|
|
uint32_t addr;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
|
result = bsec_power_safmem(true);
|
|
|
|
if (result != BSEC_OK) {
|
|
return result;
|
|
}
|
|
|
|
power_up = true;
|
|
}
|
|
|
|
if (otp < STM32MP1_UPPER_OTP_START) {
|
|
addr = otp >> ADDR_LOWER_OTP_PERLOCK_SHIFT;
|
|
data = DATA_LOWER_OTP_PERLOCK_BIT <<
|
|
((otp & DATA_LOWER_OTP_PERLOCK_MASK) << 1U);
|
|
} else {
|
|
addr = (otp >> ADDR_UPPER_OTP_PERLOCK_SHIFT) + 2U;
|
|
data = DATA_UPPER_OTP_PERLOCK_BIT <<
|
|
(otp & DATA_UPPER_OTP_PERLOCK_MASK);
|
|
}
|
|
|
|
bsec_lock();
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, data);
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF,
|
|
addr | BSEC_WRITE | BSEC_LOCK);
|
|
|
|
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
|
;
|
|
}
|
|
|
|
if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
|
result = BSEC_PROG_FAIL;
|
|
} else {
|
|
result = bsec_check_error(otp, false);
|
|
}
|
|
|
|
bsec_unlock();
|
|
|
|
if (power_up) {
|
|
if (bsec_power_safmem(false) != BSEC_OK) {
|
|
panic();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* bsec_write_debug_conf: write value in debug feature.
|
|
* to enable/disable debug service.
|
|
* val: value to write.
|
|
* return value: none.
|
|
*/
|
|
void bsec_write_debug_conf(uint32_t val)
|
|
{
|
|
if (is_otp_invalid_mode()) {
|
|
return;
|
|
}
|
|
|
|
bsec_lock();
|
|
mmio_write_32(bsec_base + BSEC_DEN_OFF, val & BSEC_DEN_ALL_MSK);
|
|
bsec_unlock();
|
|
}
|
|
|
|
/*
|
|
* bsec_read_debug_conf: return debug configuration register value.
|
|
*/
|
|
uint32_t bsec_read_debug_conf(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_DEN_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_write_scratch: write value in scratch register.
|
|
* val: value to write.
|
|
* return value: none.
|
|
*/
|
|
void bsec_write_scratch(uint32_t val)
|
|
{
|
|
#if defined(IMAGE_BL32)
|
|
if (is_otp_invalid_mode()) {
|
|
return;
|
|
}
|
|
|
|
bsec_lock();
|
|
mmio_write_32(bsec_base + BSEC_SCRATCH_OFF, val);
|
|
bsec_unlock();
|
|
#else
|
|
mmio_write_32(BSEC_BASE + BSEC_SCRATCH_OFF, val);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* bsec_read_scratch: return scratch register value.
|
|
*/
|
|
uint32_t bsec_read_scratch(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_SCRATCH_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_get_status: return status register value.
|
|
*/
|
|
uint32_t bsec_get_status(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_OTP_STATUS_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_get_hw_conf: return hardware configuration register value.
|
|
*/
|
|
uint32_t bsec_get_hw_conf(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_IPHW_CFG_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_get_version: return BSEC version register value.
|
|
*/
|
|
uint32_t bsec_get_version(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_IPVR_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_get_id: return BSEC ID register value.
|
|
*/
|
|
uint32_t bsec_get_id(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_IP_ID_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_get_magic_id: return BSEC magic number register value.
|
|
*/
|
|
uint32_t bsec_get_magic_id(void)
|
|
{
|
|
return mmio_read_32(bsec_base + BSEC_IP_MAGIC_ID_OFF);
|
|
}
|
|
|
|
/*
|
|
* bsec_set_sr_lock: set shadow-read lock.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_set_sr_lock(uint32_t otp)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bsec_lock();
|
|
mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, otp_mask);
|
|
bsec_unlock();
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_read_sr_lock: read shadow-read lock.
|
|
* otp: OTP number.
|
|
* value: read value (true or false).
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_read_sr_lock(uint32_t otp, bool *value)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
uint32_t bank_value;
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank);
|
|
|
|
*value = ((bank_value & otp_mask) != 0U);
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_set_sw_lock: set shadow-write lock.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_set_sw_lock(uint32_t otp)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bsec_lock();
|
|
mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, otp_mask);
|
|
bsec_unlock();
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_read_sw_lock: read shadow-write lock.
|
|
* otp: OTP number.
|
|
* value: read value (true or false).
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_read_sw_lock(uint32_t otp, bool *value)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
uint32_t bank_value;
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank);
|
|
|
|
*value = ((bank_value & otp_mask) != 0U);
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_set_sp_lock: set shadow-program lock.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_set_sp_lock(uint32_t otp)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bsec_lock();
|
|
mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, otp_mask);
|
|
bsec_unlock();
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_read_sp_lock: read shadow-program lock.
|
|
* otp: OTP number.
|
|
* value: read value (true or false).
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_read_sp_lock(uint32_t otp, bool *value)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
uint32_t bank_value;
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank);
|
|
|
|
*value = ((bank_value & otp_mask) != 0U);
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_read_permanent_lock: Read permanent lock status.
|
|
* otp: OTP number.
|
|
* value: read value (true or false).
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_read_permanent_lock(uint32_t otp, bool *value)
|
|
{
|
|
uint32_t bank = otp_bank_offset(otp);
|
|
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
|
uint32_t bank_value;
|
|
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
bank_value = mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank);
|
|
|
|
*value = ((bank_value & otp_mask) != 0U);
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_otp_lock: Lock Upper OTP or Global Programming or Debug Enable.
|
|
* service: Service to lock, see header file.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_otp_lock(uint32_t service)
|
|
{
|
|
uintptr_t reg = bsec_base + BSEC_OTP_LOCK_OFF;
|
|
|
|
if (is_otp_invalid_mode()) {
|
|
return BSEC_ERROR;
|
|
}
|
|
|
|
switch (service) {
|
|
case BSEC_LOCK_UPPER_OTP:
|
|
mmio_write_32(reg, BIT(BSEC_LOCK_UPPER_OTP));
|
|
break;
|
|
case BSEC_LOCK_DEBUG:
|
|
mmio_write_32(reg, BIT(BSEC_LOCK_DEBUG));
|
|
break;
|
|
case BSEC_LOCK_PROGRAM:
|
|
mmio_write_32(reg, BIT(BSEC_LOCK_PROGRAM));
|
|
break;
|
|
default:
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_power_safmem: Activate or deactivate SAFMEM power.
|
|
* power: true to power up, false to power down.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
static uint32_t bsec_power_safmem(bool power)
|
|
{
|
|
uint32_t register_val;
|
|
uint32_t timeout = BSEC_TIMEOUT_VALUE;
|
|
|
|
bsec_lock();
|
|
|
|
register_val = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF);
|
|
|
|
if (power) {
|
|
register_val |= BSEC_CONF_POWER_UP_MASK;
|
|
} else {
|
|
register_val &= ~BSEC_CONF_POWER_UP_MASK;
|
|
}
|
|
|
|
mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, register_val);
|
|
|
|
if (power) {
|
|
while (((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) &&
|
|
(timeout != 0U)) {
|
|
timeout--;
|
|
}
|
|
} else {
|
|
while (((bsec_get_status() & BSEC_MODE_PWR_MASK) != 0U) &&
|
|
(timeout != 0U)) {
|
|
timeout--;
|
|
}
|
|
}
|
|
|
|
bsec_unlock();
|
|
|
|
if (timeout == 0U) {
|
|
return BSEC_TIMEOUT;
|
|
}
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|
|
/*
|
|
* bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value.
|
|
* otp_value: read value.
|
|
* word: OTP number.
|
|
* return value: BSEC_OK if no error.
|
|
*/
|
|
uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word)
|
|
{
|
|
uint32_t result;
|
|
|
|
result = bsec_shadow_register(word);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u Shadowing Error %u\n", word, result);
|
|
return result;
|
|
}
|
|
|
|
result = bsec_read_otp(otp_value, word);
|
|
if (result != BSEC_OK) {
|
|
ERROR("BSEC: %u Read Error %u\n", word, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* bsec_check_nsec_access_rights: check non-secure access rights to target OTP.
|
|
* otp: OTP number.
|
|
* return value: BSEC_OK if authorized access.
|
|
*/
|
|
uint32_t bsec_check_nsec_access_rights(uint32_t otp)
|
|
{
|
|
#if defined(IMAGE_BL32)
|
|
if (otp > STM32MP1_OTP_MAX_ID) {
|
|
return BSEC_INVALID_PARAM;
|
|
}
|
|
|
|
if (otp >= STM32MP1_UPPER_OTP_START) {
|
|
if (!non_secure_can_access(otp)) {
|
|
return BSEC_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return BSEC_OK;
|
|
}
|
|
|