| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/base/memory.h" | |
| #include "sw/device/lib/base/mmio.h" | |
| #include "sw/device/lib/dif/dif_aes.h" | |
| #include "sw/device/lib/dif/dif_alert_handler.h" | |
| #include "sw/device/lib/dif/dif_entropy_src.h" | |
| #include "sw/device/lib/dif/dif_keymgr.h" | |
| #include "sw/device/lib/dif/dif_kmac.h" | |
| #include "sw/device/lib/dif/dif_otbn.h" | |
| #include "sw/device/lib/dif/dif_otp_ctrl.h" | |
| #include "sw/device/lib/dif/dif_pwrmgr.h" | |
| #include "sw/device/lib/dif/dif_rv_core_ibex.h" | |
| #include "sw/device/lib/runtime/log.h" | |
| #include "sw/device/lib/testing/aes_testutils.h" | |
| #include "sw/device/lib/testing/alert_handler_testutils.h" | |
| #include "sw/device/lib/testing/entropy_testutils.h" | |
| #include "sw/device/lib/testing/keymgr_testutils.h" | |
| #include "sw/device/lib/testing/otp_ctrl_testutils.h" | |
| #include "sw/device/lib/testing/pwrmgr_testutils.h" | |
| #include "sw/device/lib/testing/test_framework/check.h" | |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" | |
| #include "sw/device/tests/otbn_randomness_impl.h" | |
| #include "alert_handler_regs.h" // Generated. | |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | |
| static dif_aes_t aes; | |
| static dif_csrng_t csrng; | |
| static dif_edn_t edn0; | |
| static dif_edn_t edn1; | |
| static dif_entropy_src_t entropy_src; | |
| static dif_kmac_t kmac; | |
| static dif_keymgr_t kmgr; | |
| static dif_otbn_t otbn; | |
| static dif_otp_ctrl_t otp; | |
| static dif_pwrmgr_t pwrmgr; | |
| static dif_rv_core_ibex_t ibex; | |
| static dif_alert_handler_t alert_handler; | |
| static dif_rv_core_ibex_fpga_info_t fpga_info; | |
| enum { | |
| kFpgaLoop = 5, | |
| }; | |
| OTTF_DEFINE_TEST_CONFIG(); | |
| /** | |
| * Trigger an reseed operation. | |
| * | |
| * @param aes Aes dif handle. | |
| */ | |
| static void aes_test(const dif_aes_t *aes) { | |
| LOG_INFO("%s", __func__); | |
| dif_aes_transaction_t transaction = { | |
| .operation = kDifAesOperationEncrypt, | |
| .mode = kDifAesModeEcb, | |
| .key_len = kDifAesKey128, | |
| .key_provider = kDifAesKeySideload, | |
| .mask_reseeding = kDifAesReseedPerBlock, | |
| .manual_operation = kDifAesManualOperationManual, | |
| .reseed_on_key_change = false, | |
| .ctrl_aux_lock = false, | |
| }; | |
| CHECK_DIF_OK(dif_aes_start(aes, &transaction, /*key=*/NULL, /*iv=*/NULL)); | |
| CHECK_DIF_OK(dif_aes_trigger(aes, kDifAesTriggerPrngReseed)); | |
| } | |
| /** | |
| * Configure the kmac to use entropy from edn to request entropy. | |
| */ | |
| static void kmac_test(dif_kmac_t *kmac) { | |
| dif_kmac_operation_state_t kmac_operation_state; | |
| LOG_INFO("%s", __func__); | |
| dif_kmac_config_t config = (dif_kmac_config_t){ | |
| .entropy_mode = kDifKmacEntropyModeEdn, | |
| .entropy_fast_process = kDifToggleDisabled, | |
| }; | |
| CHECK_DIF_OK(dif_kmac_configure(kmac, config)); | |
| } | |
| /** | |
| * Configure the opt to request entropy from the edn. | |
| */ | |
| static void otp_ctrl_test(const dif_otp_ctrl_t *otp) { | |
| LOG_INFO("%s", __func__); | |
| // For security reasons, the LFSR is periodically reseeded with entropy coming | |
| // from EDN. Setting the integrity period to a small value will make the | |
| // otp_ctrl to fetch entropy from EDN. | |
| dif_otp_ctrl_config_t config = { | |
| .check_timeout = 100000, | |
| .integrity_period_mask = 0x4, | |
| .consistency_period_mask = 0x3ffffff, | |
| }; | |
| CHECK_DIF_OK(dif_otp_ctrl_configure(otp, config)); | |
| otp_ctrl_testutils_wait_for_dai(otp); | |
| } | |
| /** | |
| * Configure the reseed interval to a short period and | |
| * advance to the `kDifKeymgrStateInitialized` to request entropy from the edn. | |
| */ | |
| static void keymgr_test(const dif_keymgr_t *kmgr) { | |
| /** | |
| * Key manager can only be tested once per boot. | |
| */ | |
| static bool tested = false; | |
| if (!tested) { | |
| LOG_INFO("%s", __func__); | |
| dif_keymgr_config_t config = {.entropy_reseed_interval = 4}; | |
| CHECK_DIF_OK(dif_keymgr_configure(kmgr, config)); | |
| CHECK_DIF_OK(dif_keymgr_advance_state(kmgr, NULL)); | |
| tested = true; | |
| } | |
| } | |
| /** | |
| * Read 4 words of rnd data to request entropy from the edn. | |
| */ | |
| static void ibex_test(const dif_rv_core_ibex_t *ibex) { | |
| LOG_INFO("%s", __func__); | |
| for (size_t i = 0; i < 4; i++) { | |
| uint32_t rnd; | |
| CHECK_DIF_OK(dif_rv_core_ibex_read_rnd_data(ibex, &rnd)); | |
| } | |
| } | |
| /** | |
| * Configure the `alert_handler`to escalate up to phase 0. | |
| */ | |
| static void alert_handler_configure(const dif_alert_handler_t *alert_handler) { | |
| LOG_INFO("%s", __func__); | |
| dif_alert_handler_local_alert_t loc_alerts[] = { | |
| kDifAlertHandlerLocalAlertAlertPingFail}; | |
| dif_alert_handler_class_t loc_alert_classes[] = {kDifAlertHandlerClassB}; | |
| dif_alert_handler_escalation_phase_t esc_phases[] = { | |
| {.phase = kDifAlertHandlerClassStatePhase0, | |
| .signal = 0, | |
| .duration_cycles = 2000}}; | |
| dif_alert_handler_class_config_t class_config = { | |
| .auto_lock_accumulation_counter = kDifToggleDisabled, | |
| .accumulator_threshold = 0, | |
| .irq_deadline_cycles = 10000, | |
| .escalation_phases = esc_phases, | |
| .escalation_phases_len = ARRAYSIZE(esc_phases), | |
| .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1, | |
| }; | |
| dif_alert_handler_class_config_t class_configs[] = {class_config, | |
| class_config}; | |
| dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA, | |
| kDifAlertHandlerClassB}; | |
| dif_alert_handler_config_t config = { | |
| .alerts = NULL, | |
| .alert_classes = NULL, | |
| .alerts_len = 0, | |
| .local_alerts = loc_alerts, | |
| .local_alert_classes = loc_alert_classes, | |
| .local_alerts_len = ARRAYSIZE(loc_alerts), | |
| .classes = classes, | |
| .class_configs = class_configs, | |
| .classes_len = ARRAYSIZE(class_configs), | |
| .ping_timeout = 10, | |
| }; | |
| alert_handler_testutils_configure_all(alert_handler, config, | |
| kDifToggleEnabled); | |
| } | |
| /** | |
| * Trigger fault in the pwrmgr in order to escalate the alert handler. | |
| */ | |
| static void alert_handler_test(const dif_pwrmgr_t *pwrmgr) { | |
| LOG_INFO("%s", __func__); | |
| dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault; | |
| CHECK_DIF_OK(dif_pwrmgr_alert_force(pwrmgr, alert)); | |
| } | |
| void test_initialize(void) { | |
| CHECK_DIF_OK(dif_entropy_src_init( | |
| mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); | |
| CHECK_DIF_OK(dif_csrng_init( | |
| mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR), &csrng)); | |
| CHECK_DIF_OK( | |
| dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR), &edn0)); | |
| CHECK_DIF_OK( | |
| dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn1)); | |
| CHECK_DIF_OK(dif_rv_core_ibex_init( | |
| mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), &ibex)); | |
| CHECK_DIF_OK(dif_pwrmgr_init( | |
| mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr)); | |
| CHECK_DIF_OK(dif_keymgr_init( | |
| mmio_region_from_addr(TOP_EARLGREY_KEYMGR_BASE_ADDR), &kmgr)); | |
| CHECK_DIF_OK( | |
| dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn)); | |
| CHECK_DIF_OK(dif_otp_ctrl_init( | |
| mmio_region_from_addr(TOP_EARLGREY_OTP_CTRL_CORE_BASE_ADDR), &otp)); | |
| CHECK_DIF_OK( | |
| dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes)); | |
| CHECK_DIF_OK( | |
| dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac)); | |
| CHECK_DIF_OK(dif_alert_handler_init( | |
| mmio_region_from_addr(TOP_EARLGREY_ALERT_HANDLER_BASE_ADDR), | |
| &alert_handler)); | |
| } | |
| /** | |
| * TODO: Run the test entropy src end reqs in continuous mode | |
| * (https://github.com/lowRISC/opentitan/issues/13393) | |
| */ | |
| bool test_main() { | |
| test_initialize(); | |
| alert_handler_configure(&alert_handler); | |
| entropy_testutils_auto_mode_init(); | |
| // ensure health tests are actually running | |
| entropy_testutils_wait_for_state(&entropy_src, | |
| kDifEntropySrcMainFsmStateContHTRunning); | |
| CHECK_DIF_OK(dif_rv_core_ibex_read_fpga_info(&ibex, &fpga_info)); | |
| uint32_t loop = (fpga_info != 0) ? kFpgaLoop : 1; | |
| for (size_t i = 0; i < loop; ++i) { | |
| LOG_INFO("Entropy src test %d/%d", i, loop); | |
| alert_handler_test(&pwrmgr); | |
| aes_test(&aes); | |
| otbn_randomness_test_start(&otbn); | |
| keymgr_test(&kmgr); | |
| otp_ctrl_test(&otp); | |
| kmac_test(&kmac); | |
| ibex_test(&ibex); | |
| AES_TESTUTILS_WAIT_FOR_STATUS(&aes, kDifAesStatusIdle, /*value=*/true, | |
| /*timeout_usec=*/100000); | |
| CHECK(otbn_randomness_test_end(&otbn, /*skip_otbn_done_check=*/false)); | |
| keymgr_testutils_wait_for_operation_done(&kmgr); | |
| keymgr_testutils_check_state(&kmgr, kDifKeymgrStateInitialized); | |
| entropy_testutils_error_check(&entropy_src, &csrng, &edn0, &edn1); | |
| } | |
| return true; | |
| } |