xref: /qemu/hw/misc/xlnx-versal-trng.c (revision 14bfca0d)
192192358STong Ho /*
292192358STong Ho  * Non-crypto strength model of the True Random Number Generator
392192358STong Ho  * in the AMD/Xilinx Versal device family.
492192358STong Ho  *
592192358STong Ho  * Copyright (c) 2017-2020 Xilinx Inc.
692192358STong Ho  * Copyright (c) 2023 Advanced Micro Devices, Inc.
792192358STong Ho  *
892192358STong Ho  * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
992192358STong Ho  *
1092192358STong Ho  * Permission is hereby granted, free of charge, to any person obtaining a copy
1192192358STong Ho  * of this software and associated documentation files (the "Software"), to deal
1292192358STong Ho  * in the Software without restriction, including without limitation the rights
1392192358STong Ho  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1492192358STong Ho  * copies of the Software, and to permit persons to whom the Software is
1592192358STong Ho  * furnished to do so, subject to the following conditions:
1692192358STong Ho  *
1792192358STong Ho  * The above copyright notice and this permission notice shall be included in
1892192358STong Ho  * all copies or substantial portions of the Software.
1992192358STong Ho  *
2092192358STong Ho  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2192192358STong Ho  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2292192358STong Ho  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2392192358STong Ho  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2492192358STong Ho  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2592192358STong Ho  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2692192358STong Ho  * THE SOFTWARE.
2792192358STong Ho  */
2892192358STong Ho #include "qemu/osdep.h"
2992192358STong Ho #include "hw/misc/xlnx-versal-trng.h"
3092192358STong Ho 
3192192358STong Ho #include "qemu/bitops.h"
3292192358STong Ho #include "qemu/log.h"
3392192358STong Ho #include "qemu/error-report.h"
3492192358STong Ho #include "qemu/guest-random.h"
3592192358STong Ho #include "qemu/timer.h"
3692192358STong Ho #include "qapi/visitor.h"
3792192358STong Ho #include "migration/vmstate.h"
3892192358STong Ho #include "hw/qdev-properties.h"
3992192358STong Ho 
4092192358STong Ho #ifndef XLNX_VERSAL_TRNG_ERR_DEBUG
4192192358STong Ho #define XLNX_VERSAL_TRNG_ERR_DEBUG 0
4292192358STong Ho #endif
4392192358STong Ho 
4492192358STong Ho REG32(INT_CTRL, 0x0)
4592192358STong Ho     FIELD(INT_CTRL, CERTF_RST, 5, 1)
4692192358STong Ho     FIELD(INT_CTRL, DTF_RST, 4, 1)
4792192358STong Ho     FIELD(INT_CTRL, DONE_RST, 3, 1)
4892192358STong Ho     FIELD(INT_CTRL, CERTF_EN, 2, 1)
4992192358STong Ho     FIELD(INT_CTRL, DTF_EN, 1, 1)
5092192358STong Ho     FIELD(INT_CTRL, DONE_EN, 0, 1)
5192192358STong Ho REG32(STATUS, 0x4)
5292192358STong Ho     FIELD(STATUS, QCNT, 9, 3)
5392192358STong Ho     FIELD(STATUS, EAT, 4, 5)
5492192358STong Ho     FIELD(STATUS, CERTF, 3, 1)
5592192358STong Ho     FIELD(STATUS, DTF, 1, 1)
5692192358STong Ho     FIELD(STATUS, DONE, 0, 1)
5792192358STong Ho REG32(CTRL, 0x8)
5892192358STong Ho     FIELD(CTRL, PERSODISABLE, 10, 1)
5992192358STong Ho     FIELD(CTRL, SINGLEGENMODE, 9, 1)
6092192358STong Ho     FIELD(CTRL, EUMODE, 8, 1)
6192192358STong Ho     FIELD(CTRL, PRNGMODE, 7, 1)
6292192358STong Ho     FIELD(CTRL, TSTMODE, 6, 1)
6392192358STong Ho     FIELD(CTRL, PRNGSTART, 5, 1)
6492192358STong Ho     FIELD(CTRL, EATAU, 4, 1)
6592192358STong Ho     FIELD(CTRL, PRNGXS, 3, 1)
6692192358STong Ho     FIELD(CTRL, TRSSEN, 2, 1)
6792192358STong Ho     FIELD(CTRL, QERTUEN, 1, 1)
6892192358STong Ho     FIELD(CTRL, PRNGSRST, 0, 1)
6992192358STong Ho REG32(CTRL_2, 0xc)
7092192358STong Ho     FIELD(CTRL_2, REPCOUNTTESTCUTOFF, 8, 9)
7192192358STong Ho     FIELD(CTRL_2, RESERVED_7_5, 5, 3)
7292192358STong Ho     FIELD(CTRL_2, DIT, 0, 5)
7392192358STong Ho REG32(CTRL_3, 0x10)
7492192358STong Ho     FIELD(CTRL_3, ADAPTPROPTESTCUTOFF, 8, 10)
7592192358STong Ho     FIELD(CTRL_3, DLEN, 0, 8)
7692192358STong Ho REG32(CTRL_4, 0x14)
7792192358STong Ho     FIELD(CTRL_4, SINGLEBITRAW, 0, 1)
7892192358STong Ho REG32(EXT_SEED_0, 0x40)
7992192358STong Ho REG32(EXT_SEED_1, 0x44)
8092192358STong Ho REG32(EXT_SEED_2, 0x48)
8192192358STong Ho REG32(EXT_SEED_3, 0x4c)
8292192358STong Ho REG32(EXT_SEED_4, 0x50)
8392192358STong Ho REG32(EXT_SEED_5, 0x54)
8492192358STong Ho REG32(EXT_SEED_6, 0x58)
8592192358STong Ho REG32(EXT_SEED_7, 0x5c)
8692192358STong Ho REG32(EXT_SEED_8, 0x60)
8792192358STong Ho REG32(EXT_SEED_9, 0x64)
8892192358STong Ho REG32(EXT_SEED_10, 0x68)
8992192358STong Ho REG32(EXT_SEED_11, 0x6c)
9092192358STong Ho REG32(PER_STRNG_0, 0x80)
9192192358STong Ho REG32(PER_STRNG_1, 0x84)
9292192358STong Ho REG32(PER_STRNG_2, 0x88)
9392192358STong Ho REG32(PER_STRNG_3, 0x8c)
9492192358STong Ho REG32(PER_STRNG_4, 0x90)
9592192358STong Ho REG32(PER_STRNG_5, 0x94)
9692192358STong Ho REG32(PER_STRNG_6, 0x98)
9792192358STong Ho REG32(PER_STRNG_7, 0x9c)
9892192358STong Ho REG32(PER_STRNG_8, 0xa0)
9992192358STong Ho REG32(PER_STRNG_9, 0xa4)
10092192358STong Ho REG32(PER_STRNG_10, 0xa8)
10192192358STong Ho REG32(PER_STRNG_11, 0xac)
10292192358STong Ho REG32(CORE_OUTPUT, 0xc0)
10392192358STong Ho REG32(RESET, 0xd0)
10492192358STong Ho     FIELD(RESET, VAL, 0, 1)
10592192358STong Ho REG32(OSC_EN, 0xd4)
10692192358STong Ho     FIELD(OSC_EN, VAL, 0, 1)
10792192358STong Ho REG32(TRNG_ISR, 0xe0)
10892192358STong Ho     FIELD(TRNG_ISR, SLVERR, 1, 1)
10992192358STong Ho     FIELD(TRNG_ISR, CORE_INT, 0, 1)
11092192358STong Ho REG32(TRNG_IMR, 0xe4)
11192192358STong Ho     FIELD(TRNG_IMR, SLVERR, 1, 1)
11292192358STong Ho     FIELD(TRNG_IMR, CORE_INT, 0, 1)
11392192358STong Ho REG32(TRNG_IER, 0xe8)
11492192358STong Ho     FIELD(TRNG_IER, SLVERR, 1, 1)
11592192358STong Ho     FIELD(TRNG_IER, CORE_INT, 0, 1)
11692192358STong Ho REG32(TRNG_IDR, 0xec)
11792192358STong Ho     FIELD(TRNG_IDR, SLVERR, 1, 1)
11892192358STong Ho     FIELD(TRNG_IDR, CORE_INT, 0, 1)
11992192358STong Ho REG32(SLV_ERR_CTRL, 0xf0)
12092192358STong Ho     FIELD(SLV_ERR_CTRL, ENABLE, 0, 1)
12192192358STong Ho 
12292192358STong Ho #define R_MAX (R_SLV_ERR_CTRL + 1)
12392192358STong Ho 
12492192358STong Ho QEMU_BUILD_BUG_ON(R_MAX * 4 != sizeof_field(XlnxVersalTRng, regs));
12592192358STong Ho 
12692192358STong Ho #define TRNG_GUEST_ERROR(D, FMT, ...) \
12792192358STong Ho     do {                                                               \
12892192358STong Ho         g_autofree char *p = object_get_canonical_path(OBJECT(D));     \
12992192358STong Ho         qemu_log_mask(LOG_GUEST_ERROR, "%s: " FMT, p, ## __VA_ARGS__); \
13092192358STong Ho     } while (0)
13192192358STong Ho 
13292192358STong Ho #define TRNG_WARN(D, FMT, ...) \
13392192358STong Ho     do {                                                               \
13492192358STong Ho         g_autofree char *p = object_get_canonical_path(OBJECT(D));     \
13592192358STong Ho         warn_report("%s: " FMT, p, ## __VA_ARGS__);                    \
13692192358STong Ho     } while (0)
13792192358STong Ho 
trng_older_than_v2(XlnxVersalTRng * s)13892192358STong Ho static bool trng_older_than_v2(XlnxVersalTRng *s)
13992192358STong Ho {
14092192358STong Ho     return s->hw_version < 0x0200;
14192192358STong Ho }
14292192358STong Ho 
trng_in_reset(XlnxVersalTRng * s)14392192358STong Ho static bool trng_in_reset(XlnxVersalTRng *s)
14492192358STong Ho {
14592192358STong Ho     if (ARRAY_FIELD_EX32(s->regs, RESET, VAL)) {
14692192358STong Ho         return true;
14792192358STong Ho     }
14892192358STong Ho     if (ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSRST)) {
14992192358STong Ho         return true;
15092192358STong Ho     }
15192192358STong Ho 
15292192358STong Ho     return false;
15392192358STong Ho }
15492192358STong Ho 
trng_test_enabled(XlnxVersalTRng * s)15592192358STong Ho static bool trng_test_enabled(XlnxVersalTRng *s)
15692192358STong Ho {
15792192358STong Ho     return ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE);
15892192358STong Ho }
15992192358STong Ho 
trng_trss_enabled(XlnxVersalTRng * s)16092192358STong Ho static bool trng_trss_enabled(XlnxVersalTRng *s)
16192192358STong Ho {
16292192358STong Ho     if (trng_in_reset(s)) {
16392192358STong Ho         return false;
16492192358STong Ho     }
16592192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, CTRL, TRSSEN)) {
16692192358STong Ho         return false;
16792192358STong Ho     }
16892192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, OSC_EN, VAL)) {
16992192358STong Ho         return false;
17092192358STong Ho     }
17192192358STong Ho 
17292192358STong Ho     return true;
17392192358STong Ho }
17492192358STong Ho 
trng_seed_128(uint32_t * seed,uint64_t h00,uint64_t h64)17592192358STong Ho static void trng_seed_128(uint32_t *seed, uint64_t h00, uint64_t h64)
17692192358STong Ho {
17792192358STong Ho     seed[0] = extract64(h00, 0, 32);
17892192358STong Ho     seed[1] = extract64(h00, 32, 32);
17992192358STong Ho     seed[2] = extract64(h64, 0, 32);
18092192358STong Ho     seed[3] = extract64(h64, 32, 32);
18192192358STong Ho }
18292192358STong Ho 
trng_reseed(XlnxVersalTRng * s)18392192358STong Ho static void trng_reseed(XlnxVersalTRng *s)
18492192358STong Ho {
18592192358STong Ho     bool ext_seed = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGXS);
18692192358STong Ho     bool pers_disabled = ARRAY_FIELD_EX32(s->regs, CTRL, PERSODISABLE);
18792192358STong Ho 
18892192358STong Ho     enum {
18992192358STong Ho         U384_U8 = 384 / 8,
19092192358STong Ho         U384_U32 = 384 / 32,
19192192358STong Ho     };
19292192358STong Ho 
19392192358STong Ho     /*
19492192358STong Ho      * Maximum seed length is len(personalized string) + len(ext seed).
19592192358STong Ho      *
19692192358STong Ho      * g_rand_set_seed_array() takes array of uint32 in host endian.
19792192358STong Ho      */
19892192358STong Ho     guint32 gs[U384_U32 * 2], *seed = &gs[U384_U32];
19992192358STong Ho 
20092192358STong Ho     /*
20192192358STong Ho      * A disabled personalized string is the same as
20292192358STong Ho      * a string with all zeros.
20392192358STong Ho      *
20492192358STong Ho      * The device's hardware spec defines 3 modes (all selectable
20592192358STong Ho      * by guest at will and at anytime):
20692192358STong Ho      * 1) External seeding
20792192358STong Ho      *    This is a PRNG mode, in which the produced sequence shall
20892192358STong Ho      *    be reproducible if reseeded by the same 384-bit seed, as
20992192358STong Ho      *    supplied by guest software.
21092192358STong Ho      * 2) Test seeding
21192192358STong Ho      *    This is a PRNG mode, in which the produced sequence shall
21292192358STong Ho      *    be reproducible if reseeded by a 128-bit test seed, as
21392192358STong Ho      *    supplied by guest software.
21492192358STong Ho      * 3) Truly-random seeding
21592192358STong Ho      *    This is the TRNG mode, in which the produced sequence is
21692192358STong Ho      *    periodically reseeded by a crypto-strength entropy source.
21792192358STong Ho      *
21892192358STong Ho      * To assist debugging of certain classes of software defects,
21992192358STong Ho      * this QEMU model implements a 4th mode,
22092192358STong Ho      * 4) Forced PRNG
22192192358STong Ho      *    When in this mode, a reproducible sequence is generated
22292192358STong Ho      *    if software has selected the TRNG mode (mode 2).
22392192358STong Ho      *
22492192358STong Ho      *    This emulation-only mode can only be selected by setting
22592192358STong Ho      *    the uint64 property 'forced-prng' to a non-zero value.
22692192358STong Ho      *    Guest software cannot select this mode.
22792192358STong Ho      */
22892192358STong Ho     memset(gs, 0, sizeof(gs));
22992192358STong Ho 
23092192358STong Ho     if (!pers_disabled) {
23192192358STong Ho         memcpy(gs, &s->regs[R_PER_STRNG_0], U384_U8);
23292192358STong Ho     }
23392192358STong Ho 
23492192358STong Ho     if (ext_seed) {
23592192358STong Ho         memcpy(seed, &s->regs[R_EXT_SEED_0], U384_U8);
23692192358STong Ho     } else if (trng_test_enabled(s)) {
23792192358STong Ho         trng_seed_128(seed, s->tst_seed[0], s->tst_seed[1]);
23892192358STong Ho     } else if (s->forced_prng_seed) {
23992192358STong Ho         s->forced_prng_count++;
24092192358STong Ho         trng_seed_128(seed, s->forced_prng_count, s->forced_prng_seed);
24192192358STong Ho     } else {
24292192358STong Ho         qemu_guest_getrandom_nofail(seed, U384_U8);
24392192358STong Ho     }
24492192358STong Ho 
24592192358STong Ho     g_rand_set_seed_array(s->prng, gs, ARRAY_SIZE(gs));
24692192358STong Ho 
24792192358STong Ho     s->rand_count = 0;
24892192358STong Ho     s->rand_reseed = 1ULL << 48;
24992192358STong Ho }
25092192358STong Ho 
trng_regen(XlnxVersalTRng * s)25192192358STong Ho static void trng_regen(XlnxVersalTRng *s)
25292192358STong Ho {
25392192358STong Ho     if (s->rand_reseed == 0) {
25492192358STong Ho         TRNG_GUEST_ERROR(s, "Too many generations without a reseed");
25592192358STong Ho         trng_reseed(s);
25692192358STong Ho     }
25792192358STong Ho     s->rand_reseed--;
25892192358STong Ho 
25992192358STong Ho     /*
26092192358STong Ho      * In real hardware, each regen creates 256 bits, but QCNT
26192192358STong Ho      * reports a max of 4.
26292192358STong Ho      */
26392192358STong Ho     ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4);
26492192358STong Ho     s->rand_count = 256 / 32;
26592192358STong Ho }
26692192358STong Ho 
trng_rdout(XlnxVersalTRng * s)26792192358STong Ho static uint32_t trng_rdout(XlnxVersalTRng *s)
26892192358STong Ho {
26992192358STong Ho     assert(s->rand_count);
27092192358STong Ho 
27192192358STong Ho     s->rand_count--;
27292192358STong Ho     if (s->rand_count < 4) {
27392192358STong Ho         ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, s->rand_count);
27492192358STong Ho     }
27592192358STong Ho 
27692192358STong Ho     return g_rand_int(s->prng);
27792192358STong Ho }
27892192358STong Ho 
trng_irq_update(XlnxVersalTRng * s)27992192358STong Ho static void trng_irq_update(XlnxVersalTRng *s)
28092192358STong Ho {
28192192358STong Ho     bool pending = s->regs[R_TRNG_ISR] & ~s->regs[R_TRNG_IMR];
28292192358STong Ho     qemu_set_irq(s->irq, pending);
28392192358STong Ho }
28492192358STong Ho 
trng_isr_postw(RegisterInfo * reg,uint64_t val64)28592192358STong Ho static void trng_isr_postw(RegisterInfo *reg, uint64_t val64)
28692192358STong Ho {
28792192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
28892192358STong Ho     trng_irq_update(s);
28992192358STong Ho }
29092192358STong Ho 
trng_ier_prew(RegisterInfo * reg,uint64_t val64)29192192358STong Ho static uint64_t trng_ier_prew(RegisterInfo *reg, uint64_t val64)
29292192358STong Ho {
29392192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
29492192358STong Ho     uint32_t val = val64;
29592192358STong Ho 
29692192358STong Ho     s->regs[R_TRNG_IMR] &= ~val;
29792192358STong Ho     trng_irq_update(s);
29892192358STong Ho     return 0;
29992192358STong Ho }
30092192358STong Ho 
trng_idr_prew(RegisterInfo * reg,uint64_t val64)30192192358STong Ho static uint64_t trng_idr_prew(RegisterInfo *reg, uint64_t val64)
30292192358STong Ho {
30392192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
30492192358STong Ho     uint32_t val = val64;
30592192358STong Ho 
30692192358STong Ho     s->regs[R_TRNG_IMR] |= val;
30792192358STong Ho     trng_irq_update(s);
30892192358STong Ho     return 0;
30992192358STong Ho }
31092192358STong Ho 
trng_core_int_update(XlnxVersalTRng * s)31192192358STong Ho static void trng_core_int_update(XlnxVersalTRng *s)
31292192358STong Ho {
31392192358STong Ho     bool pending = false;
31492192358STong Ho     uint32_t st = s->regs[R_STATUS];
31592192358STong Ho     uint32_t en = s->regs[R_INT_CTRL];
31692192358STong Ho 
31792192358STong Ho     if (FIELD_EX32(st, STATUS, CERTF) && FIELD_EX32(en, INT_CTRL, CERTF_EN)) {
31892192358STong Ho         pending = true;
31992192358STong Ho     }
32092192358STong Ho 
32192192358STong Ho     if (FIELD_EX32(st, STATUS, DTF) && FIELD_EX32(en, INT_CTRL, DTF_EN)) {
32292192358STong Ho         pending = true;
32392192358STong Ho     }
32492192358STong Ho 
32592192358STong Ho     if (FIELD_EX32(st, STATUS, DONE) && FIELD_EX32(en, INT_CTRL, DONE_EN)) {
32692192358STong Ho         pending = true;
32792192358STong Ho     }
32892192358STong Ho 
32992192358STong Ho     ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, pending);
33092192358STong Ho     trng_irq_update(s);
33192192358STong Ho }
33292192358STong Ho 
trng_int_ctrl_postw(RegisterInfo * reg,uint64_t val64)33392192358STong Ho static void trng_int_ctrl_postw(RegisterInfo *reg, uint64_t val64)
33492192358STong Ho {
33592192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
33692192358STong Ho     uint32_t v32 = val64;
33792192358STong Ho     uint32_t clr_mask = 0;
33892192358STong Ho 
33992192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, CERTF_RST)) {
34092192358STong Ho         clr_mask |= R_STATUS_CERTF_MASK;
34192192358STong Ho     }
34292192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, DTF_RST)) {
34392192358STong Ho         clr_mask |= R_STATUS_DTF_MASK;
34492192358STong Ho     }
34592192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, DONE_RST)) {
34692192358STong Ho         clr_mask |= R_STATUS_DONE_MASK;
34792192358STong Ho     }
34892192358STong Ho 
34992192358STong Ho     s->regs[R_STATUS] &= ~clr_mask;
35092192358STong Ho     trng_core_int_update(s);
35192192358STong Ho }
35292192358STong Ho 
trng_done(XlnxVersalTRng * s)35392192358STong Ho static void trng_done(XlnxVersalTRng *s)
35492192358STong Ho {
35592192358STong Ho     ARRAY_FIELD_DP32(s->regs, STATUS, DONE, true);
35692192358STong Ho     trng_core_int_update(s);
35792192358STong Ho }
35892192358STong Ho 
trng_fault_event_set(XlnxVersalTRng * s,uint32_t events)35992192358STong Ho static void trng_fault_event_set(XlnxVersalTRng *s, uint32_t events)
36092192358STong Ho {
36192192358STong Ho     bool pending = false;
36292192358STong Ho 
36392192358STong Ho     /* Disabled TRSS cannot generate any fault event */
36492192358STong Ho     if (!trng_trss_enabled(s)) {
36592192358STong Ho         return;
36692192358STong Ho     }
36792192358STong Ho 
36892192358STong Ho     if (FIELD_EX32(events, STATUS, CERTF)) {
36992192358STong Ho         /* In older version, ERTU must be enabled explicitly to get CERTF */
37092192358STong Ho         if (trng_older_than_v2(s) &&
37192192358STong Ho             !ARRAY_FIELD_EX32(s->regs, CTRL, QERTUEN)) {
37292192358STong Ho             TRNG_WARN(s, "CERTF injection ignored: ERTU disabled");
37392192358STong Ho         } else {
37492192358STong Ho             ARRAY_FIELD_DP32(s->regs, STATUS, CERTF, true);
37592192358STong Ho             pending = true;
37692192358STong Ho         }
37792192358STong Ho     }
37892192358STong Ho 
37992192358STong Ho     if (FIELD_EX32(events, STATUS, DTF)) {
38092192358STong Ho         ARRAY_FIELD_DP32(s->regs, STATUS, DTF, true);
38192192358STong Ho         pending = true;
38292192358STong Ho     }
38392192358STong Ho 
38492192358STong Ho     if (pending) {
38592192358STong Ho         trng_core_int_update(s);
38692192358STong Ho     }
38792192358STong Ho }
38892192358STong Ho 
trng_soft_reset(XlnxVersalTRng * s)38992192358STong Ho static void trng_soft_reset(XlnxVersalTRng *s)
39092192358STong Ho {
39192192358STong Ho     s->rand_count = 0;
39292192358STong Ho     s->regs[R_STATUS] = 0;
39392192358STong Ho 
39492192358STong Ho     ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, 0);
39592192358STong Ho }
39692192358STong Ho 
trng_ctrl_postw(RegisterInfo * reg,uint64_t val64)39792192358STong Ho static void trng_ctrl_postw(RegisterInfo *reg, uint64_t val64)
39892192358STong Ho {
39992192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
40092192358STong Ho 
40192192358STong Ho     if (trng_in_reset(s)) {
40292192358STong Ho         return;
40392192358STong Ho     }
40492192358STong Ho 
40592192358STong Ho     if (FIELD_EX32(val64, CTRL, PRNGSRST)) {
40692192358STong Ho         trng_soft_reset(s);
40792192358STong Ho         trng_irq_update(s);
40892192358STong Ho         return;
40992192358STong Ho     }
41092192358STong Ho 
41192192358STong Ho     if (!FIELD_EX32(val64, CTRL, PRNGSTART)) {
41292192358STong Ho         return;
41392192358STong Ho     }
41492192358STong Ho 
41592192358STong Ho     if (FIELD_EX32(val64, CTRL, PRNGMODE)) {
41692192358STong Ho         trng_regen(s);
41792192358STong Ho     } else {
41892192358STong Ho         trng_reseed(s);
41992192358STong Ho     }
42092192358STong Ho 
42192192358STong Ho     trng_done(s);
42292192358STong Ho }
42392192358STong Ho 
trng_ctrl4_postw(RegisterInfo * reg,uint64_t val64)42492192358STong Ho static void trng_ctrl4_postw(RegisterInfo *reg, uint64_t val64)
42592192358STong Ho {
42692192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
42792192358STong Ho 
42892192358STong Ho     /* Only applies to test mode with TRSS enabled */
42992192358STong Ho     if (!trng_test_enabled(s) || !trng_trss_enabled(s)) {
43092192358STong Ho         return;
43192192358STong Ho     }
43292192358STong Ho 
43392192358STong Ho     /* Shift in a single bit.  */
43492192358STong Ho     s->tst_seed[1] <<= 1;
43592192358STong Ho     s->tst_seed[1] |= s->tst_seed[0] >> 63;
43692192358STong Ho     s->tst_seed[0] <<= 1;
43792192358STong Ho     s->tst_seed[0] |= val64 & 1;
43892192358STong Ho 
43992192358STong Ho     trng_reseed(s);
44092192358STong Ho     trng_regen(s);
44192192358STong Ho }
44292192358STong Ho 
trng_core_out_postr(RegisterInfo * reg,uint64_t val)44392192358STong Ho static uint64_t trng_core_out_postr(RegisterInfo *reg, uint64_t val)
44492192358STong Ho {
44592192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
44692192358STong Ho     bool oneshot = ARRAY_FIELD_EX32(s->regs, CTRL, SINGLEGENMODE);
44792192358STong Ho     bool start = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSTART);
44892192358STong Ho     uint32_t r = 0xbad;
44992192358STong Ho 
45092192358STong Ho     if (trng_in_reset(s)) {
45192192358STong Ho         TRNG_GUEST_ERROR(s, "Reading random number while in reset!");
45292192358STong Ho         return r;
45392192358STong Ho     }
45492192358STong Ho 
45592192358STong Ho     if (s->rand_count == 0) {
45692192358STong Ho         TRNG_GUEST_ERROR(s, "Reading random number when unavailable!");
45792192358STong Ho         return r;
45892192358STong Ho     }
45992192358STong Ho 
46092192358STong Ho     r = trng_rdout(s);
46192192358STong Ho 
46292192358STong Ho     /* Automatic mode regenerates when half the output reg is empty.  */
46392192358STong Ho     if (!oneshot && start && s->rand_count <= 3) {
46492192358STong Ho         trng_regen(s);
46592192358STong Ho     }
46692192358STong Ho 
46792192358STong Ho     return r;
46892192358STong Ho }
46992192358STong Ho 
trng_reset(XlnxVersalTRng * s)47092192358STong Ho static void trng_reset(XlnxVersalTRng *s)
47192192358STong Ho {
47292192358STong Ho     unsigned int i;
47392192358STong Ho 
47492192358STong Ho     s->forced_prng_count = 0;
47592192358STong Ho 
47692192358STong Ho     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
47792192358STong Ho         register_reset(&s->regs_info[i]);
47892192358STong Ho     }
47992192358STong Ho     trng_soft_reset(s);
48092192358STong Ho     trng_irq_update(s);
48192192358STong Ho }
48292192358STong Ho 
trng_reset_prew(RegisterInfo * reg,uint64_t val64)48392192358STong Ho static uint64_t trng_reset_prew(RegisterInfo *reg, uint64_t val64)
48492192358STong Ho {
48592192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
48692192358STong Ho 
48792192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, RESET, VAL) &&
48892192358STong Ho         FIELD_EX32(val64, RESET, VAL)) {
48992192358STong Ho         trng_reset(s);
49092192358STong Ho     }
49192192358STong Ho 
49292192358STong Ho     return val64;
49392192358STong Ho }
49492192358STong Ho 
trng_register_read(void * opaque,hwaddr addr,unsigned size)49592192358STong Ho static uint64_t trng_register_read(void *opaque, hwaddr addr, unsigned size)
49692192358STong Ho {
49792192358STong Ho     /*
49892192358STong Ho      * Guest provided seed and personalized strings cannot be
49992192358STong Ho      * read back, and read attempts return value of A_STATUS.
50092192358STong Ho      */
50192192358STong Ho     switch (addr) {
50292192358STong Ho     case A_EXT_SEED_0 ... A_PER_STRNG_11:
50392192358STong Ho         addr = A_STATUS;
50492192358STong Ho         break;
50592192358STong Ho     }
50692192358STong Ho 
50792192358STong Ho     return register_read_memory(opaque, addr, size);
50892192358STong Ho }
50992192358STong Ho 
trng_register_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)51092192358STong Ho static void trng_register_write(void *opaque, hwaddr addr,
51192192358STong Ho                                 uint64_t value, unsigned size)
51292192358STong Ho {
51392192358STong Ho     RegisterInfoArray *reg_array = opaque;
51492192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg_array->r[0]->opaque);
51592192358STong Ho 
51692192358STong Ho     if (trng_older_than_v2(s)) {
51792192358STong Ho         switch (addr) {
51892192358STong Ho         case A_CTRL:
51992192358STong Ho             value = FIELD_DP64(value, CTRL, PERSODISABLE, 0);
52092192358STong Ho             value = FIELD_DP64(value, CTRL, SINGLEGENMODE, 0);
52192192358STong Ho             break;
52292192358STong Ho         case A_CTRL_2:
52392192358STong Ho         case A_CTRL_3:
52492192358STong Ho         case A_CTRL_4:
52592192358STong Ho             return;
52692192358STong Ho         }
52792192358STong Ho     } else {
52892192358STong Ho         switch (addr) {
52992192358STong Ho         case A_CTRL:
53092192358STong Ho             value = FIELD_DP64(value, CTRL, EATAU, 0);
53192192358STong Ho             value = FIELD_DP64(value, CTRL, QERTUEN, 0);
53292192358STong Ho             break;
53392192358STong Ho         }
53492192358STong Ho     }
53592192358STong Ho 
53692192358STong Ho     register_write_memory(opaque, addr, value, size);
53792192358STong Ho }
53892192358STong Ho 
53992192358STong Ho static RegisterAccessInfo trng_regs_info[] = {
54092192358STong Ho     {   .name = "INT_CTRL",  .addr = A_INT_CTRL,
54192192358STong Ho         .post_write = trng_int_ctrl_postw,
54292192358STong Ho     },{ .name = "STATUS",  .addr = A_STATUS,
54392192358STong Ho         .ro = 0xfff,
54492192358STong Ho     },{ .name = "CTRL",  .addr = A_CTRL,
54592192358STong Ho         .post_write = trng_ctrl_postw,
54692192358STong Ho     },{ .name = "CTRL_2",  .addr = A_CTRL_2,
54792192358STong Ho         .reset = 0x210c,
54892192358STong Ho     },{ .name = "CTRL_3",  .addr = A_CTRL_3,
54992192358STong Ho         .reset = 0x26f09,
55092192358STong Ho     },{ .name = "CTRL_4",  .addr = A_CTRL_4,
55192192358STong Ho         .post_write = trng_ctrl4_postw,
55292192358STong Ho     },{ .name = "EXT_SEED_0",  .addr = A_EXT_SEED_0,
55392192358STong Ho     },{ .name = "EXT_SEED_1",  .addr = A_EXT_SEED_1,
55492192358STong Ho     },{ .name = "EXT_SEED_2",  .addr = A_EXT_SEED_2,
55592192358STong Ho     },{ .name = "EXT_SEED_3",  .addr = A_EXT_SEED_3,
55692192358STong Ho     },{ .name = "EXT_SEED_4",  .addr = A_EXT_SEED_4,
55792192358STong Ho     },{ .name = "EXT_SEED_5",  .addr = A_EXT_SEED_5,
55892192358STong Ho     },{ .name = "EXT_SEED_6",  .addr = A_EXT_SEED_6,
55992192358STong Ho     },{ .name = "EXT_SEED_7",  .addr = A_EXT_SEED_7,
56092192358STong Ho     },{ .name = "EXT_SEED_8",  .addr = A_EXT_SEED_8,
56192192358STong Ho     },{ .name = "EXT_SEED_9",  .addr = A_EXT_SEED_9,
56292192358STong Ho     },{ .name = "EXT_SEED_10",  .addr = A_EXT_SEED_10,
56392192358STong Ho     },{ .name = "EXT_SEED_11",  .addr = A_EXT_SEED_11,
56492192358STong Ho     },{ .name = "PER_STRNG_0",  .addr = A_PER_STRNG_0,
56592192358STong Ho     },{ .name = "PER_STRNG_1",  .addr = A_PER_STRNG_1,
56692192358STong Ho     },{ .name = "PER_STRNG_2",  .addr = A_PER_STRNG_2,
56792192358STong Ho     },{ .name = "PER_STRNG_3",  .addr = A_PER_STRNG_3,
56892192358STong Ho     },{ .name = "PER_STRNG_4",  .addr = A_PER_STRNG_4,
56992192358STong Ho     },{ .name = "PER_STRNG_5",  .addr = A_PER_STRNG_5,
57092192358STong Ho     },{ .name = "PER_STRNG_6",  .addr = A_PER_STRNG_6,
57192192358STong Ho     },{ .name = "PER_STRNG_7",  .addr = A_PER_STRNG_7,
57292192358STong Ho     },{ .name = "PER_STRNG_8",  .addr = A_PER_STRNG_8,
57392192358STong Ho     },{ .name = "PER_STRNG_9",  .addr = A_PER_STRNG_9,
57492192358STong Ho     },{ .name = "PER_STRNG_10",  .addr = A_PER_STRNG_10,
57592192358STong Ho     },{ .name = "PER_STRNG_11",  .addr = A_PER_STRNG_11,
57692192358STong Ho     },{ .name = "CORE_OUTPUT",  .addr = A_CORE_OUTPUT,
57792192358STong Ho         .ro = 0xffffffff,
57892192358STong Ho         .post_read = trng_core_out_postr,
57992192358STong Ho     },{ .name = "RESET",  .addr = A_RESET,
58092192358STong Ho         .reset = 0x1,
58192192358STong Ho         .pre_write = trng_reset_prew,
58292192358STong Ho     },{ .name = "OSC_EN",  .addr = A_OSC_EN,
58392192358STong Ho     },{ .name = "TRNG_ISR",  .addr = A_TRNG_ISR,
58492192358STong Ho         .w1c = 0x3,
58592192358STong Ho         .post_write = trng_isr_postw,
58692192358STong Ho     },{ .name = "TRNG_IMR",  .addr = A_TRNG_IMR,
58792192358STong Ho         .reset = 0x3,
58892192358STong Ho         .ro = 0x3,
58992192358STong Ho     },{ .name = "TRNG_IER",  .addr = A_TRNG_IER,
59092192358STong Ho         .pre_write = trng_ier_prew,
59192192358STong Ho     },{ .name = "TRNG_IDR",  .addr = A_TRNG_IDR,
59292192358STong Ho         .pre_write = trng_idr_prew,
59392192358STong Ho     },{ .name = "SLV_ERR_CTRL",  .addr = A_SLV_ERR_CTRL,
59492192358STong Ho     }
59592192358STong Ho };
59692192358STong Ho 
59792192358STong Ho static const MemoryRegionOps trng_ops = {
59892192358STong Ho     .read = trng_register_read,
59992192358STong Ho     .write = trng_register_write,
60092192358STong Ho     .endianness = DEVICE_LITTLE_ENDIAN,
60192192358STong Ho     .valid = {
60292192358STong Ho         .min_access_size = 4,
60392192358STong Ho         .max_access_size = 4,
60492192358STong Ho     },
60592192358STong Ho };
60692192358STong Ho 
trng_init(Object * obj)60792192358STong Ho static void trng_init(Object *obj)
60892192358STong Ho {
60992192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj);
61092192358STong Ho     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
61192192358STong Ho 
612*14bfca0dSPeter Maydell     s->reg_array =
61392192358STong Ho         register_init_block32(DEVICE(obj), trng_regs_info,
61492192358STong Ho                               ARRAY_SIZE(trng_regs_info),
61592192358STong Ho                               s->regs_info, s->regs,
61692192358STong Ho                               &trng_ops,
61792192358STong Ho                               XLNX_VERSAL_TRNG_ERR_DEBUG,
61892192358STong Ho                               R_MAX * 4);
61992192358STong Ho 
620*14bfca0dSPeter Maydell     sysbus_init_mmio(sbd, &s->reg_array->mem);
62192192358STong Ho     sysbus_init_irq(sbd, &s->irq);
62292192358STong Ho 
62392192358STong Ho     s->prng = g_rand_new();
62492192358STong Ho }
62592192358STong Ho 
trng_finalize(Object * obj)626abbfe8d8SPeter Maydell static void trng_finalize(Object *obj)
62792192358STong Ho {
628abbfe8d8SPeter Maydell     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj);
62992192358STong Ho 
630*14bfca0dSPeter Maydell     register_finalize_block(s->reg_array);
63192192358STong Ho     g_rand_free(s->prng);
63292192358STong Ho     s->prng = NULL;
63392192358STong Ho }
63492192358STong Ho 
trng_reset_hold(Object * obj,ResetType type)635ad80e367SPeter Maydell static void trng_reset_hold(Object *obj, ResetType type)
63692192358STong Ho {
63792192358STong Ho     trng_reset(XLNX_VERSAL_TRNG(obj));
63892192358STong Ho }
63992192358STong Ho 
trng_prop_fault_event_set(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)64092192358STong Ho static void trng_prop_fault_event_set(Object *obj, Visitor *v,
64192192358STong Ho                                       const char *name, void *opaque,
64292192358STong Ho                                       Error **errp)
64392192358STong Ho {
64492192358STong Ho     Property *prop = opaque;
64592192358STong Ho     uint32_t *events = object_field_prop_ptr(obj, prop);
64692192358STong Ho 
6471e0efa97SZhao Liu     if (!visit_type_uint32(v, name, events, errp)) {
64892192358STong Ho         return;
64992192358STong Ho     }
65092192358STong Ho 
65192192358STong Ho     trng_fault_event_set(XLNX_VERSAL_TRNG(obj), *events);
65292192358STong Ho }
65392192358STong Ho 
65492192358STong Ho static const PropertyInfo trng_prop_fault_events = {
65592192358STong Ho     .name = "uint32:bits",
65692192358STong Ho     .description = "Set to trigger TRNG fault events",
65792192358STong Ho     .set = trng_prop_fault_event_set,
65892192358STong Ho     .realized_set_allowed = true,
65992192358STong Ho };
66092192358STong Ho 
66192192358STong Ho static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */
66292192358STong Ho 
66392192358STong Ho static Property trng_props[] = {
66492192358STong Ho     DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0),
66592192358STong Ho     DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200),
66692192358STong Ho     DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults,
66792192358STong Ho                 trng_prop_fault_events, uint32_t),
66892192358STong Ho 
66992192358STong Ho     DEFINE_PROP_END_OF_LIST(),
67092192358STong Ho };
67192192358STong Ho 
67292192358STong Ho static const VMStateDescription vmstate_trng = {
67392192358STong Ho     .name = TYPE_XLNX_VERSAL_TRNG,
67492192358STong Ho     .version_id = 1,
67592192358STong Ho     .minimum_version_id = 1,
676e4ea952fSRichard Henderson     .fields = (const VMStateField[]) {
67792192358STong Ho         VMSTATE_UINT32(rand_count, XlnxVersalTRng),
67892192358STong Ho         VMSTATE_UINT64(rand_reseed, XlnxVersalTRng),
67992192358STong Ho         VMSTATE_UINT64(forced_prng_count, XlnxVersalTRng),
68092192358STong Ho         VMSTATE_UINT64_ARRAY(tst_seed, XlnxVersalTRng, 2),
68192192358STong Ho         VMSTATE_UINT32_ARRAY(regs, XlnxVersalTRng, R_MAX),
68292192358STong Ho         VMSTATE_END_OF_LIST(),
68392192358STong Ho     }
68492192358STong Ho };
68592192358STong Ho 
trng_class_init(ObjectClass * klass,void * data)68692192358STong Ho static void trng_class_init(ObjectClass *klass, void *data)
68792192358STong Ho {
68892192358STong Ho     DeviceClass *dc = DEVICE_CLASS(klass);
68992192358STong Ho     ResettableClass *rc = RESETTABLE_CLASS(klass);
69092192358STong Ho 
69192192358STong Ho     dc->vmsd = &vmstate_trng;
69292192358STong Ho     rc->phases.hold = trng_reset_hold;
69392192358STong Ho 
69492192358STong Ho     /* Clone uint64 property with set allowed after realized */
69592192358STong Ho     trng_prop_uint64 = qdev_prop_uint64;
69692192358STong Ho     trng_prop_uint64.realized_set_allowed = true;
69792192358STong Ho     trng_props[0].info = &trng_prop_uint64;
69892192358STong Ho 
69992192358STong Ho     device_class_set_props(dc, trng_props);
70092192358STong Ho }
70192192358STong Ho 
70292192358STong Ho static const TypeInfo trng_info = {
70392192358STong Ho     .name          = TYPE_XLNX_VERSAL_TRNG,
70492192358STong Ho     .parent        = TYPE_SYS_BUS_DEVICE,
70592192358STong Ho     .instance_size = sizeof(XlnxVersalTRng),
70692192358STong Ho     .class_init    = trng_class_init,
70792192358STong Ho     .instance_init = trng_init,
708abbfe8d8SPeter Maydell     .instance_finalize = trng_finalize,
70992192358STong Ho };
71092192358STong Ho 
trng_register_types(void)71192192358STong Ho static void trng_register_types(void)
71292192358STong Ho {
71392192358STong Ho     type_register_static(&trng_info);
71492192358STong Ho }
71592192358STong Ho 
71692192358STong Ho type_init(trng_register_types)
717