/* * Copyright (c) 2024, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #define BSEC_IP_VERSION_1_0 U(0x10) #define BSEC_IP_ID_3 U(0x100033) #define MAX_NB_TRIES U(3) /* * IP configuration */ #define BSEC_OTP_MASK GENMASK_32(4, 0) #define BSEC_OTP_BANK_SHIFT U(5) #define BSEC_TIMEOUT_VALUE U(0x800000) /* ~7sec @1.2GHz */ /* Magic use to indicated valid SHADOW = 'B' 'S' 'E' 'C' */ #define BSEC_MAGIC U(0x42534543) #define OTP_MAX_SIZE (STM32MP2_OTP_MAX_ID + U(1)) struct bsec_shadow { uint32_t magic; uint32_t state; uint32_t value[OTP_MAX_SIZE]; uint32_t status[OTP_MAX_SIZE]; }; static uint32_t otp_bank(uint32_t otp) { if (otp > STM32MP2_OTP_MAX_ID) { panic(); } return (otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT; } static uint32_t otp_bit_mask(uint32_t otp) { return BIT(otp & BSEC_OTP_MASK); } /* * bsec_get_status: return status register value. */ static uint32_t bsec_get_status(void) { return mmio_read_32(BSEC_BASE + BSEC_OTPSR); } /* * bsec_get_version: return BSEC version. */ static uint32_t bsec_get_version(void) { return mmio_read_32(BSEC_BASE + BSEC_VERR) & BSEC_VERR_MASK; } /* * bsec_get_id: return BSEC ID. */ static uint32_t bsec_get_id(void) { return mmio_read_32(BSEC_BASE + BSEC_IPIDR); } static bool is_fuse_shadowed(uint32_t otp) { uint32_t bank = otp_bank(otp); uint32_t otp_mask = otp_bit_mask(otp); uint32_t bank_value; bank_value = mmio_read_32(BSEC_BASE + BSEC_SFSR(bank)); if ((bank_value & otp_mask) != 0U) { return true; } return false; } static void poll_otp_status_busy(void) { uint32_t timeout = BSEC_TIMEOUT_VALUE; while (((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) && (timeout != 0U)) { timeout--; } if ((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) { ERROR("BSEC timeout\n"); panic(); } } static uint32_t check_read_error(uint32_t otp) { uint32_t status = bsec_get_status(); if ((status & BSEC_OTPSR_SECF) != 0U) { VERBOSE("BSEC read %u single error correction detected\n", otp); } if ((status & BSEC_OTPSR_PPLF) != 0U) { VERBOSE("BSEC read %u permanent programming lock detected.\n", otp); } if ((status & BSEC_OTPSR_PPLMF) != 0U) { ERROR("BSEC read %u error 0x%x\n", otp, status); return BSEC_ERROR; } if ((status & (BSEC_OTPSR_DISTURBF | BSEC_OTPSR_DEDF | BSEC_OTPSR_AMEF)) != 0U) { ERROR("BSEC read %u error 0x%x with invalid FVR\n", otp, status); return BSEC_RETRY; } return BSEC_OK; } static uint32_t check_program_error(uint32_t otp) { uint32_t status = bsec_get_status(); if ((status & BSEC_OTPSR_PROGFAIL) != 0U) { ERROR("BSEC program %u error 0x%x\n", otp, status); return BSEC_RETRY; } return BSEC_OK; } static void check_reset_error(void) { uint32_t status = bsec_get_status(); /* check initial status reporting */ if ((status & BSEC_OTPSR_BUSY) != 0U) { VERBOSE("BSEC reset and busy when OTPSR read\n"); } if ((status & BSEC_OTPSR_HIDEUP) != 0U) { VERBOSE("BSEC upper fuse are not accessible (HIDEUP)\n"); } if ((status & BSEC_OTPSR_OTPSEC) != 0U) { VERBOSE("BSEC reset single error correction detected\n"); } if ((status & BSEC_OTPSR_OTPNVIR) == 0U) { VERBOSE("BSEC reset first fuse word 0 is detected zero\n"); } if ((status & BSEC_OTPSR_OTPERR) != 0U) { ERROR("BSEC reset critical error 0x%x\n", status); panic(); } if ((status & BSEC_OTPSR_FUSEOK) != BSEC_OTPSR_FUSEOK) { ERROR("BSEC reset critical error 0x%x\n", status); panic(); } } static bool is_bsec_write_locked(void) { return (mmio_read_32(BSEC_BASE + BSEC_LOCKR) & BSEC_LOCKR_GWLOCK_MASK) != 0U; } /* * bsec_probe: initialize BSEC driver. * return value: BSEC_OK if no error. */ uint32_t bsec_probe(void) { uint32_t version = bsec_get_version(); uint32_t id = bsec_get_id(); if ((version != BSEC_IP_VERSION_1_0) || (id != BSEC_IP_ID_3)) { EARLY_ERROR("%s: version = 0x%x, id = 0x%x\n", __func__, version, id); panic(); } check_reset_error(); return BSEC_OK; } /* * bsec_shadow_register: copy SAFMEM OTP to BSEC data. * otp: OTP number. * return value: BSEC_OK if no error. */ static uint32_t bsec_shadow_register(uint32_t otp) { uint32_t result; uint32_t i; bool value; result = bsec_read_sr_lock(otp, &value); if (result != BSEC_OK) { WARN("BSEC: %u Sticky-read bit read Error %u\n", otp, result); } else if (value) { VERBOSE("BSEC: OTP %u is locked and will not be refreshed\n", otp); } for (i = 0U; i < MAX_NB_TRIES; i++) { mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp); poll_otp_status_busy(); result = check_read_error(otp); if (result != BSEC_RETRY) { break; } } return result; } /* * bsec_write_otp: write a value in shadow OTP. * val: value to program. * otp: OTP number. * return value: BSEC_OK if no error. */ uint32_t bsec_write_otp(uint32_t val, uint32_t otp) { bool state; uint32_t result; if (otp > STM32MP2_OTP_MAX_ID) { panic(); } if (!is_fuse_shadowed(otp)) { return BSEC_ERROR; } if (is_bsec_write_locked()) { return BSEC_WRITE_LOCKED; } result = bsec_read_sw_lock(otp, &state); if (result != BSEC_OK) { WARN("Shadow register is SW locked\n"); return result; } mmio_write_32(BSEC_BASE + BSEC_FVR(otp), val); return BSEC_OK; } /* * 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; uint32_t i; bool value; if (otp > STM32MP2_OTP_MAX_ID) { panic(); } if (is_bsec_write_locked() == true) { return BSEC_WRITE_LOCKED; } result = bsec_read_sp_lock(otp, &value); if (result != BSEC_OK) { WARN("BSEC: %u Sticky-prog bit read Error %u\n", otp, result); } else if (value) { WARN("BSEC: OTP locked, prog will be ignored\n"); return BSEC_WRITE_LOCKED; } mmio_write_32(BSEC_BASE + BSEC_WDR, val); for (i = 0U; i < MAX_NB_TRIES; i++) { mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp | BSEC_OTPCR_PROG); poll_otp_status_busy(); result = check_program_error(otp); if (result != BSEC_RETRY) { break; } } return result; } /* * bsec_read_debug_conf: read debug configuration. */ uint32_t bsec_read_debug_conf(void) { return mmio_read_32(BSEC_BASE + BSEC_DENR); } static uint32_t bsec_lock_register_set(uint32_t offset, uint32_t mask) { uint32_t value = mmio_read_32(BSEC_BASE + offset); /* The lock is already set */ if ((value & mask) != 0U) { return BSEC_OK; } if (is_bsec_write_locked()) { return BSEC_WRITE_LOCKED; } value |= mask; mmio_write_32(BSEC_BASE + offset, value); return BSEC_OK; } static bool bsec_lock_register_get(uint32_t offset, uint32_t mask) { uint32_t value = mmio_read_32(BSEC_BASE + offset); return (value & mask) != 0U; } /* * 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(otp); uint32_t otp_mask = otp_bit_mask(otp); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } return bsec_lock_register_set(BSEC_SRLOCK(bank), otp_mask); } /* * 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(otp); uint32_t otp_mask = otp_bit_mask(otp); assert(value != NULL); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } *value = bsec_lock_register_get(BSEC_SRLOCK(bank), otp_mask); 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(otp); uint32_t otp_mask = otp_bit_mask(otp); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } return bsec_lock_register_set(BSEC_SWLOCK(bank), otp_mask); } /* * 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(otp); uint32_t otp_mask = otp_bit_mask(otp); assert(value != NULL); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } *value = bsec_lock_register_get(BSEC_SWLOCK(bank), otp_mask); 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(otp); uint32_t otp_mask = otp_bit_mask(otp); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } return bsec_lock_register_set(BSEC_SPLOCK(bank), otp_mask); } /* * 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(otp); uint32_t otp_mask = otp_bit_mask(otp); assert(value != NULL); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } *value = bsec_lock_register_get(BSEC_SPLOCK(bank), otp_mask); return BSEC_OK; } /* * bsec_get_secure_state: read state in BSEC status register. * return: secure state */ uint32_t bsec_get_secure_state(void) { uint32_t state = BSEC_STATE_INVALID; uint32_t status = bsec_get_status(); uint32_t bsec_sr = mmio_read_32(BSEC_BASE + BSEC_SR); if ((status & BSEC_OTPSR_FUSEOK) == BSEC_OTPSR_FUSEOK) { /* NVSTATE is only valid if FUSEOK */ uint32_t nvstates = (bsec_sr & BSEC_SR_NVSTATE_MASK) >> BSEC_SR_NVSTATE_SHIFT; if (nvstates == BSEC_SR_NVSTATE_OPEN) { state = BSEC_STATE_SEC_OPEN; } else if (nvstates == BSEC_SR_NVSTATE_CLOSED) { state = BSEC_STATE_SEC_CLOSED; } else { VERBOSE("%s nvstates = %u\n", __func__, nvstates); } } return state; } /* * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value * val: read value. * otp: OTP number. * return value: BSEC_OK if no error. */ uint32_t bsec_shadow_read_otp(uint32_t *val, uint32_t otp) { assert(val != NULL); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } *val = 0U; if (is_bsec_write_locked()) { return BSEC_WRITE_LOCKED; } if (!is_fuse_shadowed(otp)) { uint32_t result = bsec_shadow_register(otp); if (result != BSEC_OK) { ERROR("BSEC: %u Shadowing Error %u\n", otp, result); return result; } } *val = mmio_read_32(BSEC_BASE + BSEC_FVR(otp)); return BSEC_OK; } /* * 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) { assert(val != NULL); if (otp > STM32MP2_OTP_MAX_ID) { panic(); } return bsec_shadow_read_otp(val, otp); }