| // 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/testing/entropy_testutils.h" |
| |
| #include "sw/device/lib/base/mmio.h" |
| #include "sw/device/lib/dif/dif_csrng.h" |
| #include "sw/device/lib/dif/dif_csrng_shared.h" |
| #include "sw/device/lib/dif/dif_edn.h" |
| #include "sw/device/lib/dif/dif_entropy_src.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| |
| static void setup_entropy_src(const dif_entropy_src_t *entropy_src) { |
| CHECK_DIF_OK(dif_entropy_src_configure( |
| entropy_src, entropy_testutils_config_default(), kDifToggleEnabled)); |
| } |
| |
| dif_entropy_src_config_t entropy_testutils_config_default(void) { |
| return (dif_entropy_src_config_t){ |
| .fips_enable = true, |
| .route_to_firmware = false, |
| .bypass_conditioner = false, |
| .single_bit_mode = kDifEntropySrcSingleBitModeDisabled, |
| .health_test_window_size = 0x0200, |
| .alert_threshold = 2, |
| }; |
| } |
| |
| void entropy_testutils_auto_mode_init(void) { |
| const dif_entropy_src_t entropy_src = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR)}; |
| const dif_csrng_t csrng = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR)}; |
| const dif_edn_t edn0 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR)}; |
| const dif_edn_t edn1 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR)}; |
| |
| entropy_testutils_stop_all(); |
| |
| // re-eanble entropy src and csrng |
| setup_entropy_src(&entropy_src); |
| CHECK_DIF_OK(dif_csrng_configure(&csrng)); |
| |
| // Re-enable EDN0 in auto mode. |
| CHECK_DIF_OK(dif_edn_set_auto_mode( |
| &edn0, |
| (dif_edn_auto_params_t){ |
| // EDN0 provides lower-quality entropy. Let one generate command |
| // return 8 |
| // blocks, and reseed every 32 generates. |
| .instantiate_cmd = |
| { |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, |
| /*generate_len=*/0), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| .reseed_cmd = |
| { |
| .cmd = csrng_cmd_header_build( |
| kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, /*generate_len=*/0), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| .generate_cmd = |
| { |
| // Generate 8 128-bit blocks. |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, |
| /*generate_len=*/8), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| // Reseed every 32 generates. |
| .reseed_interval = 32, |
| })); |
| |
| // Re-enable EDN1 in auto mode. |
| CHECK_DIF_OK(dif_edn_set_auto_mode( |
| &edn1, |
| (dif_edn_auto_params_t){ |
| // EDN1 provides highest-quality entropy. Let one generate command |
| // return 1 block, and reseed after every generate. |
| .instantiate_cmd = |
| { |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, |
| /*generate_len=*/0), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| .reseed_cmd = |
| { |
| .cmd = csrng_cmd_header_build( |
| kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, /*generate_len=*/0), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| .generate_cmd = |
| { |
| // Generate 1 128-bit block. |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, |
| /*generate_len=*/1), |
| .seed_material = |
| { |
| .len = 0, |
| }, |
| }, |
| // Reseed after every 4 generates. |
| .reseed_interval = 4, |
| })); |
| } |
| |
| void entropy_testutils_boot_mode_init(void) { |
| const dif_entropy_src_t entropy_src = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR)}; |
| const dif_csrng_t csrng = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR)}; |
| const dif_edn_t edn0 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR)}; |
| const dif_edn_t edn1 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR)}; |
| |
| entropy_testutils_stop_all(); |
| |
| setup_entropy_src(&entropy_src); |
| CHECK_DIF_OK(dif_csrng_configure(&csrng)); |
| CHECK_DIF_OK(dif_edn_set_boot_mode(&edn0)); |
| CHECK_DIF_OK(dif_edn_set_boot_mode(&edn1)); |
| CHECK_DIF_OK(dif_edn_configure(&edn0)); |
| CHECK_DIF_OK(dif_edn_configure(&edn1)); |
| } |
| |
| void entropy_testutils_fw_override_enable(dif_entropy_src_t *entropy_src, |
| uint8_t buffer_threshold, |
| bool route_to_firmware, |
| bool bypass_conditioner) { |
| const dif_entropy_src_fw_override_config_t fw_override_config = { |
| .entropy_insert_enable = true, |
| .buffer_threshold = buffer_threshold, |
| }; |
| CHECK_DIF_OK(dif_entropy_src_fw_override_configure( |
| entropy_src, fw_override_config, kDifToggleEnabled)); |
| |
| const dif_entropy_src_config_t config = { |
| .fips_enable = true, |
| .route_to_firmware = route_to_firmware, |
| .bypass_conditioner = bypass_conditioner, |
| .single_bit_mode = kDifEntropySrcSingleBitModeDisabled, |
| .health_test_threshold_scope = false, |
| .health_test_window_size = 0x0200, |
| .alert_threshold = 2, |
| }; |
| CHECK_DIF_OK( |
| dif_entropy_src_configure(entropy_src, config, kDifToggleEnabled)); |
| } |
| |
| void entropy_testutils_wait_for_state(const dif_entropy_src_t *entropy_src, |
| dif_entropy_src_main_fsm_t state) { |
| dif_entropy_src_main_fsm_t cur_state; |
| |
| do { |
| CHECK_DIF_OK(dif_entropy_src_get_main_fsm_state(entropy_src, &cur_state)); |
| } while (cur_state != state); |
| } |
| |
| void entropy_testutils_stop_all(void) { |
| const dif_entropy_src_t entropy_src = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR)}; |
| const dif_csrng_t csrng = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR)}; |
| const dif_edn_t edn0 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN0_BASE_ADDR)}; |
| const dif_edn_t edn1 = { |
| .base_addr = mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR)}; |
| |
| CHECK_DIF_OK(dif_edn_stop(&edn0)); |
| CHECK_DIF_OK(dif_edn_stop(&edn1)); |
| CHECK_DIF_OK(dif_csrng_stop(&csrng)); |
| CHECK_DIF_OK(dif_entropy_src_stop(&entropy_src)); |
| } |
| |
| void entropy_testutils_error_check(const dif_entropy_src_t *entropy_src, |
| const dif_csrng_t *csrng, |
| const dif_edn_t *edn0, |
| const dif_edn_t *edn1) { |
| uint32_t err_code; |
| bool found_error = false; |
| CHECK_DIF_OK(dif_entropy_src_get_errors(entropy_src, &err_code)); |
| if (err_code) { |
| found_error = true; |
| LOG_ERROR("entropy_src status. err: 0x%x", err_code); |
| } |
| |
| dif_csrng_cmd_status_t status; |
| CHECK_DIF_OK(dif_csrng_get_cmd_interface_status(csrng, &status)); |
| if (status.errors) { |
| found_error = true; |
| LOG_ERROR("csrng error status. err: 0x%x, fifo_err: 0x%x, kind: 0x%x", |
| status.errors, status.unhealthy_fifos, status.kind); |
| } |
| |
| uint32_t fifo_errors; |
| CHECK_DIF_OK(dif_edn_get_errors(edn0, &fifo_errors, &err_code)); |
| if (err_code || fifo_errors) { |
| found_error = true; |
| LOG_ERROR("end0 error status. err: 0x%x, fifo_err: 0x%x", err_code, |
| fifo_errors); |
| } |
| |
| CHECK_DIF_OK(dif_edn_get_errors(edn1, &fifo_errors, &err_code)); |
| if (err_code || fifo_errors) { |
| found_error = true; |
| LOG_ERROR("end1 error status. err: 0x%x, fifo_err: 0x%x", err_code, |
| fifo_errors); |
| } |
| CHECK(!found_error, "entropy_testutils_error_check fail"); |
| } |