| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "hw/ip/aes/model/aes_modes.h" |
| #include "sw/device/lib/base/math.h" |
| #include "sw/device/lib/base/multibits.h" |
| #include "sw/device/lib/crypto/drivers/otbn.h" |
| #include "sw/device/lib/crypto/impl/rsa_3072/rsa_3072_verify.h" |
| #include "sw/device/lib/dif/dif_adc_ctrl.h" |
| #include "sw/device/lib/dif/dif_aes.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/dif/dif_flash_ctrl.h" |
| #include "sw/device/lib/dif/dif_gpio.h" |
| #include "sw/device/lib/dif/dif_hmac.h" |
| #include "sw/device/lib/dif/dif_i2c.h" |
| #include "sw/device/lib/dif/dif_kmac.h" |
| #include "sw/device/lib/dif/dif_otbn.h" |
| #include "sw/device/lib/dif/dif_pattgen.h" |
| #include "sw/device/lib/dif/dif_pinmux.h" |
| #include "sw/device/lib/dif/dif_pwm.h" |
| #include "sw/device/lib/dif/dif_rv_plic.h" |
| #include "sw/device/lib/dif/dif_spi_device.h" |
| #include "sw/device/lib/dif/dif_spi_host.h" |
| #include "sw/device/lib/dif/dif_uart.h" |
| #include "sw/device/lib/runtime/log.h" |
| #include "sw/device/lib/testing/aes_testutils.h" |
| #include "sw/device/lib/testing/entropy_testutils.h" |
| #include "sw/device/lib/testing/hmac_testutils.h" |
| #include "sw/device/lib/testing/i2c_testutils.h" |
| #include "sw/device/lib/testing/pinmux_testutils.h" |
| #include "sw/device/lib/testing/spi_device_testutils.h" |
| #include "sw/device/lib/testing/test_framework/check.h" |
| #include "sw/device/lib/testing/test_framework/ottf_macros.h" |
| #include "sw/device/lib/testing/test_framework/ottf_main.h" |
| |
| #include "adc_ctrl_regs.h" // Generated. |
| #include "aes_regs.h" // Generated. |
| #include "csrng_regs.h" // Generated. |
| #include "entropy_src_regs.h" // Generated. |
| #include "gpio_regs.h" // Generated. |
| #include "hmac_regs.h" // Generated. |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| #include "i2c_regs.h" // Generated. |
| #include "kmac_regs.h" // Generated. |
| #include "pattgen_regs.h" // Generated. |
| #include "pwm_regs.h" // Generated. |
| #include "spi_host_regs.h" // Generated. |
| #include "uart_regs.h" // Generated. |
| |
| // The autogen rule that creates this header creates it in a directory named |
| // after the rule, then manipulates the include path in the |
| // cc_compilation_context to include that directory, so the compiler will find |
| // the version of this file matching the Bazel rule under test. |
| #include "rsa_3072_verify_testvectors.h" |
| |
| OTTF_DEFINE_TEST_CONFIG(.enable_concurrency = true, |
| .enable_uart_flow_control = true, |
| .can_clobber_uart = false, ); |
| |
| /** |
| * Peripheral DIF Handles. |
| */ |
| static dif_pinmux_t pinmux; |
| static dif_gpio_t gpio; |
| static dif_adc_ctrl_t adc_ctrl; |
| static dif_entropy_src_t entropy_src; |
| static dif_csrng_t csrng; |
| static dif_edn_t edn_0; |
| static dif_edn_t edn_1; |
| static dif_aes_t aes; |
| static dif_hmac_t hmac; |
| static dif_kmac_t kmac; |
| static dif_otbn_t otbn; |
| static dif_i2c_t i2c_0; |
| static dif_i2c_t i2c_1; |
| static dif_i2c_t i2c_2; |
| static dif_spi_device_handle_t spi_device; |
| static dif_spi_host_t spi_host_0; |
| static dif_spi_host_t spi_host_1; |
| static dif_uart_t uart_1; |
| static dif_uart_t uart_2; |
| static dif_uart_t uart_3; |
| static dif_pattgen_t pattgen; |
| static dif_pwm_t pwm; |
| static dif_flash_ctrl_state_t flash_ctrl; |
| static dif_rv_plic_t rv_plic; |
| |
| static const dif_i2c_t *i2c_handles[] = {&i2c_0, &i2c_1, &i2c_2}; |
| static const dif_uart_t *uart_handles[] = {&uart_1, &uart_2, &uart_3}; |
| static dif_kmac_operation_state_t kmac_operation_state; |
| static const dif_pattgen_channel_t pattgen_channels[] = {kDifPattgenChannel0, |
| kDifPattgenChannel1}; |
| static const dif_pwm_channel_t pwm_channels[PWM_PARAM_N_OUTPUTS] = { |
| kDifPwmChannel0, kDifPwmChannel1, kDifPwmChannel2, |
| kDifPwmChannel3, kDifPwmChannel4, kDifPwmChannel5, |
| }; |
| |
| /** |
| * Test configuration parameters. |
| */ |
| enum { |
| /** |
| * Test timeout parameter. |
| */ |
| kTestTimeoutMicros = 1000, // 1ms |
| /** |
| * ADC controller parameters. |
| */ |
| kAdcCtrlPowerUpTimeAonCycles = 15, // maximum power-up time |
| /** |
| * Entropy Source parameters. |
| */ |
| kEntropySrcHealthTestWindowSize = 0x60, |
| kEntropySrcAdaptiveProportionHealthTestHighThreshold = 0x50, |
| kEntropySrcAdaptiveProportionHealthTestLowThreshold = 0x10, |
| /** |
| * EDN parameters. |
| */ |
| kEdn0SeedMaterialNumWords = 0, |
| kEdn1SeedMaterialNumWords = 12, |
| kEdn0ReseedInterval = 128, |
| kEdn1ReseedInterval = 32, |
| /** |
| * KMAC parameters. |
| */ |
| kKmacEntropyReseedInterval = 1, |
| kKmacEntropyHashThreshold = 1, // KMAC operations between entropy requests |
| kKmacEntropyWaitTimer = 0xffff, |
| kKmacEntropyPrescaler = 0x3ff, |
| kKmacMessageLength = 200, |
| kKmacDigestLength = 16, |
| /** |
| * I2C parameters. |
| */ |
| kI2cSdaRiseFallTimeNs = 10, |
| kI2cDeviceMask = 0x7f, |
| kI2c0DeviceAddress0 = 0x11, |
| kI2c0DeviceAddress1 = 0x22, |
| kI2c1DeviceAddress0 = 0x33, |
| kI2c1DeviceAddress1 = 0x44, |
| kI2c2DeviceAddress0 = 0x55, |
| kI2c2DeviceAddress1 = 0x66, |
| kI2c0TargetAddress = 0x01, |
| kI2c1TargetAddress = 0x02, |
| kI2c2TargetAddress = 0x03, |
| /** |
| * UART parameters. |
| */ |
| kUartFifoDepth = 32, |
| /** |
| * Pattern Generator parameters. |
| */ |
| kPattgenClockDivisor = 0, |
| kPattgenSeedPatternLowerWord = 0xaaaaaaaa, |
| kPattgenSeedPatternUpperWord = 0xaaaaaaaa, |
| kPattgenSeedPatternLength = 64, |
| kPattgenNumPatternRepetitions = 1024, |
| /** |
| * PWM parameters. |
| */ |
| kPwmClockDivisor = 1, |
| kPwmBeatsPerCycle = 2, |
| kPwmOnBeats = 1, |
| kPwmPhaseDelayBeats = 0, |
| /** |
| * SPI Host parameters. |
| */ |
| kSpiHost1Csid = 0xaabbaabb, |
| kSpiHost1TxDataWord = 0xaaaaaaaa, |
| }; |
| |
| static uint32_t csrng_reseed_cmd_header; |
| |
| /** |
| * These symbols are meant to be backdoor read by the testbench. Due to current |
| * DV infrastructure limitations, these must be `volatile const` to be accessed |
| * by the testbench. |
| */ |
| static volatile const uint32_t kI2cSclPeriodNs = 1000; |
| static volatile uint32_t peripheral_clock_period_ns; |
| |
| /** |
| * Pinmux pad configurations. |
| */ |
| static const pinmux_pad_attributes_t pinmux_pad_attributes[] = { |
| // Enable pull-ups for spi_host_0 data pins to avoid floating inputs. |
| { |
| .pad = kTopEarlgreyDirectPadsSpiHost0Sd0, |
| .kind = kDifPinmuxPadKindDio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyDirectPadsSpiHost0Sd1, |
| .kind = kDifPinmuxPadKindDio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyDirectPadsSpiHost0Sd2, |
| .kind = kDifPinmuxPadKindDio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyDirectPadsSpiHost0Sd3, |
| .kind = kDifPinmuxPadKindDio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| // Enable pull-ups for spi_host_1 data pins to avoid floating inputs. |
| { |
| .pad = kTopEarlgreyMuxedPadsIoa2, // SD0 |
| .kind = kDifPinmuxPadKindMio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyMuxedPadsIor11, // SD1 |
| .kind = kDifPinmuxPadKindMio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyMuxedPadsIor12, // SD2 |
| .kind = kDifPinmuxPadKindMio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| { |
| .pad = kTopEarlgreyMuxedPadsIor13, // SD3 |
| .kind = kDifPinmuxPadKindMio, |
| .flags = kDifPinmuxPadAttrPullResistorEnable | |
| kDifPinmuxPadAttrPullResistorUp, |
| }, |
| }; |
| |
| /** |
| * The mask share, used to mask kAesKey. |
| */ |
| static const uint8_t kAesKeyShare1[] = { |
| 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, |
| 0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, |
| 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa, |
| }; |
| |
| static const dif_kmac_key_t kKmacKey = { |
| .share0 = {0x43424140, 0x47464544, 0x4B4A4948, 0x4F4E4D4C, 0x53525150, |
| 0x57565554, 0x5B5A5958, 0x5F5E5D5C}, |
| .share1 = {0}, |
| .length = kDifKmacKeyLen256, |
| }; |
| |
| static const char *kKmacMessage = |
| "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" |
| "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" |
| "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" |
| "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" |
| "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" |
| "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" |
| "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" |
| "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" |
| "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" |
| "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" |
| "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" |
| "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" |
| "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"; |
| |
| static const uint32_t kKmacDigest[] = { |
| 0xF71886B5, 0xD5E1921F, 0x558C1B6C, 0x18CDD7DD, 0xCAB4978B, 0x1E83994D, |
| 0x839A69B2, 0xD9E4A27D, 0xFDACFB70, 0xAE3300E5, 0xA2F185A5, 0xC3108570, |
| 0x0888072D, 0x2818BD01, 0x6847FE98, 0x6589FC76}; |
| |
| static rsa_3072_verify_test_vector_t rsa3072_test_vector; |
| static rsa_3072_int_t rsa3072_encoded_message; |
| static rsa_3072_constants_t rsa3072_constants; |
| |
| static const uint8_t kUartMessage[] = { |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| }; |
| |
| static const uint8_t kI2cMessage[] = { |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, |
| }; |
| |
| static void log_entropy_src_alert_failures(void) { |
| dif_entropy_src_alert_fail_counts_t counts; |
| CHECK_DIF_OK(dif_entropy_src_get_alert_fail_counts(&entropy_src, &counts)); |
| LOG_INFO("Entropy source health test failure encountered."); |
| LOG_INFO("Total Fails: %d", counts.total_fails); |
| for (size_t i = 0; i < kDifEntropySrcTestNumVariants; ++i) { |
| switch (i) { |
| case kDifEntropySrcTestRepetitionCount: |
| LOG_INFO("Fails (Repetition Count): %d", counts.high_fails[i]); |
| break; |
| case kDifEntropySrcTestRepetitionCountSymbol: |
| LOG_INFO("Fails (Repetition Symbol Count): %d", counts.high_fails[i]); |
| break; |
| case kDifEntropySrcTestAdaptiveProportion: |
| LOG_INFO("High Fails (Adaptive Proportion): %d", counts.high_fails[i]); |
| LOG_INFO("Low Fails (Adaptive Proportion): %d", counts.low_fails[i]); |
| break; |
| case kDifEntropySrcTestBucket: |
| LOG_INFO("Fails (Bucket): %d", counts.high_fails[i]); |
| break; |
| case kDifEntropySrcTestMarkov: |
| LOG_INFO("High Fails (Markov): %d", counts.high_fails[i]); |
| LOG_INFO("Low Fails (Markov): %d", counts.low_fails[i]); |
| break; |
| case kDifEntropySrcTestMailbox: |
| LOG_INFO("High Fails (Mailbox): %d", counts.high_fails[i]); |
| LOG_INFO("Low Fails (Mailbox): %d", counts.low_fails[i]); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * External (OTTF) ISR override. |
| */ |
| void ottf_external_isr(void) { |
| // Find which interrupt fired at PLIC by claiming it. |
| dif_rv_plic_irq_id_t irq_id; |
| CHECK_DIF_OK( |
| dif_rv_plic_irq_claim(&rv_plic, kTopEarlgreyPlicTargetIbex0, &irq_id)); |
| |
| top_earlgrey_plic_peripheral_t periph = |
| top_earlgrey_plic_interrupt_for_peripheral[irq_id]; |
| switch (periph) { |
| case kTopEarlgreyPlicPeripheralEntropySrc: |
| log_entropy_src_alert_failures(); |
| CHECK(false); |
| break; |
| default: |
| CHECK(false, "Unexpected IRQ fired with ID: %d", irq_id); |
| break; |
| } |
| } |
| |
| /** |
| * Initializes all DIF handles for each peripheral used in this test. |
| */ |
| static void init_peripheral_handles(void) { |
| CHECK_DIF_OK(dif_adc_ctrl_init( |
| mmio_region_from_addr(TOP_EARLGREY_ADC_CTRL_AON_BASE_ADDR), &adc_ctrl)); |
| CHECK_DIF_OK( |
| dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes)); |
| 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), &edn_0)); |
| CHECK_DIF_OK( |
| dif_edn_init(mmio_region_from_addr(TOP_EARLGREY_EDN1_BASE_ADDR), &edn_1)); |
| CHECK_DIF_OK(dif_entropy_src_init( |
| mmio_region_from_addr(TOP_EARLGREY_ENTROPY_SRC_BASE_ADDR), &entropy_src)); |
| CHECK_DIF_OK( |
| dif_hmac_init(mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR), &hmac)); |
| CHECK_DIF_OK( |
| dif_gpio_init(mmio_region_from_addr(TOP_EARLGREY_GPIO_BASE_ADDR), &gpio)); |
| CHECK_DIF_OK( |
| dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac)); |
| CHECK_DIF_OK(dif_pinmux_init( |
| mmio_region_from_addr(TOP_EARLGREY_PINMUX_AON_BASE_ADDR), &pinmux)); |
| // UART 0 is already configured (and used) by the OTTF. |
| CHECK_DIF_OK(dif_uart_init( |
| mmio_region_from_addr(TOP_EARLGREY_UART1_BASE_ADDR), &uart_1)); |
| CHECK_DIF_OK(dif_uart_init( |
| mmio_region_from_addr(TOP_EARLGREY_UART2_BASE_ADDR), &uart_2)); |
| CHECK_DIF_OK(dif_uart_init( |
| mmio_region_from_addr(TOP_EARLGREY_UART3_BASE_ADDR), &uart_3)); |
| CHECK_DIF_OK( |
| dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C0_BASE_ADDR), &i2c_0)); |
| CHECK_DIF_OK( |
| dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C1_BASE_ADDR), &i2c_1)); |
| CHECK_DIF_OK( |
| dif_i2c_init(mmio_region_from_addr(TOP_EARLGREY_I2C2_BASE_ADDR), &i2c_2)); |
| CHECK_DIF_OK(dif_spi_device_init_handle( |
| mmio_region_from_addr(TOP_EARLGREY_SPI_DEVICE_BASE_ADDR), &spi_device)); |
| CHECK_DIF_OK(dif_spi_host_init( |
| mmio_region_from_addr(TOP_EARLGREY_SPI_HOST0_BASE_ADDR), &spi_host_0)); |
| CHECK_DIF_OK(dif_spi_host_init( |
| mmio_region_from_addr(TOP_EARLGREY_SPI_HOST1_BASE_ADDR), &spi_host_1)); |
| CHECK_DIF_OK( |
| dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn)); |
| CHECK_DIF_OK(dif_pattgen_init( |
| mmio_region_from_addr(TOP_EARLGREY_PATTGEN_BASE_ADDR), &pattgen)); |
| CHECK_DIF_OK(dif_pwm_init( |
| mmio_region_from_addr(TOP_EARLGREY_PWM_AON_BASE_ADDR), &pwm)); |
| CHECK_DIF_OK(dif_flash_ctrl_init_state( |
| &flash_ctrl, |
| mmio_region_from_addr(TOP_EARLGREY_FLASH_CTRL_CORE_BASE_ADDR))); |
| CHECK_DIF_OK(dif_rv_plic_init( |
| mmio_region_from_addr(TOP_EARLGREY_RV_PLIC_BASE_ADDR), &rv_plic)); |
| } |
| |
| static void configure_pinmux(void) { |
| // Configure UART0 (console) and SW strapping pins. |
| pinmux_testutils_init(&pinmux); |
| |
| // Configure GPIO max-power period indicator pin on IOA2 |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa2, |
| kTopEarlgreyPinmuxOutselGpioGpio0)); |
| |
| // UART1: |
| // RX on IOA4 |
| // TX on IOA5 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInUart1Rx, |
| kTopEarlgreyPinmuxInselIoa4)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa4, |
| kTopEarlgreyPinmuxOutselConstantHighZ)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa5, |
| kTopEarlgreyPinmuxOutselUart1Tx)); |
| |
| // UART2: |
| // RX on IOB4 |
| // TX on IOB5 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInUart2Rx, |
| kTopEarlgreyPinmuxInselIob4)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob4, |
| kTopEarlgreyPinmuxOutselConstantHighZ)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob5, |
| kTopEarlgreyPinmuxOutselUart2Tx)); |
| |
| // UART3: |
| // RX on IOA0 |
| // TX on IOA1 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInUart3Rx, |
| kTopEarlgreyPinmuxInselIoa0)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa0, |
| kTopEarlgreyPinmuxOutselConstantHighZ)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa1, |
| kTopEarlgreyPinmuxOutselUart3Tx)); |
| |
| // I2C0: |
| // SDA on IOA7 |
| // SCL on IOA8 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c0Sda, |
| kTopEarlgreyPinmuxInselIoa7)); |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c0Scl, |
| kTopEarlgreyPinmuxInselIoa8)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa7, |
| kTopEarlgreyPinmuxOutselI2c0Sda)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa8, |
| kTopEarlgreyPinmuxOutselI2c0Scl)); |
| |
| // I2C1: |
| // SDA on IOB9 |
| // SCL on IOB10 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c1Sda, |
| kTopEarlgreyPinmuxInselIob9)); |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c1Scl, |
| kTopEarlgreyPinmuxInselIob10)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob9, |
| kTopEarlgreyPinmuxOutselI2c1Sda)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob10, |
| kTopEarlgreyPinmuxOutselI2c1Scl)); |
| |
| // I2C2: |
| // SDA on IOB11 |
| // SCL on IOB12 |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c2Sda, |
| kTopEarlgreyPinmuxInselIob11)); |
| CHECK_DIF_OK(dif_pinmux_input_select(&pinmux, |
| kTopEarlgreyPinmuxPeripheralInI2c2Scl, |
| kTopEarlgreyPinmuxInselIob12)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob11, |
| kTopEarlgreyPinmuxOutselI2c2Sda)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob12, |
| kTopEarlgreyPinmuxOutselI2c2Scl)); |
| |
| // Enable pull-ups for SPI_HOST_0/1 data pins to avoid floating inputs. |
| if (kDeviceType == kDeviceSimDV || kDeviceType == kDeviceSimVerilator) { |
| pinmux_testutils_configure_pads(&pinmux, pinmux_pad_attributes, |
| ARRAYSIZE(pinmux_pad_attributes)); |
| } |
| |
| // PATTGEN: |
| // Channel 0 PDA on IOR0 |
| // Channel 0 PCL on IOR1 |
| // Channel 1 PDA on IOR2 |
| // Channel 1 PCL on IOR3 |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor0, |
| kTopEarlgreyPinmuxOutselPattgenPda0Tx)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor1, |
| kTopEarlgreyPinmuxOutselPattgenPcl0Tx)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor2, |
| kTopEarlgreyPinmuxOutselPattgenPda1Tx)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor3, |
| kTopEarlgreyPinmuxOutselPattgenPcl1Tx)); |
| |
| // PWM: |
| // Channel 0 on IOB1 |
| // Channel 1 on IOB2 |
| // Channel 2 on IOR5 |
| // Channel 3 on IOR6 |
| // Channel 4 on IOR7 |
| // Channel 5 on IOR10 |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob1, |
| kTopEarlgreyPinmuxOutselPwmAonPwm0)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob2, |
| kTopEarlgreyPinmuxOutselPwmAonPwm1)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor5, |
| kTopEarlgreyPinmuxOutselPwmAonPwm2)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor6, |
| kTopEarlgreyPinmuxOutselPwmAonPwm3)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor7, |
| kTopEarlgreyPinmuxOutselPwmAonPwm4)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor10, |
| kTopEarlgreyPinmuxOutselPwmAonPwm5)); |
| |
| // SPI Host 1: |
| // CSB on IOB0 |
| // SCK on IOB3 |
| // SD0 on IOA2 |
| // SD1 on IOR11 |
| // SD2 on IOR12 |
| // SD3 on IOR13 |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob0, |
| kTopEarlgreyPinmuxOutselSpiHost1Csb)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIob3, |
| kTopEarlgreyPinmuxOutselSpiHost1Sck)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIoa2, |
| kTopEarlgreyPinmuxOutselSpiHost1Sd0)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor11, |
| kTopEarlgreyPinmuxOutselSpiHost1Sd1)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor12, |
| kTopEarlgreyPinmuxOutselSpiHost1Sd1)); |
| CHECK_DIF_OK(dif_pinmux_output_select(&pinmux, kTopEarlgreyPinmuxMioOutIor13, |
| kTopEarlgreyPinmuxOutselSpiHost1Sd1)); |
| CHECK_DIF_OK(dif_pinmux_input_select( |
| &pinmux, kTopEarlgreyPinmuxPeripheralInSpiHost1Sd0, |
| kTopEarlgreyPinmuxInselIoa2)); |
| CHECK_DIF_OK(dif_pinmux_input_select( |
| &pinmux, kTopEarlgreyPinmuxPeripheralInSpiHost1Sd1, |
| kTopEarlgreyPinmuxInselIor11)); |
| CHECK_DIF_OK(dif_pinmux_input_select( |
| &pinmux, kTopEarlgreyPinmuxPeripheralInSpiHost1Sd2, |
| kTopEarlgreyPinmuxInselIor12)); |
| CHECK_DIF_OK(dif_pinmux_input_select( |
| &pinmux, kTopEarlgreyPinmuxPeripheralInSpiHost1Sd3, |
| kTopEarlgreyPinmuxInselIor13)); |
| } |
| |
| /** |
| * Configures adc_ctrl to continuously sample data (applying all filters across |
| * both channels) in normal power mode, which is the most power intensive |
| * sampling mode. |
| */ |
| static void configure_adc_ctrl_to_continuously_sample(void) { |
| CHECK_DIF_OK(dif_adc_ctrl_configure( |
| &adc_ctrl, |
| (dif_adc_ctrl_config_t){ |
| .mode = kDifAdcCtrlNormalPowerScanMode, |
| .power_up_time_aon_cycles = kAdcCtrlPowerUpTimeAonCycles, |
| // Below configurations are unused, so set them to their reset |
| // values. |
| .wake_up_time_aon_cycles = ADC_CTRL_ADC_PD_CTL_WAKEUP_TIME_MASK, |
| .num_low_power_samples = ADC_CTRL_ADC_LP_SAMPLE_CTL_REG_RESVAL, |
| .num_normal_power_samples = ADC_CTRL_ADC_SAMPLE_CTL_REG_RESVAL, |
| })); |
| for (size_t filter = 0; filter < ADC_CTRL_PARAM_NUM_ADC_FILTER; ++filter) { |
| for (size_t channel = 0; channel < ADC_CTRL_PARAM_NUM_ADC_CHANNEL; |
| ++channel) { |
| CHECK_DIF_OK(dif_adc_ctrl_configure_filter( |
| &adc_ctrl, (dif_adc_ctrl_channel_t)channel, |
| (dif_adc_ctrl_filter_config_t){ |
| .filter = (dif_adc_ctrl_filter_t)filter, |
| // Set max range. |
| .min_voltage = 0, |
| .max_voltage = ADC_CTRL_ADC_CHN0_FILTER_CTL_0_MAX_V_0_MASK, |
| .in_range = true, |
| .generate_wakeup_on_match = false, |
| .generate_irq_on_match = false, |
| }, |
| kDifToggleEnabled)); |
| } |
| } |
| } |
| |
| static void configure_entropy_complex(void) { |
| // The (test) ROM enables the entropy complex, and to reconfigure it |
| // requires temporarily disabling it. |
| entropy_testutils_stop_all(); |
| |
| // Enable entropy_src interrupts for health-test alert detection. |
| CHECK_DIF_OK(dif_rv_plic_irq_set_priority( |
| &rv_plic, kTopEarlgreyPlicIrqIdEntropySrcEsHealthTestFailed, 0x1)); |
| CHECK_DIF_OK(dif_rv_plic_irq_set_enabled( |
| &rv_plic, kTopEarlgreyPlicIrqIdEntropySrcEsHealthTestFailed, |
| kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled)); |
| CHECK_DIF_OK(dif_entropy_src_irq_set_enabled( |
| &entropy_src, kDifEntropySrcIrqEsHealthTestFailed, kDifToggleEnabled)); |
| |
| // Configure entropy_src and health tests. |
| CHECK_DIF_OK(dif_entropy_src_health_test_configure( |
| &entropy_src, (dif_entropy_src_health_test_config_t){ |
| .test_type = kDifEntropySrcTestRepetitionCount, |
| .high_threshold = 0xf, |
| .low_threshold = 0})); |
| CHECK_DIF_OK(dif_entropy_src_health_test_configure( |
| &entropy_src, (dif_entropy_src_health_test_config_t){ |
| .test_type = kDifEntropySrcTestRepetitionCountSymbol, |
| .high_threshold = 0xf, |
| .low_threshold = 0})); |
| CHECK_DIF_OK(dif_entropy_src_health_test_configure( |
| &entropy_src, |
| (dif_entropy_src_health_test_config_t){ |
| .test_type = kDifEntropySrcTestAdaptiveProportion, |
| .high_threshold = |
| kEntropySrcAdaptiveProportionHealthTestHighThreshold, |
| .low_threshold = |
| kEntropySrcAdaptiveProportionHealthTestLowThreshold})); |
| CHECK_DIF_OK(dif_entropy_src_health_test_configure( |
| &entropy_src, (dif_entropy_src_health_test_config_t){ |
| .test_type = kDifEntropySrcTestBucket, |
| .high_threshold = 0xff, |
| .low_threshold = 0})); |
| CHECK_DIF_OK(dif_entropy_src_health_test_configure( |
| &entropy_src, (dif_entropy_src_health_test_config_t){ |
| .test_type = kDifEntropySrcTestMarkov, |
| .high_threshold = 0xff, |
| .low_threshold = 0})); |
| CHECK_DIF_OK(dif_entropy_src_fw_override_configure( |
| &entropy_src, |
| (dif_entropy_src_fw_override_config_t){ |
| .entropy_insert_enable = false, |
| .buffer_threshold = ENTROPY_SRC_OBSERVE_FIFO_THRESH_REG_RESVAL, |
| }, |
| kDifToggleDisabled)); |
| CHECK_DIF_OK(dif_entropy_src_configure( |
| &entropy_src, |
| (dif_entropy_src_config_t){ |
| .fips_enable = true, |
| .route_to_firmware = false, |
| .bypass_conditioner = false, |
| .single_bit_mode = kDifEntropySrcSingleBitModeDisabled, |
| .health_test_threshold_scope = false, |
| .health_test_window_size = kEntropySrcHealthTestWindowSize, |
| .alert_threshold = UINT16_MAX}, |
| kDifToggleEnabled)); |
| |
| // Configure CSRNG and create reseed command header for later use during max |
| // power epoch. |
| CHECK_DIF_OK(dif_csrng_configure(&csrng)); |
| csrng_reseed_cmd_header = csrng_cmd_header_build( |
| kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable, /*cmd_len=*/0, |
| /*generate_len=*/0); |
| |
| // Configure EDNs in auto mode. |
| dif_edn_seed_material_t edn_empty_seed = { |
| .len = kEdn0SeedMaterialNumWords, |
| }; |
| dif_edn_seed_material_t edn_384_bit_seed = { |
| .len = kEdn1SeedMaterialNumWords, |
| .data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, |
| }; |
| dif_edn_auto_params_t edn_auto_params = { |
| .instantiate_cmd = |
| { |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdInstantiate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/edn_384_bit_seed.len, |
| /*generate_len=*/0), |
| .seed_material = edn_384_bit_seed, |
| }, |
| .reseed_cmd = |
| { |
| .cmd = csrng_cmd_header_build( |
| kCsrngAppCmdReseed, kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/edn_384_bit_seed.len, /*generate_len=*/0), |
| .seed_material = edn_384_bit_seed, |
| }, |
| .generate_cmd = |
| { |
| .cmd = csrng_cmd_header_build(kCsrngAppCmdGenerate, |
| kDifCsrngEntropySrcToggleEnable, |
| /*cmd_len=*/0, |
| /*generate_len=*/4096), |
| .seed_material = edn_empty_seed, |
| }, |
| .reseed_interval = 0, |
| }; |
| // EDN0 provides lower-quality entropy. Let one generate command return |
| // eight 128-bit blocks, and reseed every 128 generates. |
| edn_auto_params.reseed_interval = kEdn0ReseedInterval; |
| CHECK_DIF_OK(dif_edn_set_auto_mode(&edn_0, edn_auto_params)); |
| // EDN1 provides higher-quality entropy. Let one generate command return |
| // eight 128-bit blocks, and reseed every 32 generates. |
| edn_auto_params.reseed_interval = kEdn1ReseedInterval; |
| CHECK_DIF_OK(dif_edn_set_auto_mode(&edn_1, edn_auto_params)); |
| CHECK_DIF_OK(dif_edn_configure(&edn_0)); |
| CHECK_DIF_OK(dif_edn_configure(&edn_1)); |
| } |
| |
| static void configure_aes(void) { |
| // Prepare and load AES key shares. |
| uint8_t aes_key_share0[sizeof(kAesModesKey256)]; |
| for (size_t i = 0; i < sizeof(kAesModesKey256); ++i) { |
| aes_key_share0[i] = kAesModesKey256[i] ^ kAesKeyShare1[i]; |
| } |
| dif_aes_key_share_t aes_key; |
| memcpy(aes_key.share0, aes_key_share0, sizeof(aes_key.share0)); |
| memcpy(aes_key.share1, kAesKeyShare1, sizeof(aes_key.share1)); |
| |
| // Prepare and load AES IV. |
| dif_aes_iv_t aes_iv; |
| memcpy(aes_iv.iv, kAesModesIvCbc, sizeof(aes_iv.iv)); |
| |
| // Setup AES in automatic, 256-bit SW provided key, CBC encryption mode. |
| // Additionally, we want to keep the entropy complex busing by constantly |
| // reseeding PRNGs. |
| dif_aes_transaction_t aes_transaction_cfg = { |
| .operation = kDifAesOperationEncrypt, |
| .mode = kDifAesModeCbc, |
| .key_len = kDifAesKey256, |
| .key_provider = kDifAesKeySoftwareProvided, |
| .mask_reseeding = kDifAesReseedPerBlock, |
| .manual_operation = kDifAesManualOperationManual, |
| .reseed_on_key_change = false, |
| .ctrl_aux_lock = false, |
| }; |
| |
| // Start the AES operation. Since we are in manual-mode, the encryption will |
| // not start until plain text data is loaded into the appropriate CSRs, and |
| // the encryption operation is triggered. |
| AES_TESTUTILS_WAIT_FOR_STATUS(&aes, kDifAesStatusIdle, true, |
| kTestTimeoutMicros); |
| CHECK_DIF_OK(dif_aes_start(&aes, &aes_transaction_cfg, &aes_key, &aes_iv)); |
| } |
| |
| static void configure_hmac(void) { |
| dif_hmac_transaction_t hmac_transaction_cfg = { |
| .digest_endianness = kDifHmacEndiannessLittle, |
| .message_endianness = kDifHmacEndiannessLittle, |
| }; |
| // Use HMAC in SHA256 mode to generate a 256bit key from `kHmacRefLongKey`. |
| CHECK_DIF_OK(dif_hmac_mode_sha256_start(&hmac, hmac_transaction_cfg)); |
| hmac_testutils_push_message(&hmac, (char *)kHmacRefLongKey, |
| sizeof(kHmacRefLongKey)); |
| hmac_testutils_check_message_length(&hmac, sizeof(kHmacRefLongKey) * 8); |
| CHECK_DIF_OK(dif_hmac_process(&hmac)); |
| dif_hmac_digest_t hmac_key_digest; |
| hmac_testutils_finish_polled(&hmac, &hmac_key_digest); |
| |
| // Configure the HMAC in HMAC mode. |
| CHECK_DIF_OK(dif_hmac_mode_hmac_start( |
| &hmac, (uint8_t *)&hmac_key_digest.digest[0], hmac_transaction_cfg)); |
| } |
| |
| static void configure_kmac(void) { |
| dif_kmac_config_t kmac_cfg = (dif_kmac_config_t){ |
| .entropy_mode = kDifKmacEntropyModeEdn, |
| .entropy_fast_process = kDifToggleDisabled, |
| .entropy_hash_threshold = kKmacEntropyHashThreshold, |
| .entropy_wait_timer = kKmacEntropyWaitTimer, |
| .entropy_prescaler = kKmacEntropyPrescaler, |
| .message_big_endian = false, |
| .output_big_endian = false, |
| .sideload = false, |
| .msg_mask = false, |
| }; |
| CHECK_DIF_OK(dif_kmac_configure(&kmac, kmac_cfg)); |
| } |
| |
| static void configure_uart(dif_uart_t *uart) { |
| CHECK_DIF_OK( |
| dif_uart_configure(uart, (dif_uart_config_t){ |
| .baudrate = kUartBaudrate, |
| .clk_freq_hz = kClockFreqPeripheralHz, |
| .parity_enable = kDifToggleEnabled, |
| .parity = kDifUartParityEven, |
| .tx_enable = kDifToggleDisabled, |
| .rx_enable = kDifToggleDisabled, |
| })); |
| CHECK_DIF_OK( |
| dif_uart_loopback_set(uart, kDifUartLoopbackSystem, kDifToggleEnabled)); |
| } |
| |
| static void configure_i2c(dif_i2c_t *i2c, uint8_t device_addr_0, |
| uint8_t device_addr_1) { |
| dif_i2c_config_t config; |
| |
| CHECK_DIF_OK(dif_i2c_compute_timing( |
| (dif_i2c_timing_config_t){ |
| .lowest_target_device_speed = kDifI2cSpeedFastPlus, |
| .clock_period_nanos = peripheral_clock_period_ns, |
| .sda_rise_nanos = kI2cSdaRiseFallTimeNs, |
| .sda_fall_nanos = kI2cSdaRiseFallTimeNs, |
| .scl_period_nanos = kI2cSclPeriodNs}, |
| &config)); |
| CHECK_DIF_OK(dif_i2c_configure(i2c, config)); |
| dif_i2c_id_t id_0 = {.mask = kI2cDeviceMask, .address = device_addr_0}; |
| dif_i2c_id_t id_1 = {.mask = kI2cDeviceMask, .address = device_addr_1}; |
| CHECK_DIF_OK(dif_i2c_set_device_id(i2c, &id_0, &id_1)); |
| CHECK_DIF_OK(dif_i2c_line_loopback_set_enabled(i2c, kDifToggleEnabled)); |
| } |
| |
| static void configure_spi_host(const dif_spi_host_t *spi_host, bool enable) { |
| CHECK_DIF_OK(dif_spi_host_configure( |
| spi_host, (dif_spi_host_config_t){ |
| .spi_clock = kClockFreqHiSpeedPeripheralHz / 2, |
| .peripheral_clock_freq_hz = kClockFreqHiSpeedPeripheralHz, |
| .chip_select = |
| { |
| .idle = 2, |
| .trail = 2, |
| .lead = 2, |
| }, |
| })); |
| CHECK_DIF_OK(dif_spi_host_output_set_enabled(spi_host, /*enabled=*/enable)); |
| } |
| |
| void configure_pattgen(void) { |
| for (size_t i = 0; i < ARRAYSIZE(pattgen_channels); ++i) { |
| CHECK_DIF_OK(dif_pattgen_channel_set_enabled(&pattgen, pattgen_channels[i], |
| kDifToggleDisabled)); |
| CHECK_DIF_OK(dif_pattgen_configure_channel( |
| &pattgen, pattgen_channels[i], |
| (dif_pattgen_channel_config_t){ |
| .polarity = kDifPattgenPolarityHigh, |
| .clock_divisor = kPattgenClockDivisor, |
| .seed_pattern_lower_word = kPattgenSeedPatternLowerWord, |
| .seed_pattern_upper_word = kPattgenSeedPatternUpperWord, |
| .seed_pattern_length = kPattgenSeedPatternLength, |
| .num_pattern_repetitions = kPattgenNumPatternRepetitions, |
| |
| })); |
| } |
| } |
| |
| void configure_pwm(void) { |
| CHECK_DIF_OK( |
| dif_pwm_configure(&pwm, (dif_pwm_config_t){ |
| .clock_divisor = kPwmClockDivisor, |
| .beats_per_pulse_cycle = kPwmBeatsPerCycle, |
| })); |
| CHECK_DIF_OK(dif_pwm_channel_set_enabled( |
| &pwm, (1u << PWM_PARAM_N_OUTPUTS) - 1, kDifToggleDisabled)); |
| for (size_t i = 0; i < PWM_PARAM_N_OUTPUTS; ++i) { |
| CHECK_DIF_OK( |
| dif_pwm_configure_channel(&pwm, pwm_channels[i], |
| (dif_pwm_channel_config_t){ |
| .duty_cycle_a = kPwmOnBeats, |
| .duty_cycle_b = 0, // unused |
| .phase_delay = kPwmPhaseDelayBeats, |
| .mode = kDifPwmModeFirmware, |
| .polarity = kDifPwmPolarityActiveHigh, |
| .blink_parameter_x = 0, // unused |
| .blink_parameter_y = 0, // unused |
| })); |
| } |
| } |
| |
| static void configure_otbn(void) { |
| rsa3072_test_vector = rsa_3072_verify_tests[0]; |
| // Only one exponent (65537) is currently supported. |
| CHECK(rsa3072_test_vector.publicKey.e == 65537); |
| CHECK_STATUS_OK(rsa_3072_encode_sha256(rsa3072_test_vector.msg, |
| rsa3072_test_vector.msgLen, |
| &rsa3072_encoded_message)); |
| CHECK_STATUS_OK(rsa_3072_compute_constants(&rsa3072_test_vector.publicKey, |
| &rsa3072_constants)); |
| } |
| |
| static void check_crypto_blocks_idle(void) { |
| // CSRNG |
| CHECK(mmio_region_get_bit32(csrng.base_addr, CSRNG_SW_CMD_STS_REG_OFFSET, |
| CSRNG_SW_CMD_STS_CMD_RDY_BIT)); |
| // AES |
| CHECK(aes_testutils_get_status(&aes, kDifAesStatusIdle)); |
| // HMAC - no status register to check. |
| // KMAC |
| dif_kmac_status_t kmac_status; |
| CHECK_DIF_OK(dif_kmac_get_status(&kmac, &kmac_status)); |
| CHECK(kmac_status.sha3_state == kDifKmacSha3StateAbsorbing); |
| // OTBN |
| dif_otbn_status_t otbn_status; |
| CHECK_DIF_OK(dif_otbn_get_status(&otbn, &otbn_status)); |
| CHECK(otbn_status == kDifOtbnStatusIdle); |
| } |
| |
| static void complete_kmac_operations(uint32_t *digest) { |
| // Poll the status register until in the 'squeeze' state. |
| CHECK_DIF_OK(dif_kmac_poll_status(&kmac, KMAC_STATUS_SHA3_SQUEEZE_BIT)); |
| |
| // Read both shares of digest from state register and combine using XOR. |
| uint32_t digest_offset = KMAC_STATE_REG_OFFSET; |
| for (size_t i = 0; i < kKmacDigestLength; ++i) { |
| uint32_t share0 = mmio_region_read32(kmac.base_addr, digest_offset); |
| uint32_t share1 = mmio_region_read32( |
| kmac.base_addr, digest_offset + kDifKmacStateShareOffset); |
| digest[i] = share0 ^ share1; |
| digest_offset += sizeof(uint32_t); |
| } |
| kmac_operation_state.offset += kKmacDigestLength; |
| |
| // Complete KMAC operations and reset operation state. |
| CHECK_DIF_OK(dif_kmac_end(&kmac, &kmac_operation_state)); |
| } |
| |
| static void crypto_data_load_task(void *task_parameters) { |
| LOG_INFO("Loading crypto block FIFOs with data ..."); |
| |
| // Load data into AES block. |
| dif_aes_data_t aes_plain_text; |
| memcpy(aes_plain_text.data, kAesModesPlainText, sizeof(aes_plain_text.data)); |
| AES_TESTUTILS_WAIT_FOR_STATUS(&aes, kDifAesStatusInputReady, true, |
| kTestTimeoutMicros); |
| CHECK_DIF_OK(dif_aes_load_data(&aes, aes_plain_text)); |
| |
| // Load data into HMAC block. |
| hmac_testutils_push_message(&hmac, kHmacRefData, sizeof(kHmacRefData)); |
| hmac_testutils_check_message_length(&hmac, sizeof(kHmacRefData) * 8); |
| |
| // Load data into KMAC block. |
| dif_kmac_customization_string_t kmac_customization_string; |
| CHECK_DIF_OK(dif_kmac_customization_string_init("My Tagged Application", 21, |
| &kmac_customization_string)); |
| CHECK_DIF_OK(dif_kmac_mode_kmac_start( |
| &kmac, &kmac_operation_state, kDifKmacModeKmacLen256, kKmacDigestLength, |
| &kKmacKey, &kmac_customization_string)); |
| CHECK_DIF_OK(dif_kmac_absorb(&kmac, &kmac_operation_state, kKmacMessage, |
| kKmacMessageLength, NULL)); |
| // Prepare KMAC for squeeze command (to come later in max power epoch) by |
| // formatting message for KMAC operation. Note, below code is derived from |
| // the KMAC DIF: `dif_kmac_sqeeze()`. |
| CHECK(!kmac_operation_state.squeezing); |
| if (kmac_operation_state.append_d) { |
| // The KMAC operation requires that the output length (d) in bits be |
| // right encoded and appended to the end of the message. |
| uint32_t kmac_output_length_bits = kmac_operation_state.d * 32; |
| int len = 1 + (kmac_output_length_bits > 0xFF) + |
| (kmac_output_length_bits > 0xFFFF) + |
| (kmac_output_length_bits > 0xFFFFFF); |
| int shift = (len - 1) * 8; |
| while (shift >= 8) { |
| mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, |
| (uint8_t)(kmac_output_length_bits >> shift)); |
| shift -= 8; |
| } |
| mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, |
| (uint8_t)kmac_output_length_bits); |
| mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, (uint8_t)len); |
| } |
| |
| OTTF_TASK_DELETE_SELF_OR_DIE; |
| } |
| |
| static void comms_data_load_task(void *task_parameters) { |
| LOG_INFO("Loading communication block FIFOs with data ..."); |
| size_t bytes_written; |
| CHECK(ARRAYSIZE(kUartMessage) == kUartFifoDepth); |
| CHECK(ARRAYSIZE(kI2cMessage) == I2C_PARAM_FIFO_DEPTH); |
| |
| // Load data into UART FIFOs. |
| for (size_t i = 0; i < ARRAYSIZE(uart_handles); ++i) { |
| bytes_written = 0; |
| CHECK_DIF_OK(dif_uart_bytes_send(uart_handles[i], kUartMessage, |
| ARRAYSIZE(kUartMessage), &bytes_written)); |
| CHECK(bytes_written == ARRAYSIZE(kUartMessage)); |
| } |
| |
| // Load data into I2C FIFOs. |
| dif_i2c_status_t i2c_status; |
| for (size_t i = 0; i < ARRAYSIZE(i2c_handles); ++i) { |
| i2c_testutils_wr(i2c_handles[i], /*addr=*/i, I2C_PARAM_FIFO_DEPTH, |
| kI2cMessage, /*skip_stop=*/false); |
| CHECK_DIF_OK(dif_i2c_get_status(i2c_handles[i], &i2c_status)); |
| CHECK(i2c_status.fmt_fifo_full); |
| } |
| |
| // Load data into SPI host (1; as 0 is used in passthrough mode) FIFO. |
| uint32_t spi_host_tx_data[SPI_HOST_PARAM_TX_DEPTH]; |
| for (size_t i = 0; i < SPI_HOST_PARAM_TX_DEPTH; ++i) { |
| spi_host_tx_data[i] = kSpiHost1TxDataWord; |
| } |
| dif_spi_host_segment_t spi_host_tx_segment = { |
| .type = kDifSpiHostSegmentTypeTx, |
| .tx = { |
| .width = kDifSpiHostWidthQuad, |
| .buf = (void *)&spi_host_tx_data, |
| .length = ARRAYSIZE(spi_host_tx_data), |
| }}; |
| CHECK_DIF_OK(dif_spi_host_transaction(&spi_host_1, kSpiHost1Csid, |
| &spi_host_tx_segment, 1)); |
| |
| OTTF_TASK_DELETE_SELF_OR_DIE; |
| } |
| |
| static void max_power_task(void *task_parameters) { |
| LOG_INFO("Starting the max power task ..."); |
| // *************************************************************************** |
| // Trigger all chip operations. |
| // |
| // Note: We trigger the activations of each operation manually, rather than |
| // use the DIFs, so that we can maximize the time overlap between all |
| // operations. |
| // *************************************************************************** |
| |
| // Prepare AES, HMAC, and KMAC trigger / process commands. |
| uint32_t aes_trigger_reg = bitfield_bit32_write(0, kDifAesTriggerStart, true); |
| uint32_t hmac_cmd_reg = |
| mmio_region_read32(hmac.base_addr, HMAC_CMD_REG_OFFSET); |
| hmac_cmd_reg = |
| bitfield_bit32_write(hmac_cmd_reg, HMAC_CMD_HASH_PROCESS_BIT, true); |
| uint32_t kmac_cmd_reg = |
| bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_PROCESS); |
| |
| // Prepare UART, I2C, SPI host enablement commands (note, all configurations |
| // between each IP instance should be configured the same). |
| uint32_t uart_ctrl_reg = |
| mmio_region_read32(uart_1.base_addr, UART_CTRL_REG_OFFSET); |
| uart_ctrl_reg = bitfield_bit32_write(uart_ctrl_reg, UART_CTRL_TX_BIT, true); |
| uart_ctrl_reg = bitfield_bit32_write(uart_ctrl_reg, UART_CTRL_RX_BIT, true); |
| uint32_t i2c_ctrl_reg = |
| mmio_region_read32(i2c_0.base_addr, I2C_CTRL_REG_OFFSET); |
| i2c_ctrl_reg = |
| bitfield_bit32_write(i2c_ctrl_reg, I2C_CTRL_ENABLEHOST_BIT, true); |
| uint32_t spi_host_1_ctrl_reg = |
| mmio_region_read32(spi_host_1.base_addr, SPI_HOST_CONTROL_REG_OFFSET); |
| spi_host_1_ctrl_reg = bitfield_bit32_write( |
| spi_host_1_ctrl_reg, SPI_HOST_CONTROL_OUTPUT_EN_BIT, true); |
| |
| // Prepare pattgen enablement command. |
| uint32_t pattgen_ctrl_reg = |
| mmio_region_read32(pattgen.base_addr, PATTGEN_CTRL_REG_OFFSET); |
| pattgen_ctrl_reg = |
| bitfield_bit32_write(pattgen_ctrl_reg, PATTGEN_CTRL_ENABLE_CH0_BIT, true); |
| pattgen_ctrl_reg = |
| bitfield_bit32_write(pattgen_ctrl_reg, PATTGEN_CTRL_ENABLE_CH1_BIT, true); |
| |
| // Prepare GPIO register values (for max power indicator). |
| const uint32_t gpio_on_reg_val = (1u << 16) | 1u; |
| const uint32_t gpio_off_reg_val = 1u << 16; |
| |
| check_crypto_blocks_idle(); |
| |
| LOG_INFO("Entering max power epoch ..."); |
| |
| // Enable all PWM channels |
| mmio_region_write32(pwm.base_addr, PWM_PWM_EN_REG_OFFSET, |
| (1u << PWM_PARAM_N_OUTPUTS) - 1); |
| |
| // Enable all UARTs and I2Cs. |
| mmio_region_write32(uart_1.base_addr, UART_CTRL_REG_OFFSET, uart_ctrl_reg); |
| mmio_region_write32(uart_2.base_addr, UART_CTRL_REG_OFFSET, uart_ctrl_reg); |
| mmio_region_write32(uart_3.base_addr, UART_CTRL_REG_OFFSET, uart_ctrl_reg); |
| mmio_region_write32(i2c_0.base_addr, I2C_CTRL_REG_OFFSET, i2c_ctrl_reg); |
| mmio_region_write32(i2c_1.base_addr, I2C_CTRL_REG_OFFSET, i2c_ctrl_reg); |
| mmio_region_write32(i2c_2.base_addr, I2C_CTRL_REG_OFFSET, i2c_ctrl_reg); |
| |
| // Issue OTBN start command. |
| CHECK_STATUS_OK(rsa_3072_verify_start(&rsa3072_test_vector.signature, |
| &rsa3072_test_vector.publicKey, |
| &rsa3072_constants)); |
| |
| // Enable pattgen. |
| mmio_region_write32(pattgen.base_addr, PATTGEN_CTRL_REG_OFFSET, |
| pattgen_ctrl_reg); |
| |
| // Enable SPI host (1). |
| mmio_region_write32(spi_host_1.base_addr, SPI_HOST_CONTROL_REG_OFFSET, |
| spi_host_1_ctrl_reg); |
| |
| // Request entropy during max power epoch. Since AES is so fast, realistically |
| // we will only be able to request a single block of entropy. |
| mmio_region_write32(csrng.base_addr, CSRNG_CMD_REQ_REG_OFFSET, |
| csrng_reseed_cmd_header); |
| |
| // Issue HMAC process and KMAC squeeze commands. |
| mmio_region_write32(hmac.base_addr, HMAC_CMD_REG_OFFSET, hmac_cmd_reg); |
| kmac_operation_state.squeezing = true; |
| mmio_region_write32(kmac.base_addr, KMAC_CMD_REG_OFFSET, kmac_cmd_reg); |
| |
| // Toggle GPIO pin to indicate we are in max power consumption epoch. Note, we |
| // do this BEFORE triggering the AES, since by the type the new value |
| // propagates to the pin, the AES will already be active. |
| mmio_region_write32(gpio.base_addr, GPIO_MASKED_OUT_LOWER_REG_OFFSET, |
| gpio_on_reg_val); |
| |
| // Issue AES trigger commands. |
| mmio_region_write32(aes.base_addr, AES_TRIGGER_REG_OFFSET, aes_trigger_reg); |
| |
| // Wait for AES to complete encryption, as this is the fastest block. |
| while (!(mmio_region_read32(aes.base_addr, AES_STATUS_REG_OFFSET) & |
| (1u << AES_STATUS_OUTPUT_VALID_BIT))) |
| ; |
| |
| // Toggle GPIO pin to indicate we are out of max power consumption epoch. |
| mmio_region_write32(gpio.base_addr, GPIO_MASKED_OUT_LOWER_REG_OFFSET, |
| gpio_off_reg_val); |
| |
| LOG_INFO("Exited max power epoch."); |
| |
| // *************************************************************************** |
| // Check operation results. |
| // *************************************************************************** |
| // Check AES operation. |
| dif_aes_data_t aes_cipher_text; |
| CHECK_DIF_OK(dif_aes_read_output(&aes, &aes_cipher_text)); |
| CHECK_ARRAYS_EQ((uint8_t *)aes_cipher_text.data, kAesModesCipherTextCbc256, |
| ARRAYSIZE(aes_cipher_text.data)); |
| |
| // Check HMAC operations. |
| hmac_testutils_finish_and_check_polled(&hmac, &kHmacRefExpectedDigest); |
| |
| // Check KMAC operations. |
| uint32_t kmac_digest[kKmacDigestLength]; |
| complete_kmac_operations(kmac_digest); |
| CHECK(kKmacDigestLength == ARRAYSIZE(kKmacDigest)); |
| CHECK_ARRAYS_EQ(kmac_digest, kKmacDigest, ARRAYSIZE(kKmacDigest)); |
| |
| // Check OTBN operations. |
| hardened_bool_t result; |
| CHECK_STATUS_OK(rsa_3072_verify_finalize(&rsa3072_encoded_message, &result)); |
| CHECK(result == kHardenedBoolTrue); |
| |
| // Check UART transactions. |
| uint8_t received_uart_data[kUartFifoDepth]; |
| size_t num_uart_rx_bytes; |
| for (size_t i = 0; i < ARRAYSIZE(uart_handles); ++i) { |
| CHECK_DIF_OK( |
| dif_uart_rx_bytes_available(uart_handles[i], &num_uart_rx_bytes)); |
| // Note, we don't care if all bytes have been transmitted out of the UART by |
| // the time the fastest processing crypto block (i.e., the AES) has |
| // completed. Likely, we won't have transmitted all data since the UART is |
| // quite a bit slower. We just check that what was transmitted is correct. |
| memset((void *)received_uart_data, 0, kUartFifoDepth); |
| CHECK_DIF_OK(dif_uart_bytes_receive(uart_handles[i], num_uart_rx_bytes, |
| received_uart_data, NULL)); |
| CHECK_ARRAYS_EQ(received_uart_data, kUartMessage, num_uart_rx_bytes); |
| } |
| |
| OTTF_TASK_DELETE_SELF_OR_DIE; |
| } |
| |
| static void check_otp_csr_configs(void) { |
| dif_flash_ctrl_region_properties_t default_properties; |
| CHECK_DIF_OK(dif_flash_ctrl_get_default_region_properties( |
| &flash_ctrl, &default_properties)); |
| CHECK(default_properties.scramble_en == kMultiBitBool4False); |
| CHECK(default_properties.ecc_en == kMultiBitBool4False); |
| CHECK(default_properties.high_endurance_en == kMultiBitBool4False); |
| } |
| |
| bool test_main(void) { |
| peripheral_clock_period_ns = |
| udiv64_slow(1000000000, kClockFreqPeripheralHz, NULL); |
| // Note: DO NOT change this message string without updating the DV testbench. |
| LOG_INFO("Computed peripheral clock period."); |
| |
| // *************************************************************************** |
| // Initialize and configure all IPs. |
| // *************************************************************************** |
| init_peripheral_handles(); |
| configure_pinmux(); |
| // Clear GPIO pin 0 (max power indicator pin). |
| CHECK_DIF_OK( |
| dif_gpio_output_set_enabled(&gpio, /*pin=*/0, kDifToggleEnabled)); |
| configure_adc_ctrl_to_continuously_sample(); |
| configure_entropy_complex(); |
| // Note: configuration of OTBN must be done *before* configuration of the |
| // HMAC, as the cryptolib uses HMAC in SHA256 mode, which will cause HMAC |
| // computation errors later in this test. |
| configure_otbn(); |
| configure_aes(); |
| configure_hmac(); |
| configure_kmac(); |
| configure_uart(&uart_1); |
| configure_uart(&uart_2); |
| configure_uart(&uart_3); |
| configure_i2c(&i2c_0, kI2c0DeviceAddress0, kI2c0DeviceAddress1); |
| configure_i2c(&i2c_1, kI2c1DeviceAddress0, kI2c1DeviceAddress1); |
| configure_i2c(&i2c_2, kI2c2DeviceAddress0, kI2c2DeviceAddress1); |
| configure_spi_host(&spi_host_0, /*enable=*/true); |
| // We don't enable SPI host 1 just yet, as we want to pre-load its FIFO with |
| // data before enabling it at the last moment, to initiate max power draw. |
| configure_spi_host(&spi_host_1, /*enable=*/false); |
| spi_device_testutils_configure_passthrough(&spi_device, /*filters=*/0, |
| /*upload_write_commands=*/false); |
| configure_pattgen(); |
| configure_pwm(); |
| LOG_INFO("All IPs configured."); |
| |
| // *************************************************************************** |
| // Check OTP configurations propagated to CSRs. |
| // *************************************************************************** |
| check_otp_csr_configs(); |
| |
| // *************************************************************************** |
| // Kick off test tasks. |
| // *************************************************************************** |
| CHECK(ottf_task_create(crypto_data_load_task, "CryptoDataLoadTask", |
| kOttfFreeRtosMinStackSize, 1)); |
| CHECK(ottf_task_create(comms_data_load_task, "CommsDataLoadTask", |
| kOttfFreeRtosMinStackSize, 1)); |
| CHECK(ottf_task_create(max_power_task, "MaxPowerTask", |
| kOttfFreeRtosMinStackSize, 1)); |
| |
| // *************************************************************************** |
| // Yield control flow to the highest priority task in the run queue. Since |
| // the tasks created above all have a higher priority level than the current |
| // "test_main" task, and no tasks block, execution will not be returned to the |
| // current task until the above tasks have been deleted. |
| // *************************************************************************** |
| LOG_INFO("Yielding execution to another task."); |
| ottf_task_yield(); |
| |
| return true; |
| } |