| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sw/device/silicon_creator/lib/drivers/ast.h" |
| |
| #include "sw/device/lib/arch/device.h" |
| #include "sw/device/lib/base/abs_mmio.h" |
| #include "sw/device/lib/base/csr.h" |
| #include "sw/device/lib/base/multibits.h" |
| #include "sw/device/silicon_creator/lib/drivers/otp.h" |
| |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| #include "otp_ctrl_regs.h" |
| #include "sensor_ctrl_regs.h" |
| |
| #ifndef OT_PLATFORM_RV32 |
| // Provide a definition for off-target unit tests. |
| const uint32_t kAstCheckPollCpuCycles = 10000; |
| #endif |
| |
| rom_error_t ast_check(uintptr_t base_addr, uintptr_t otp_addr, lifecycle_state_t lc_state) { |
| // In some lifecycle states we want to continue the boot process even if the |
| // AST is not initialized. Note that in these states OTP may not have been |
| // configured. |
| switch (launder32(lc_state)) { |
| case kLcStateTest: |
| HARDENED_CHECK_EQ(lc_state, kLcStateTest); |
| return kErrorOk; |
| case kLcStateRma: |
| HARDENED_CHECK_EQ(lc_state, kLcStateRma); |
| return kErrorOk; |
| case kLcStateDev: |
| HARDENED_CHECK_EQ(lc_state, kLcStateDev); |
| break; |
| case kLcStateProd: |
| HARDENED_CHECK_EQ(lc_state, kLcStateProd); |
| break; |
| case kLcStateProdEnd: |
| HARDENED_CHECK_EQ(lc_state, kLcStateProdEnd); |
| break; |
| default: |
| HARDENED_UNREACHABLE(); |
| } |
| |
| // OTP can be configured to skip AST initialization. In this situation we do |
| // not check that AST_INIT_DONE is set. |
| uint32_t en = otp_read32(otp_addr, OTP_CTRL_PARAM_CREATOR_SW_CFG_AST_INIT_EN_OFFSET); |
| if (launder32(en) == kMultiBitBool4False) { |
| HARDENED_CHECK_EQ(en, kMultiBitBool4False); |
| return kErrorOk; |
| } |
| |
| // AST initialization may take up to 100us. It is most likely already complete |
| // at this point but for resilience poll for up to 100us. |
| uint32_t mcycle; |
| rom_error_t res = kErrorAstInitNotDone; |
| CSR_WRITE(CSR_REG_MCYCLE, 0); |
| do { |
| CSR_READ(CSR_REG_MCYCLE, &mcycle); |
| hardened_bool_t init_done = ast_init_done(base_addr); |
| if (init_done != kHardenedBoolFalse) { |
| static_assert(kErrorOk == (rom_error_t)kHardenedBoolTrue, |
| "kErrorOk must be equal to kHardenedBoolTrue"); |
| res = (rom_error_t)init_done; |
| break; |
| } |
| } while (mcycle < kAstCheckPollCpuCycles); |
| |
| return res; |
| } |
| |
| static bool done_bit_get(uintptr_t base_addr) { |
| uint32_t reg = abs_mmio_read32(base_addr + SENSOR_CTRL_STATUS_REG_OFFSET); |
| return bitfield_bit32_read(reg, SENSOR_CTRL_STATUS_AST_INIT_DONE_BIT); |
| } |
| |
| hardened_bool_t ast_init_done(uintptr_t base_addr) { |
| static_assert(kHardenedBoolTrue == 0x739, |
| "This function expects kHardenedBoolTrue to be 0x739"); |
| |
| // The code below reads the AST_INIT_DONE bit twice and modifies `res` with |
| // the result of each attempt. `res` should be `kHardenedBoolTrue` if all |
| // attempts return true. |
| hardened_bool_t res = 0x631; |
| res |= done_bit_get(base_addr) << 3; |
| res |= done_bit_get(base_addr) << 8; |
| |
| if (res != kHardenedBoolTrue) { |
| return kHardenedBoolFalse; |
| } |
| return res; |
| } |