diff --git a/hw/top_chip/dv/top_chip_sim_cfg.hjson b/hw/top_chip/dv/top_chip_sim_cfg.hjson index 1a712326d..8e82453ac 100644 --- a/hw/top_chip/dv/top_chip_sim_cfg.hjson +++ b/hw/top_chip/dv/top_chip_sim_cfg.hjson @@ -359,6 +359,20 @@ run_opts: ["+ChipMemDRAM_image_file={run_dir}/entropy_src_smoketest_cheri.vmem", "+ChipMemROM_image_file={run_dir}/bootrom_scrambled.vmem"] } + { + name: kmac_rom_app_test + uvm_test_seq: top_chip_dv_base_vseq + sw_images: ["kmac_smoketest_vanilla:5" "bootrom:5"] + run_opts: ["+ChipMemDRAM_image_file={run_dir}/kmac_smoketest_vanilla.vmem", + "+ChipMemROM_image_file={run_dir}/bootrom_scrambled.vmem"] + } + { + name: kmac_rom_app_test_cheri + uvm_test_seq: top_chip_dv_base_vseq + sw_images: ["kmac_smoketest_cheri:5" "bootrom:5"] + run_opts: ["+ChipMemDRAM_image_file={run_dir}/kmac_smoketest_cheri.vmem", + "+ChipMemROM_image_file={run_dir}/bootrom_scrambled.vmem"] + } ] // List of regressions. @@ -402,6 +416,8 @@ "pwrmgr_smoke_cheri", "entropy_src_smoke", "entropy_src_smoke_cheri", + "kmac_rom_app_test", + "kmac_rom_app_test_cheri", ] } { @@ -521,5 +537,12 @@ "entropy_src_smoke_cheri" ] } + { + name: kmac + tests: [ + "kmac_rom_app_test", + "kmac_rom_app_test_cheri" + ] + } ] } diff --git a/sw/device/lib/hal/rom_ctrl.c b/sw/device/lib/hal/rom_ctrl.c index eaa21f76e..a83e818c8 100644 --- a/sw/device/lib/hal/rom_ctrl.c +++ b/sw/device/lib/hal/rom_ctrl.c @@ -5,6 +5,7 @@ #include "hal/rom_ctrl.h" #include "hal/mmio.h" #include "hal/mocha.h" +#include #include /* Read a 32 bit data word from ROM memory */ @@ -12,3 +13,18 @@ uint32_t read_rom(rom_t rom, uint32_t rel_addr) { return DEV_READ(rom + rel_addr); } + +rom_ctrl_fatal_alert_cause rom_ctrl_fatal_alert_cause_read(rom_ctrl_t rom_ctrl) +{ + return VOLATILE_READ(rom_ctrl->fatal_alert_cause); +} + +uint32_t rom_ctrl_digest_read(rom_ctrl_t rom_ctrl, size_t index) +{ + return VOLATILE_READ(rom_ctrl->digest[index]); +} + +uint32_t rom_ctrl_exp_digest_read(rom_ctrl_t rom_ctrl, size_t index) +{ + return VOLATILE_READ(rom_ctrl->exp_digest[index]); +} diff --git a/sw/device/lib/hal/rom_ctrl.h b/sw/device/lib/hal/rom_ctrl.h index 6708b4182..64731189f 100644 --- a/sw/device/lib/hal/rom_ctrl.h +++ b/sw/device/lib/hal/rom_ctrl.h @@ -4,15 +4,23 @@ #pragma once +#include "autogen/rom_ctrl.h" #include "builtin.h" #include +#include #include - -#define ROM_CTRL_REG (0x0) -#define FATAL_ALERT_CAUSE (0x4) - -typedef void *rom_ctrl_t; typedef void *rom_t; uint32_t read_rom(rom_t rom, uint32_t rel_addr); + +/* Reasons a fatal alert was raised: a failure in the ROM checker, or a bus + * integrity error. The bits are sticky and cannot be cleared once set. */ +rom_ctrl_fatal_alert_cause rom_ctrl_fatal_alert_cause_read(rom_ctrl_t rom_ctrl); + +/* One word of the digest computed by KMAC over the boot ROM contents. */ +uint32_t rom_ctrl_digest_read(rom_ctrl_t rom_ctrl, size_t index); + +/* One word of the expected digest, stored in the top words of the boot ROM + * image. rom_ctrl compares this against the digest KMAC computed. */ +uint32_t rom_ctrl_exp_digest_read(rom_ctrl_t rom_ctrl, size_t index); diff --git a/sw/device/tests/CMakeLists.txt b/sw/device/tests/CMakeLists.txt index ba043919e..520af84d2 100644 --- a/sw/device/tests/CMakeLists.txt +++ b/sw/device/tests/CMakeLists.txt @@ -19,6 +19,7 @@ mocha_add_test(NAME gpio_reg_access_test SOURCES gpio/reg_access_test.c LIBRARIE mocha_add_test(NAME gpio_smoketest SOURCES gpio/smoketest.c LIBRARIES ${LIBS} SKIP_VERILATOR SKIP_FPGA) mocha_add_test(NAME i2c_temperature_sensor_test SOURCES i2c/temperature_sensor_test.c LIBRARIES ${LIBS}) mocha_add_test(NAME i2c_host_tx_rx_test SOURCES i2c/host_tx_rx_test.c LIBRARIES ${LIBS} SKIP_VERILATOR SKIP_FPGA) +mocha_add_test(NAME kmac_smoketest SOURCES kmac/smoketest.c LIBRARIES ${LIBS}) mocha_add_test(NAME mailbox_smoketest SOURCES mailbox/smoketest.c LIBRARIES ${LIBS}) mocha_add_test(NAME plic_smoketest SOURCES plic/smoketest.c LIBRARIES ${LIBS}) mocha_add_test(NAME pwrmgr_smoketest SOURCES pwrmgr/smoketest.c LIBRARIES ${LIBS}) diff --git a/sw/device/tests/kmac/smoketest.c b/sw/device/tests/kmac/smoketest.c new file mode 100644 index 000000000..e9b495f73 --- /dev/null +++ b/sw/device/tests/kmac/smoketest.c @@ -0,0 +1,69 @@ +// Copyright lowRISC contributors (COSMIC project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/* Connectivity smoke test for the KMAC block. + * + * Software cannot reach KMAC directly in Mocha: its register interface is tied + * off at the top level. Its only live connection is to rom_ctrl, which uses it + * to hash the boot ROM at power-on. + * + * The hash is produced twice. At build time scramble_image.py hashes the ROM + * image and stores the result in the top 8 words of that image. At power-on + * rom_ctrl reads the ROM back, sends everything below those 8 words to KMAC, + * and gets a freshly computed hash in return. rom_ctrl holds the stored copy in + * exp_digest and the computed one in digest, and only lets the CPU start if the + * two match. Mocha never bypasses that check, because lc_dft_en_i is hardwired + * off; upstream OpenTitan does bypass it in TEST and RMA. Running at all + * therefore already means the hashes agreed. + * + * That also limits what this test can find. If the hashes had disagreed, or if + * rom_ctrl had reported a fault, the CPU would never have started and this code + * would never run. The simulation would simply time out. So the checks below + * only ever see the case where everything already worked. + * + * What they can find is a fault in reading the registers back out. rom_ctrl + * compares the values it stored, so if storing them were broken the chip would + * not boot at all. But if only the read path is wrong, say the wrong address or + * the words in the wrong order, the chip boots normally while software sees the + * wrong values. Comparing the two registers here catches that. + */ + +#include "builtin.h" +#include "hal/mocha.h" +#include "hal/rom_ctrl.h" +#include +#include +#include + +static bool rom_digest_test(rom_ctrl_t rom_ctrl) +{ + uint32_t any_bit_set = 0; + uint32_t any_bit_clear = 0; + + for (size_t i = 0; i < ARRAY_LEN(rom_ctrl->digest); i++) { + uint32_t digest = rom_ctrl_digest_read(rom_ctrl, i); + + // Check that all the digests match the expected + if (digest != rom_ctrl_exp_digest_read(rom_ctrl, i)) { + return false; + } + + // Check if the digest was all 0s or all 1s + any_bit_set |= digest; + any_bit_clear |= ~digest; + } + + return any_bit_set != 0 && any_bit_clear != 0; +} + +bool test_main() +{ + rom_ctrl_t rom_ctrl = mocha_system_rom_ctrl(); + + if (rom_ctrl_fatal_alert_cause_read(rom_ctrl) != rom_ctrl_fatal_alert_cause_none) { + return false; + } + + return rom_digest_test(rom_ctrl); +}