Hi Tong, On [2023 Sep 21] Thu 23:50:10, Tong Ho wrote: > Signed-off-by: Tong Ho <tong...@amd.com> > --- > tests/qtest/meson.build | 2 +- > tests/qtest/xlnx-versal-trng-test.c | 490 ++++++++++++++++++++++++++++ > 2 files changed, 491 insertions(+), 1 deletion(-) > create mode 100644 tests/qtest/xlnx-versal-trng-test.c > > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > index 1fba07f4ed..215d20e8cf 100644 > --- a/tests/qtest/meson.build > +++ b/tests/qtest/meson.build > @@ -216,7 +216,7 @@ qtests_aarch64 = \ > (config_all.has_key('CONFIG_TCG') and > config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ > ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + > \ > (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', > 'fuzz-xlnx-dp-test'] : []) + \ > - (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test'] : > []) + \ > + (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', > 'xlnx-versal-trng-test'] : []) + \ > (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + > \ > (config_all.has_key('CONFIG_TCG') and > \ > config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : > []) + \ . . . > + > +static const char *trng_info(void) > +{ > + uint32_t sta = trng_status(); > + uint32_t ctl = trng_read(R_TRNG_CTRL); > + > + static char info[64]; > + > + snprintf(info, sizeof(info), "; status=0x%x, ctrl=0x%x", sta, ctl); > + return info; > +} > + > +static void trng_wait(uint32_t wait_mask, bool on, const char *act)
A suggestion is to remove the 'bool on' arg (it is only called with 'true' below). > +{ > + time_t tmo = time(NULL) + 2; /* at most 2 seconds */ > + uint32_t event_mask = 0; > + uint32_t clear_mask = 0; > + > + /* > + * Only selected bits are events in R_TRNG_STATUS, and > + * clear them needs to go through R_INT_CTRL. > + */ > + if (wait_mask & TRNG_STATUS_CERTF_MASK) { > + event_mask |= TRNG_STATUS_CERTF_MASK; > + clear_mask |= TRNG_INT_CTRL_CERTF_RST_MASK; > + } > + if (wait_mask & TRNG_STATUS_DTF_MASK) { > + event_mask |= TRNG_STATUS_DTF_MASK; > + clear_mask |= TRNG_INT_CTRL_DTF_RST_MASK; > + } > + if (wait_mask & TRNG_STATUS_DONE_MASK) { > + event_mask |= TRNG_STATUS_DONE_MASK; > + clear_mask |= TRNG_INT_CTRL_DONE_RST_MASK; > + } > + > + for (;;) { > + bool sta = !!(trng_status() & event_mask); > + > + if ((on ^ sta) == 0) { > + break; > + } > + > + if (time(NULL) >= tmo) { > + FAILED("%s: Timed out waiting for event 0x%x to be %d%s", > + act, event_mask, (int)on, trng_info()); > + } > + > + g_usleep(10000); > + } > + > + /* Remove event */ > + trng_bit_set(R_TRNG_INT_CTRL, clear_mask); > + > + if (!!(trng_read(R_TRNG_STATUS) & event_mask)) { > + FAILED("%s: Event 0x%0x stuck at 1 after clear: %s", > + act, event_mask, trng_info()); > + } > +} > + > +static void trng_wait_done(const char *act) > +{ > + trng_wait(TRNG_STATUS_DONE_MASK, true, act); > +} > + > +static void trng_wait_dtf(void) > +{ > + trng_wait(TRNG_STATUS_DTF_MASK, true, "DTF injection"); > +} > + > +static void trng_wait_certf(void) > +{ > + trng_wait(TRNG_STATUS_CERTF_MASK, true, "CERTF injection"); > +} > + > +static void trng_reset(void) > +{ > + trng_write(R_TRNG_RESET, TRNG_RESET_VAL_MASK); > + trng_write(R_TRNG_RESET, 0); > +} > + > +static void trng_load(unsigned r0, const uint32_t *b384) > +{ > + static const uint32_t zero[12] = { 0 }; > + unsigned k; > + > + if (!b384) { > + b384 = zero; > + } > + > + for (k = 0; k < 12; k++) { > + trng_write(r0 + 4 * k, b384[k]); > + } > +} > + > +static void trng_reseed(const uint32_t *seed) > +{ > + const char *act; > + uint32_t ctl; > + > + ctl = TRNG_CTRL_PRNGSTART_MASK | > + TRNG_CTRL_PRNGXS_MASK | > + TRNG_CTRL_TRSSEN_MASK; > + > + trng_ctrl_clr(ctl | TRNG_CTRL_PRNGMODE_MASK); > + > + if (seed) { > + trng_load(R_TRNG_EXT_SEED_0, seed); > + act = "Reseed PRNG"; > + ctl &= ~TRNG_CTRL_TRSSEN_MASK; > + } else { > + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); > + act = "Reseed TRNG"; > + ctl &= ~TRNG_CTRL_PRNGXS_MASK; > + } > + > + trng_ctrl_set(ctl); > + trng_wait_done(act); > + trng_ctrl_clr(TRNG_CTRL_PRNGSTART_MASK); > +} > + > +static void trng_generate(bool auto_enb) > +{ > + uint32_t ctl; > + > + ctl = TRNG_CTRL_PRNGSTART_MASK | TRNG_CTRL_SINGLEGENMODE_MASK; > + trng_ctrl_clr(ctl); > + > + if (auto_enb) { > + ctl &= ~TRNG_CTRL_SINGLEGENMODE_MASK; > + } > + > + trng_ctrl_set(ctl | TRNG_CTRL_PRNGMODE_MASK); > + > + trng_wait_done("Generate"); > + g_assert(trng_qcnt() != 7); > +} > + > +static size_t trng_collect(uint32_t *rnd, size_t cnt) > +{ > + size_t i; > + > + for (i = 0; i < cnt; i++) { > + if (trng_qcnt() == 0) { > + return i; > + } > + > + rnd[i] = trng_read(R_TRNG_CORE_OUTPUT); > + } > + > + return i; > +} > + > +static void trng_test_autogen(void) > +{ > + const size_t cnt = 512 / 32; > + uint32_t rng[cnt], prng[cnt]; > + size_t n; > + > + trng_reset(); > + > + /* PRNG run #1 */ > + trng_reseed(prng_seed); > + trng_generate(true); > + > + n = trng_collect(prng, cnt); > + if (n != cnt) { > + FAILED("PRNG_1 Auto-gen test failed: expected = %u, got = %u", > + (unsigned)cnt, (unsigned)n); > + } > + > + /* TRNG, should not match PRNG */ > + trng_reseed(NULL); > + trng_generate(true); > + > + n = trng_collect(rng, cnt); > + if (n != cnt) { > + FAILED("TRNG Auto-gen test failed: expected = %u, got = %u", > + (unsigned)cnt, (unsigned)n); > + } > + > + if (!memcmp(rng, prng, sizeof(rng))) { > + FAILED("TRNG test failed: matching PRNG"); > + } > + > + /* PRNG #2: should matches run #1 */ > + trng_reseed(prng_seed); > + trng_generate(true); > + > + n = trng_collect(rng, cnt); > + if (n != cnt) { > + FAILED("PRNG_2 Auto-gen test failed: expected = %u, got = %u", > + (unsigned)cnt, (unsigned)n); > + } > + > + if (memcmp(rng, prng, sizeof(rng))) { > + FAILED("PRNG_2 Auto-gen test failed: does not match PRNG_1"); > + } > +} > + > +static void trng_test_oneshot(void) > +{ > + const size_t cnt = 512 / 32; > + uint32_t rng[cnt]; > + size_t n; > + > + trng_reset(); > + > + /* PRNG run #1 */ > + trng_reseed(prng_seed); > + trng_generate(false); > + > + n = trng_collect(rng, cnt); > + if (n == cnt) { > + FAILED("PRNG_1 One-shot gen test failed"); > + } > + > + /* TRNG, should not match PRNG */ > + trng_reseed(NULL); > + trng_generate(false); > + > + n = trng_collect(rng, cnt); > + if (n == cnt) { > + FAILED("TRNG One-shot test failed"); > + } > +} > + > +static void trng_test_per_str(void) > +{ > + const size_t cnt = 512 / 32; > + uint32_t rng[cnt], prng[cnt]; > + size_t n; > + > + trng_reset(); > + > + /* #1: disabled */ > + trng_ctrl_set(TRNG_CTRL_PERSODISABLE_MASK); > + trng_reseed(prng_seed); > + trng_ctrl_clr(TRNG_CTRL_PERSODISABLE_MASK); > + > + trng_generate(true); > + n = trng_collect(prng, cnt); > + g_assert_cmpuint(n, ==, cnt); > + > + /* #2: zero string should match personalization disabled */ > + trng_load(R_TRNG_PER_STRNG_0, NULL); > + trng_reseed(prng_seed); > + > + trng_generate(true); > + n = trng_collect(rng, cnt); > + g_assert_cmpuint(n, ==, cnt); > + > + if (memcmp(rng, prng, sizeof(rng))) { > + FAILED("Failed: PER_DISABLE != PER_STRNG_ALL_ZERO"); > + } > + > + /* #3: non-zero string should not match personalization disabled */ > + trng_load(R_TRNG_PER_STRNG_0, pers_str); > + trng_reseed(prng_seed); > + > + trng_generate(true); > + n = trng_collect(rng, cnt); > + g_assert_cmpuint(n, ==, cnt); > + > + if (!memcmp(rng, prng, sizeof(rng))) { > + FAILED("Failed: PER_DISABLE == PER_STRNG_NON_ZERO"); > + } > +} > + > +static void trng_test_forced_prng(void) > +{ > + const char *prop = "forced-prng"; > + const uint64_t seed = 0xdeadbeefbad1bad0ULL; > + > + trng_reset(); We need to move above line below the variable initialization below. > + const size_t cnt = 512 / 32; > + uint32_t rng[cnt], prng[cnt]; > + size_t n; Looks good to me otherwise! Best regards, Francisco Iglesias > + > + trng_test_set_uint_prop(prop, seed); > + > + /* TRNG run #1 */ > + trng_reset(); > + trng_reseed(NULL); > + trng_generate(true); > + > + n = trng_collect(prng, cnt); > + g_assert_cmpuint(n, ==, cnt); > + > + /* TRNG run #2 should match run #1 */ > + trng_reset(); > + trng_reseed(NULL); > + trng_generate(true); > + > + n = trng_collect(rng, cnt); > + g_assert_cmpuint(n, ==, cnt); > + > + if (memcmp(rng, prng, sizeof(rng))) { > + FAILED("Forced-prng test failed: results do not match"); > + } > +} > + > +static void trng_test_fault_events(void) > +{ > + const char *prop = "fips-fault-events"; > + > + trng_reset(); > + > + /* Fault events only when TRSS is enabled */ > + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); > + trng_ctrl_set(TRNG_CTRL_TRSSEN_MASK); > + > + trng_test_set_uint_prop(prop, TRNG_STATUS_CERTF_MASK); > + trng_wait_certf(); > + > + trng_test_set_uint_prop(prop, TRNG_STATUS_DTF_MASK); > + trng_wait_dtf(); > + > + trng_reset(); > +} > + > +int main(int argc, char **argv) > +{ > + int rc; > + > + g_test_init(&argc, &argv, NULL); > + > + #define TRNG_TEST_ADD(n) \ > + qtest_add_func("/hw/misc/xlnx-versal-trng/" #n, trng_test_ ## n); > + TRNG_TEST_ADD(autogen); > + TRNG_TEST_ADD(oneshot); > + TRNG_TEST_ADD(per_str); > + TRNG_TEST_ADD(forced_prng); > + TRNG_TEST_ADD(fault_events); > + #undef TRNG_TEST_ADD > + > + trng_test_start(); > + rc = g_test_run(); > + trng_test_stop(); > + > + return rc; > +} > -- > 2.25.1 > >