xref: /qemu/hw/misc/xlnx-versal-trng.c (revision 921923583fb2583a277e3b3d6cb8e8e13c622d48)
1*92192358STong Ho /*
2*92192358STong Ho  * Non-crypto strength model of the True Random Number Generator
3*92192358STong Ho  * in the AMD/Xilinx Versal device family.
4*92192358STong Ho  *
5*92192358STong Ho  * Copyright (c) 2017-2020 Xilinx Inc.
6*92192358STong Ho  * Copyright (c) 2023 Advanced Micro Devices, Inc.
7*92192358STong Ho  *
8*92192358STong Ho  * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
9*92192358STong Ho  *
10*92192358STong Ho  * Permission is hereby granted, free of charge, to any person obtaining a copy
11*92192358STong Ho  * of this software and associated documentation files (the "Software"), to deal
12*92192358STong Ho  * in the Software without restriction, including without limitation the rights
13*92192358STong Ho  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14*92192358STong Ho  * copies of the Software, and to permit persons to whom the Software is
15*92192358STong Ho  * furnished to do so, subject to the following conditions:
16*92192358STong Ho  *
17*92192358STong Ho  * The above copyright notice and this permission notice shall be included in
18*92192358STong Ho  * all copies or substantial portions of the Software.
19*92192358STong Ho  *
20*92192358STong Ho  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21*92192358STong Ho  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22*92192358STong Ho  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23*92192358STong Ho  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24*92192358STong Ho  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25*92192358STong Ho  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26*92192358STong Ho  * THE SOFTWARE.
27*92192358STong Ho  */
28*92192358STong Ho #include "qemu/osdep.h"
29*92192358STong Ho #include "hw/misc/xlnx-versal-trng.h"
30*92192358STong Ho 
31*92192358STong Ho #include "qemu/bitops.h"
32*92192358STong Ho #include "qemu/log.h"
33*92192358STong Ho #include "qemu/error-report.h"
34*92192358STong Ho #include "qemu/guest-random.h"
35*92192358STong Ho #include "qemu/timer.h"
36*92192358STong Ho #include "qapi/visitor.h"
37*92192358STong Ho #include "migration/vmstate.h"
38*92192358STong Ho #include "hw/qdev-properties.h"
39*92192358STong Ho 
40*92192358STong Ho #ifndef XLNX_VERSAL_TRNG_ERR_DEBUG
41*92192358STong Ho #define XLNX_VERSAL_TRNG_ERR_DEBUG 0
42*92192358STong Ho #endif
43*92192358STong Ho 
44*92192358STong Ho REG32(INT_CTRL, 0x0)
45*92192358STong Ho     FIELD(INT_CTRL, CERTF_RST, 5, 1)
46*92192358STong Ho     FIELD(INT_CTRL, DTF_RST, 4, 1)
47*92192358STong Ho     FIELD(INT_CTRL, DONE_RST, 3, 1)
48*92192358STong Ho     FIELD(INT_CTRL, CERTF_EN, 2, 1)
49*92192358STong Ho     FIELD(INT_CTRL, DTF_EN, 1, 1)
50*92192358STong Ho     FIELD(INT_CTRL, DONE_EN, 0, 1)
51*92192358STong Ho REG32(STATUS, 0x4)
52*92192358STong Ho     FIELD(STATUS, QCNT, 9, 3)
53*92192358STong Ho     FIELD(STATUS, EAT, 4, 5)
54*92192358STong Ho     FIELD(STATUS, CERTF, 3, 1)
55*92192358STong Ho     FIELD(STATUS, DTF, 1, 1)
56*92192358STong Ho     FIELD(STATUS, DONE, 0, 1)
57*92192358STong Ho REG32(CTRL, 0x8)
58*92192358STong Ho     FIELD(CTRL, PERSODISABLE, 10, 1)
59*92192358STong Ho     FIELD(CTRL, SINGLEGENMODE, 9, 1)
60*92192358STong Ho     FIELD(CTRL, EUMODE, 8, 1)
61*92192358STong Ho     FIELD(CTRL, PRNGMODE, 7, 1)
62*92192358STong Ho     FIELD(CTRL, TSTMODE, 6, 1)
63*92192358STong Ho     FIELD(CTRL, PRNGSTART, 5, 1)
64*92192358STong Ho     FIELD(CTRL, EATAU, 4, 1)
65*92192358STong Ho     FIELD(CTRL, PRNGXS, 3, 1)
66*92192358STong Ho     FIELD(CTRL, TRSSEN, 2, 1)
67*92192358STong Ho     FIELD(CTRL, QERTUEN, 1, 1)
68*92192358STong Ho     FIELD(CTRL, PRNGSRST, 0, 1)
69*92192358STong Ho REG32(CTRL_2, 0xc)
70*92192358STong Ho     FIELD(CTRL_2, REPCOUNTTESTCUTOFF, 8, 9)
71*92192358STong Ho     FIELD(CTRL_2, RESERVED_7_5, 5, 3)
72*92192358STong Ho     FIELD(CTRL_2, DIT, 0, 5)
73*92192358STong Ho REG32(CTRL_3, 0x10)
74*92192358STong Ho     FIELD(CTRL_3, ADAPTPROPTESTCUTOFF, 8, 10)
75*92192358STong Ho     FIELD(CTRL_3, DLEN, 0, 8)
76*92192358STong Ho REG32(CTRL_4, 0x14)
77*92192358STong Ho     FIELD(CTRL_4, SINGLEBITRAW, 0, 1)
78*92192358STong Ho REG32(EXT_SEED_0, 0x40)
79*92192358STong Ho REG32(EXT_SEED_1, 0x44)
80*92192358STong Ho REG32(EXT_SEED_2, 0x48)
81*92192358STong Ho REG32(EXT_SEED_3, 0x4c)
82*92192358STong Ho REG32(EXT_SEED_4, 0x50)
83*92192358STong Ho REG32(EXT_SEED_5, 0x54)
84*92192358STong Ho REG32(EXT_SEED_6, 0x58)
85*92192358STong Ho REG32(EXT_SEED_7, 0x5c)
86*92192358STong Ho REG32(EXT_SEED_8, 0x60)
87*92192358STong Ho REG32(EXT_SEED_9, 0x64)
88*92192358STong Ho REG32(EXT_SEED_10, 0x68)
89*92192358STong Ho REG32(EXT_SEED_11, 0x6c)
90*92192358STong Ho REG32(PER_STRNG_0, 0x80)
91*92192358STong Ho REG32(PER_STRNG_1, 0x84)
92*92192358STong Ho REG32(PER_STRNG_2, 0x88)
93*92192358STong Ho REG32(PER_STRNG_3, 0x8c)
94*92192358STong Ho REG32(PER_STRNG_4, 0x90)
95*92192358STong Ho REG32(PER_STRNG_5, 0x94)
96*92192358STong Ho REG32(PER_STRNG_6, 0x98)
97*92192358STong Ho REG32(PER_STRNG_7, 0x9c)
98*92192358STong Ho REG32(PER_STRNG_8, 0xa0)
99*92192358STong Ho REG32(PER_STRNG_9, 0xa4)
100*92192358STong Ho REG32(PER_STRNG_10, 0xa8)
101*92192358STong Ho REG32(PER_STRNG_11, 0xac)
102*92192358STong Ho REG32(CORE_OUTPUT, 0xc0)
103*92192358STong Ho REG32(RESET, 0xd0)
104*92192358STong Ho     FIELD(RESET, VAL, 0, 1)
105*92192358STong Ho REG32(OSC_EN, 0xd4)
106*92192358STong Ho     FIELD(OSC_EN, VAL, 0, 1)
107*92192358STong Ho REG32(TRNG_ISR, 0xe0)
108*92192358STong Ho     FIELD(TRNG_ISR, SLVERR, 1, 1)
109*92192358STong Ho     FIELD(TRNG_ISR, CORE_INT, 0, 1)
110*92192358STong Ho REG32(TRNG_IMR, 0xe4)
111*92192358STong Ho     FIELD(TRNG_IMR, SLVERR, 1, 1)
112*92192358STong Ho     FIELD(TRNG_IMR, CORE_INT, 0, 1)
113*92192358STong Ho REG32(TRNG_IER, 0xe8)
114*92192358STong Ho     FIELD(TRNG_IER, SLVERR, 1, 1)
115*92192358STong Ho     FIELD(TRNG_IER, CORE_INT, 0, 1)
116*92192358STong Ho REG32(TRNG_IDR, 0xec)
117*92192358STong Ho     FIELD(TRNG_IDR, SLVERR, 1, 1)
118*92192358STong Ho     FIELD(TRNG_IDR, CORE_INT, 0, 1)
119*92192358STong Ho REG32(SLV_ERR_CTRL, 0xf0)
120*92192358STong Ho     FIELD(SLV_ERR_CTRL, ENABLE, 0, 1)
121*92192358STong Ho 
122*92192358STong Ho #define R_MAX (R_SLV_ERR_CTRL + 1)
123*92192358STong Ho 
124*92192358STong Ho QEMU_BUILD_BUG_ON(R_MAX * 4 != sizeof_field(XlnxVersalTRng, regs));
125*92192358STong Ho 
126*92192358STong Ho #define TRNG_GUEST_ERROR(D, FMT, ...) \
127*92192358STong Ho     do {                                                               \
128*92192358STong Ho         g_autofree char *p = object_get_canonical_path(OBJECT(D));     \
129*92192358STong Ho         qemu_log_mask(LOG_GUEST_ERROR, "%s: " FMT, p, ## __VA_ARGS__); \
130*92192358STong Ho     } while (0)
131*92192358STong Ho 
132*92192358STong Ho #define TRNG_WARN(D, FMT, ...) \
133*92192358STong Ho     do {                                                               \
134*92192358STong Ho         g_autofree char *p = object_get_canonical_path(OBJECT(D));     \
135*92192358STong Ho         warn_report("%s: " FMT, p, ## __VA_ARGS__);                    \
136*92192358STong Ho     } while (0)
137*92192358STong Ho 
138*92192358STong Ho static bool trng_older_than_v2(XlnxVersalTRng *s)
139*92192358STong Ho {
140*92192358STong Ho     return s->hw_version < 0x0200;
141*92192358STong Ho }
142*92192358STong Ho 
143*92192358STong Ho static bool trng_in_reset(XlnxVersalTRng *s)
144*92192358STong Ho {
145*92192358STong Ho     if (ARRAY_FIELD_EX32(s->regs, RESET, VAL)) {
146*92192358STong Ho         return true;
147*92192358STong Ho     }
148*92192358STong Ho     if (ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSRST)) {
149*92192358STong Ho         return true;
150*92192358STong Ho     }
151*92192358STong Ho 
152*92192358STong Ho     return false;
153*92192358STong Ho }
154*92192358STong Ho 
155*92192358STong Ho static bool trng_test_enabled(XlnxVersalTRng *s)
156*92192358STong Ho {
157*92192358STong Ho     return ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE);
158*92192358STong Ho }
159*92192358STong Ho 
160*92192358STong Ho static bool trng_trss_enabled(XlnxVersalTRng *s)
161*92192358STong Ho {
162*92192358STong Ho     if (trng_in_reset(s)) {
163*92192358STong Ho         return false;
164*92192358STong Ho     }
165*92192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, CTRL, TRSSEN)) {
166*92192358STong Ho         return false;
167*92192358STong Ho     }
168*92192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, OSC_EN, VAL)) {
169*92192358STong Ho         return false;
170*92192358STong Ho     }
171*92192358STong Ho 
172*92192358STong Ho     return true;
173*92192358STong Ho }
174*92192358STong Ho 
175*92192358STong Ho static void trng_seed_128(uint32_t *seed, uint64_t h00, uint64_t h64)
176*92192358STong Ho {
177*92192358STong Ho     seed[0] = extract64(h00, 0, 32);
178*92192358STong Ho     seed[1] = extract64(h00, 32, 32);
179*92192358STong Ho     seed[2] = extract64(h64, 0, 32);
180*92192358STong Ho     seed[3] = extract64(h64, 32, 32);
181*92192358STong Ho }
182*92192358STong Ho 
183*92192358STong Ho static void trng_reseed(XlnxVersalTRng *s)
184*92192358STong Ho {
185*92192358STong Ho     bool ext_seed = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGXS);
186*92192358STong Ho     bool pers_disabled = ARRAY_FIELD_EX32(s->regs, CTRL, PERSODISABLE);
187*92192358STong Ho 
188*92192358STong Ho     enum {
189*92192358STong Ho         U384_U8 = 384 / 8,
190*92192358STong Ho         U384_U32 = 384 / 32,
191*92192358STong Ho     };
192*92192358STong Ho 
193*92192358STong Ho     /*
194*92192358STong Ho      * Maximum seed length is len(personalized string) + len(ext seed).
195*92192358STong Ho      *
196*92192358STong Ho      * g_rand_set_seed_array() takes array of uint32 in host endian.
197*92192358STong Ho      */
198*92192358STong Ho     guint32 gs[U384_U32 * 2], *seed = &gs[U384_U32];
199*92192358STong Ho 
200*92192358STong Ho     /*
201*92192358STong Ho      * A disabled personalized string is the same as
202*92192358STong Ho      * a string with all zeros.
203*92192358STong Ho      *
204*92192358STong Ho      * The device's hardware spec defines 3 modes (all selectable
205*92192358STong Ho      * by guest at will and at anytime):
206*92192358STong Ho      * 1) External seeding
207*92192358STong Ho      *    This is a PRNG mode, in which the produced sequence shall
208*92192358STong Ho      *    be reproducible if reseeded by the same 384-bit seed, as
209*92192358STong Ho      *    supplied by guest software.
210*92192358STong Ho      * 2) Test seeding
211*92192358STong Ho      *    This is a PRNG mode, in which the produced sequence shall
212*92192358STong Ho      *    be reproducible if reseeded by a 128-bit test seed, as
213*92192358STong Ho      *    supplied by guest software.
214*92192358STong Ho      * 3) Truly-random seeding
215*92192358STong Ho      *    This is the TRNG mode, in which the produced sequence is
216*92192358STong Ho      *    periodically reseeded by a crypto-strength entropy source.
217*92192358STong Ho      *
218*92192358STong Ho      * To assist debugging of certain classes of software defects,
219*92192358STong Ho      * this QEMU model implements a 4th mode,
220*92192358STong Ho      * 4) Forced PRNG
221*92192358STong Ho      *    When in this mode, a reproducible sequence is generated
222*92192358STong Ho      *    if software has selected the TRNG mode (mode 2).
223*92192358STong Ho      *
224*92192358STong Ho      *    This emulation-only mode can only be selected by setting
225*92192358STong Ho      *    the uint64 property 'forced-prng' to a non-zero value.
226*92192358STong Ho      *    Guest software cannot select this mode.
227*92192358STong Ho      */
228*92192358STong Ho     memset(gs, 0, sizeof(gs));
229*92192358STong Ho 
230*92192358STong Ho     if (!pers_disabled) {
231*92192358STong Ho         memcpy(gs, &s->regs[R_PER_STRNG_0], U384_U8);
232*92192358STong Ho     }
233*92192358STong Ho 
234*92192358STong Ho     if (ext_seed) {
235*92192358STong Ho         memcpy(seed, &s->regs[R_EXT_SEED_0], U384_U8);
236*92192358STong Ho     } else if (trng_test_enabled(s)) {
237*92192358STong Ho         trng_seed_128(seed, s->tst_seed[0], s->tst_seed[1]);
238*92192358STong Ho     } else if (s->forced_prng_seed) {
239*92192358STong Ho         s->forced_prng_count++;
240*92192358STong Ho         trng_seed_128(seed, s->forced_prng_count, s->forced_prng_seed);
241*92192358STong Ho     } else {
242*92192358STong Ho         qemu_guest_getrandom_nofail(seed, U384_U8);
243*92192358STong Ho     }
244*92192358STong Ho 
245*92192358STong Ho     g_rand_set_seed_array(s->prng, gs, ARRAY_SIZE(gs));
246*92192358STong Ho 
247*92192358STong Ho     s->rand_count = 0;
248*92192358STong Ho     s->rand_reseed = 1ULL << 48;
249*92192358STong Ho }
250*92192358STong Ho 
251*92192358STong Ho static void trng_regen(XlnxVersalTRng *s)
252*92192358STong Ho {
253*92192358STong Ho     if (s->rand_reseed == 0) {
254*92192358STong Ho         TRNG_GUEST_ERROR(s, "Too many generations without a reseed");
255*92192358STong Ho         trng_reseed(s);
256*92192358STong Ho     }
257*92192358STong Ho     s->rand_reseed--;
258*92192358STong Ho 
259*92192358STong Ho     /*
260*92192358STong Ho      * In real hardware, each regen creates 256 bits, but QCNT
261*92192358STong Ho      * reports a max of 4.
262*92192358STong Ho      */
263*92192358STong Ho     ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4);
264*92192358STong Ho     s->rand_count = 256 / 32;
265*92192358STong Ho }
266*92192358STong Ho 
267*92192358STong Ho static uint32_t trng_rdout(XlnxVersalTRng *s)
268*92192358STong Ho {
269*92192358STong Ho     assert(s->rand_count);
270*92192358STong Ho 
271*92192358STong Ho     s->rand_count--;
272*92192358STong Ho     if (s->rand_count < 4) {
273*92192358STong Ho         ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, s->rand_count);
274*92192358STong Ho     }
275*92192358STong Ho 
276*92192358STong Ho     return g_rand_int(s->prng);
277*92192358STong Ho }
278*92192358STong Ho 
279*92192358STong Ho static void trng_irq_update(XlnxVersalTRng *s)
280*92192358STong Ho {
281*92192358STong Ho     bool pending = s->regs[R_TRNG_ISR] & ~s->regs[R_TRNG_IMR];
282*92192358STong Ho     qemu_set_irq(s->irq, pending);
283*92192358STong Ho }
284*92192358STong Ho 
285*92192358STong Ho static void trng_isr_postw(RegisterInfo *reg, uint64_t val64)
286*92192358STong Ho {
287*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
288*92192358STong Ho     trng_irq_update(s);
289*92192358STong Ho }
290*92192358STong Ho 
291*92192358STong Ho static uint64_t trng_ier_prew(RegisterInfo *reg, uint64_t val64)
292*92192358STong Ho {
293*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
294*92192358STong Ho     uint32_t val = val64;
295*92192358STong Ho 
296*92192358STong Ho     s->regs[R_TRNG_IMR] &= ~val;
297*92192358STong Ho     trng_irq_update(s);
298*92192358STong Ho     return 0;
299*92192358STong Ho }
300*92192358STong Ho 
301*92192358STong Ho static uint64_t trng_idr_prew(RegisterInfo *reg, uint64_t val64)
302*92192358STong Ho {
303*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
304*92192358STong Ho     uint32_t val = val64;
305*92192358STong Ho 
306*92192358STong Ho     s->regs[R_TRNG_IMR] |= val;
307*92192358STong Ho     trng_irq_update(s);
308*92192358STong Ho     return 0;
309*92192358STong Ho }
310*92192358STong Ho 
311*92192358STong Ho static void trng_core_int_update(XlnxVersalTRng *s)
312*92192358STong Ho {
313*92192358STong Ho     bool pending = false;
314*92192358STong Ho     uint32_t st = s->regs[R_STATUS];
315*92192358STong Ho     uint32_t en = s->regs[R_INT_CTRL];
316*92192358STong Ho 
317*92192358STong Ho     if (FIELD_EX32(st, STATUS, CERTF) && FIELD_EX32(en, INT_CTRL, CERTF_EN)) {
318*92192358STong Ho         pending = true;
319*92192358STong Ho     }
320*92192358STong Ho 
321*92192358STong Ho     if (FIELD_EX32(st, STATUS, DTF) && FIELD_EX32(en, INT_CTRL, DTF_EN)) {
322*92192358STong Ho         pending = true;
323*92192358STong Ho     }
324*92192358STong Ho 
325*92192358STong Ho     if (FIELD_EX32(st, STATUS, DONE) && FIELD_EX32(en, INT_CTRL, DONE_EN)) {
326*92192358STong Ho         pending = true;
327*92192358STong Ho     }
328*92192358STong Ho 
329*92192358STong Ho     ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, pending);
330*92192358STong Ho     trng_irq_update(s);
331*92192358STong Ho }
332*92192358STong Ho 
333*92192358STong Ho static void trng_int_ctrl_postw(RegisterInfo *reg, uint64_t val64)
334*92192358STong Ho {
335*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
336*92192358STong Ho     uint32_t v32 = val64;
337*92192358STong Ho     uint32_t clr_mask = 0;
338*92192358STong Ho 
339*92192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, CERTF_RST)) {
340*92192358STong Ho         clr_mask |= R_STATUS_CERTF_MASK;
341*92192358STong Ho     }
342*92192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, DTF_RST)) {
343*92192358STong Ho         clr_mask |= R_STATUS_DTF_MASK;
344*92192358STong Ho     }
345*92192358STong Ho     if (FIELD_EX32(v32, INT_CTRL, DONE_RST)) {
346*92192358STong Ho         clr_mask |= R_STATUS_DONE_MASK;
347*92192358STong Ho     }
348*92192358STong Ho 
349*92192358STong Ho     s->regs[R_STATUS] &= ~clr_mask;
350*92192358STong Ho     trng_core_int_update(s);
351*92192358STong Ho }
352*92192358STong Ho 
353*92192358STong Ho static void trng_done(XlnxVersalTRng *s)
354*92192358STong Ho {
355*92192358STong Ho     ARRAY_FIELD_DP32(s->regs, STATUS, DONE, true);
356*92192358STong Ho     trng_core_int_update(s);
357*92192358STong Ho }
358*92192358STong Ho 
359*92192358STong Ho static void trng_fault_event_set(XlnxVersalTRng *s, uint32_t events)
360*92192358STong Ho {
361*92192358STong Ho     bool pending = false;
362*92192358STong Ho 
363*92192358STong Ho     /* Disabled TRSS cannot generate any fault event */
364*92192358STong Ho     if (!trng_trss_enabled(s)) {
365*92192358STong Ho         return;
366*92192358STong Ho     }
367*92192358STong Ho 
368*92192358STong Ho     if (FIELD_EX32(events, STATUS, CERTF)) {
369*92192358STong Ho         /* In older version, ERTU must be enabled explicitly to get CERTF */
370*92192358STong Ho         if (trng_older_than_v2(s) &&
371*92192358STong Ho             !ARRAY_FIELD_EX32(s->regs, CTRL, QERTUEN)) {
372*92192358STong Ho             TRNG_WARN(s, "CERTF injection ignored: ERTU disabled");
373*92192358STong Ho         } else {
374*92192358STong Ho             ARRAY_FIELD_DP32(s->regs, STATUS, CERTF, true);
375*92192358STong Ho             pending = true;
376*92192358STong Ho         }
377*92192358STong Ho     }
378*92192358STong Ho 
379*92192358STong Ho     if (FIELD_EX32(events, STATUS, DTF)) {
380*92192358STong Ho         ARRAY_FIELD_DP32(s->regs, STATUS, DTF, true);
381*92192358STong Ho         pending = true;
382*92192358STong Ho     }
383*92192358STong Ho 
384*92192358STong Ho     if (pending) {
385*92192358STong Ho         trng_core_int_update(s);
386*92192358STong Ho     }
387*92192358STong Ho }
388*92192358STong Ho 
389*92192358STong Ho static void trng_soft_reset(XlnxVersalTRng *s)
390*92192358STong Ho {
391*92192358STong Ho     s->rand_count = 0;
392*92192358STong Ho     s->regs[R_STATUS] = 0;
393*92192358STong Ho 
394*92192358STong Ho     ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, 0);
395*92192358STong Ho }
396*92192358STong Ho 
397*92192358STong Ho static void trng_ctrl_postw(RegisterInfo *reg, uint64_t val64)
398*92192358STong Ho {
399*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
400*92192358STong Ho 
401*92192358STong Ho     if (trng_in_reset(s)) {
402*92192358STong Ho         return;
403*92192358STong Ho     }
404*92192358STong Ho 
405*92192358STong Ho     if (FIELD_EX32(val64, CTRL, PRNGSRST)) {
406*92192358STong Ho         trng_soft_reset(s);
407*92192358STong Ho         trng_irq_update(s);
408*92192358STong Ho         return;
409*92192358STong Ho     }
410*92192358STong Ho 
411*92192358STong Ho     if (!FIELD_EX32(val64, CTRL, PRNGSTART)) {
412*92192358STong Ho         return;
413*92192358STong Ho     }
414*92192358STong Ho 
415*92192358STong Ho     if (FIELD_EX32(val64, CTRL, PRNGMODE)) {
416*92192358STong Ho         trng_regen(s);
417*92192358STong Ho     } else {
418*92192358STong Ho         trng_reseed(s);
419*92192358STong Ho     }
420*92192358STong Ho 
421*92192358STong Ho     trng_done(s);
422*92192358STong Ho }
423*92192358STong Ho 
424*92192358STong Ho static void trng_ctrl4_postw(RegisterInfo *reg, uint64_t val64)
425*92192358STong Ho {
426*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
427*92192358STong Ho 
428*92192358STong Ho     /* Only applies to test mode with TRSS enabled */
429*92192358STong Ho     if (!trng_test_enabled(s) || !trng_trss_enabled(s)) {
430*92192358STong Ho         return;
431*92192358STong Ho     }
432*92192358STong Ho 
433*92192358STong Ho     /* Shift in a single bit.  */
434*92192358STong Ho     s->tst_seed[1] <<= 1;
435*92192358STong Ho     s->tst_seed[1] |= s->tst_seed[0] >> 63;
436*92192358STong Ho     s->tst_seed[0] <<= 1;
437*92192358STong Ho     s->tst_seed[0] |= val64 & 1;
438*92192358STong Ho 
439*92192358STong Ho     trng_reseed(s);
440*92192358STong Ho     trng_regen(s);
441*92192358STong Ho }
442*92192358STong Ho 
443*92192358STong Ho static uint64_t trng_core_out_postr(RegisterInfo *reg, uint64_t val)
444*92192358STong Ho {
445*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
446*92192358STong Ho     bool oneshot = ARRAY_FIELD_EX32(s->regs, CTRL, SINGLEGENMODE);
447*92192358STong Ho     bool start = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSTART);
448*92192358STong Ho     uint32_t r = 0xbad;
449*92192358STong Ho 
450*92192358STong Ho     if (trng_in_reset(s)) {
451*92192358STong Ho         TRNG_GUEST_ERROR(s, "Reading random number while in reset!");
452*92192358STong Ho         return r;
453*92192358STong Ho     }
454*92192358STong Ho 
455*92192358STong Ho     if (s->rand_count == 0) {
456*92192358STong Ho         TRNG_GUEST_ERROR(s, "Reading random number when unavailable!");
457*92192358STong Ho         return r;
458*92192358STong Ho     }
459*92192358STong Ho 
460*92192358STong Ho     r = trng_rdout(s);
461*92192358STong Ho 
462*92192358STong Ho     /* Automatic mode regenerates when half the output reg is empty.  */
463*92192358STong Ho     if (!oneshot && start && s->rand_count <= 3) {
464*92192358STong Ho         trng_regen(s);
465*92192358STong Ho     }
466*92192358STong Ho 
467*92192358STong Ho     return r;
468*92192358STong Ho }
469*92192358STong Ho 
470*92192358STong Ho static void trng_reset(XlnxVersalTRng *s)
471*92192358STong Ho {
472*92192358STong Ho     unsigned int i;
473*92192358STong Ho 
474*92192358STong Ho     s->forced_prng_count = 0;
475*92192358STong Ho 
476*92192358STong Ho     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
477*92192358STong Ho         register_reset(&s->regs_info[i]);
478*92192358STong Ho     }
479*92192358STong Ho     trng_soft_reset(s);
480*92192358STong Ho     trng_irq_update(s);
481*92192358STong Ho }
482*92192358STong Ho 
483*92192358STong Ho static uint64_t trng_reset_prew(RegisterInfo *reg, uint64_t val64)
484*92192358STong Ho {
485*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque);
486*92192358STong Ho 
487*92192358STong Ho     if (!ARRAY_FIELD_EX32(s->regs, RESET, VAL) &&
488*92192358STong Ho         FIELD_EX32(val64, RESET, VAL)) {
489*92192358STong Ho         trng_reset(s);
490*92192358STong Ho     }
491*92192358STong Ho 
492*92192358STong Ho     return val64;
493*92192358STong Ho }
494*92192358STong Ho 
495*92192358STong Ho static uint64_t trng_register_read(void *opaque, hwaddr addr, unsigned size)
496*92192358STong Ho {
497*92192358STong Ho     /*
498*92192358STong Ho      * Guest provided seed and personalized strings cannot be
499*92192358STong Ho      * read back, and read attempts return value of A_STATUS.
500*92192358STong Ho      */
501*92192358STong Ho     switch (addr) {
502*92192358STong Ho     case A_EXT_SEED_0 ... A_PER_STRNG_11:
503*92192358STong Ho         addr = A_STATUS;
504*92192358STong Ho         break;
505*92192358STong Ho     }
506*92192358STong Ho 
507*92192358STong Ho     return register_read_memory(opaque, addr, size);
508*92192358STong Ho }
509*92192358STong Ho 
510*92192358STong Ho static void trng_register_write(void *opaque, hwaddr addr,
511*92192358STong Ho                                 uint64_t value, unsigned size)
512*92192358STong Ho {
513*92192358STong Ho     RegisterInfoArray *reg_array = opaque;
514*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg_array->r[0]->opaque);
515*92192358STong Ho 
516*92192358STong Ho     if (trng_older_than_v2(s)) {
517*92192358STong Ho         switch (addr) {
518*92192358STong Ho         case A_CTRL:
519*92192358STong Ho             value = FIELD_DP64(value, CTRL, PERSODISABLE, 0);
520*92192358STong Ho             value = FIELD_DP64(value, CTRL, SINGLEGENMODE, 0);
521*92192358STong Ho             break;
522*92192358STong Ho         case A_CTRL_2:
523*92192358STong Ho         case A_CTRL_3:
524*92192358STong Ho         case A_CTRL_4:
525*92192358STong Ho             return;
526*92192358STong Ho         }
527*92192358STong Ho     } else {
528*92192358STong Ho         switch (addr) {
529*92192358STong Ho         case A_CTRL:
530*92192358STong Ho             value = FIELD_DP64(value, CTRL, EATAU, 0);
531*92192358STong Ho             value = FIELD_DP64(value, CTRL, QERTUEN, 0);
532*92192358STong Ho             break;
533*92192358STong Ho         }
534*92192358STong Ho     }
535*92192358STong Ho 
536*92192358STong Ho     register_write_memory(opaque, addr, value, size);
537*92192358STong Ho }
538*92192358STong Ho 
539*92192358STong Ho static RegisterAccessInfo trng_regs_info[] = {
540*92192358STong Ho     {   .name = "INT_CTRL",  .addr = A_INT_CTRL,
541*92192358STong Ho         .post_write = trng_int_ctrl_postw,
542*92192358STong Ho     },{ .name = "STATUS",  .addr = A_STATUS,
543*92192358STong Ho         .ro = 0xfff,
544*92192358STong Ho     },{ .name = "CTRL",  .addr = A_CTRL,
545*92192358STong Ho         .post_write = trng_ctrl_postw,
546*92192358STong Ho     },{ .name = "CTRL_2",  .addr = A_CTRL_2,
547*92192358STong Ho         .reset = 0x210c,
548*92192358STong Ho     },{ .name = "CTRL_3",  .addr = A_CTRL_3,
549*92192358STong Ho         .reset = 0x26f09,
550*92192358STong Ho     },{ .name = "CTRL_4",  .addr = A_CTRL_4,
551*92192358STong Ho         .post_write = trng_ctrl4_postw,
552*92192358STong Ho     },{ .name = "EXT_SEED_0",  .addr = A_EXT_SEED_0,
553*92192358STong Ho     },{ .name = "EXT_SEED_1",  .addr = A_EXT_SEED_1,
554*92192358STong Ho     },{ .name = "EXT_SEED_2",  .addr = A_EXT_SEED_2,
555*92192358STong Ho     },{ .name = "EXT_SEED_3",  .addr = A_EXT_SEED_3,
556*92192358STong Ho     },{ .name = "EXT_SEED_4",  .addr = A_EXT_SEED_4,
557*92192358STong Ho     },{ .name = "EXT_SEED_5",  .addr = A_EXT_SEED_5,
558*92192358STong Ho     },{ .name = "EXT_SEED_6",  .addr = A_EXT_SEED_6,
559*92192358STong Ho     },{ .name = "EXT_SEED_7",  .addr = A_EXT_SEED_7,
560*92192358STong Ho     },{ .name = "EXT_SEED_8",  .addr = A_EXT_SEED_8,
561*92192358STong Ho     },{ .name = "EXT_SEED_9",  .addr = A_EXT_SEED_9,
562*92192358STong Ho     },{ .name = "EXT_SEED_10",  .addr = A_EXT_SEED_10,
563*92192358STong Ho     },{ .name = "EXT_SEED_11",  .addr = A_EXT_SEED_11,
564*92192358STong Ho     },{ .name = "PER_STRNG_0",  .addr = A_PER_STRNG_0,
565*92192358STong Ho     },{ .name = "PER_STRNG_1",  .addr = A_PER_STRNG_1,
566*92192358STong Ho     },{ .name = "PER_STRNG_2",  .addr = A_PER_STRNG_2,
567*92192358STong Ho     },{ .name = "PER_STRNG_3",  .addr = A_PER_STRNG_3,
568*92192358STong Ho     },{ .name = "PER_STRNG_4",  .addr = A_PER_STRNG_4,
569*92192358STong Ho     },{ .name = "PER_STRNG_5",  .addr = A_PER_STRNG_5,
570*92192358STong Ho     },{ .name = "PER_STRNG_6",  .addr = A_PER_STRNG_6,
571*92192358STong Ho     },{ .name = "PER_STRNG_7",  .addr = A_PER_STRNG_7,
572*92192358STong Ho     },{ .name = "PER_STRNG_8",  .addr = A_PER_STRNG_8,
573*92192358STong Ho     },{ .name = "PER_STRNG_9",  .addr = A_PER_STRNG_9,
574*92192358STong Ho     },{ .name = "PER_STRNG_10",  .addr = A_PER_STRNG_10,
575*92192358STong Ho     },{ .name = "PER_STRNG_11",  .addr = A_PER_STRNG_11,
576*92192358STong Ho     },{ .name = "CORE_OUTPUT",  .addr = A_CORE_OUTPUT,
577*92192358STong Ho         .ro = 0xffffffff,
578*92192358STong Ho         .post_read = trng_core_out_postr,
579*92192358STong Ho     },{ .name = "RESET",  .addr = A_RESET,
580*92192358STong Ho         .reset = 0x1,
581*92192358STong Ho         .pre_write = trng_reset_prew,
582*92192358STong Ho     },{ .name = "OSC_EN",  .addr = A_OSC_EN,
583*92192358STong Ho     },{ .name = "TRNG_ISR",  .addr = A_TRNG_ISR,
584*92192358STong Ho         .w1c = 0x3,
585*92192358STong Ho         .post_write = trng_isr_postw,
586*92192358STong Ho     },{ .name = "TRNG_IMR",  .addr = A_TRNG_IMR,
587*92192358STong Ho         .reset = 0x3,
588*92192358STong Ho         .ro = 0x3,
589*92192358STong Ho     },{ .name = "TRNG_IER",  .addr = A_TRNG_IER,
590*92192358STong Ho         .pre_write = trng_ier_prew,
591*92192358STong Ho     },{ .name = "TRNG_IDR",  .addr = A_TRNG_IDR,
592*92192358STong Ho         .pre_write = trng_idr_prew,
593*92192358STong Ho     },{ .name = "SLV_ERR_CTRL",  .addr = A_SLV_ERR_CTRL,
594*92192358STong Ho     }
595*92192358STong Ho };
596*92192358STong Ho 
597*92192358STong Ho static const MemoryRegionOps trng_ops = {
598*92192358STong Ho     .read = trng_register_read,
599*92192358STong Ho     .write = trng_register_write,
600*92192358STong Ho     .endianness = DEVICE_LITTLE_ENDIAN,
601*92192358STong Ho     .valid = {
602*92192358STong Ho         .min_access_size = 4,
603*92192358STong Ho         .max_access_size = 4,
604*92192358STong Ho     },
605*92192358STong Ho };
606*92192358STong Ho 
607*92192358STong Ho static void trng_init(Object *obj)
608*92192358STong Ho {
609*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj);
610*92192358STong Ho     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
611*92192358STong Ho     RegisterInfoArray *reg_array;
612*92192358STong Ho 
613*92192358STong Ho     reg_array =
614*92192358STong Ho         register_init_block32(DEVICE(obj), trng_regs_info,
615*92192358STong Ho                               ARRAY_SIZE(trng_regs_info),
616*92192358STong Ho                               s->regs_info, s->regs,
617*92192358STong Ho                               &trng_ops,
618*92192358STong Ho                               XLNX_VERSAL_TRNG_ERR_DEBUG,
619*92192358STong Ho                               R_MAX * 4);
620*92192358STong Ho 
621*92192358STong Ho     sysbus_init_mmio(sbd, &reg_array->mem);
622*92192358STong Ho     sysbus_init_irq(sbd, &s->irq);
623*92192358STong Ho 
624*92192358STong Ho     s->prng = g_rand_new();
625*92192358STong Ho }
626*92192358STong Ho 
627*92192358STong Ho static void trng_unrealize(DeviceState *dev)
628*92192358STong Ho {
629*92192358STong Ho     XlnxVersalTRng *s = XLNX_VERSAL_TRNG(dev);
630*92192358STong Ho 
631*92192358STong Ho     g_rand_free(s->prng);
632*92192358STong Ho     s->prng = NULL;
633*92192358STong Ho }
634*92192358STong Ho 
635*92192358STong Ho static void trng_reset_hold(Object *obj)
636*92192358STong Ho {
637*92192358STong Ho     trng_reset(XLNX_VERSAL_TRNG(obj));
638*92192358STong Ho }
639*92192358STong Ho 
640*92192358STong Ho static void trng_prop_fault_event_set(Object *obj, Visitor *v,
641*92192358STong Ho                                       const char *name, void *opaque,
642*92192358STong Ho                                       Error **errp)
643*92192358STong Ho {
644*92192358STong Ho     Property *prop = opaque;
645*92192358STong Ho     uint32_t *events = object_field_prop_ptr(obj, prop);
646*92192358STong Ho 
647*92192358STong Ho     visit_type_uint32(v, name, events, errp);
648*92192358STong Ho     if (*errp) {
649*92192358STong Ho         return;
650*92192358STong Ho     }
651*92192358STong Ho 
652*92192358STong Ho     trng_fault_event_set(XLNX_VERSAL_TRNG(obj), *events);
653*92192358STong Ho }
654*92192358STong Ho 
655*92192358STong Ho static const PropertyInfo trng_prop_fault_events = {
656*92192358STong Ho     .name = "uint32:bits",
657*92192358STong Ho     .description = "Set to trigger TRNG fault events",
658*92192358STong Ho     .set = trng_prop_fault_event_set,
659*92192358STong Ho     .realized_set_allowed = true,
660*92192358STong Ho };
661*92192358STong Ho 
662*92192358STong Ho static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */
663*92192358STong Ho 
664*92192358STong Ho static Property trng_props[] = {
665*92192358STong Ho     DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0),
666*92192358STong Ho     DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200),
667*92192358STong Ho     DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults,
668*92192358STong Ho                 trng_prop_fault_events, uint32_t),
669*92192358STong Ho 
670*92192358STong Ho     DEFINE_PROP_END_OF_LIST(),
671*92192358STong Ho };
672*92192358STong Ho 
673*92192358STong Ho static const VMStateDescription vmstate_trng = {
674*92192358STong Ho     .name = TYPE_XLNX_VERSAL_TRNG,
675*92192358STong Ho     .version_id = 1,
676*92192358STong Ho     .minimum_version_id = 1,
677*92192358STong Ho     .fields = (VMStateField[]) {
678*92192358STong Ho         VMSTATE_UINT32(rand_count, XlnxVersalTRng),
679*92192358STong Ho         VMSTATE_UINT64(rand_reseed, XlnxVersalTRng),
680*92192358STong Ho         VMSTATE_UINT64(forced_prng_count, XlnxVersalTRng),
681*92192358STong Ho         VMSTATE_UINT64_ARRAY(tst_seed, XlnxVersalTRng, 2),
682*92192358STong Ho         VMSTATE_UINT32_ARRAY(regs, XlnxVersalTRng, R_MAX),
683*92192358STong Ho         VMSTATE_END_OF_LIST(),
684*92192358STong Ho     }
685*92192358STong Ho };
686*92192358STong Ho 
687*92192358STong Ho static void trng_class_init(ObjectClass *klass, void *data)
688*92192358STong Ho {
689*92192358STong Ho     DeviceClass *dc = DEVICE_CLASS(klass);
690*92192358STong Ho     ResettableClass *rc = RESETTABLE_CLASS(klass);
691*92192358STong Ho 
692*92192358STong Ho     dc->vmsd = &vmstate_trng;
693*92192358STong Ho     dc->unrealize = trng_unrealize;
694*92192358STong Ho     rc->phases.hold = trng_reset_hold;
695*92192358STong Ho 
696*92192358STong Ho     /* Clone uint64 property with set allowed after realized */
697*92192358STong Ho     trng_prop_uint64 = qdev_prop_uint64;
698*92192358STong Ho     trng_prop_uint64.realized_set_allowed = true;
699*92192358STong Ho     trng_props[0].info = &trng_prop_uint64;
700*92192358STong Ho 
701*92192358STong Ho     device_class_set_props(dc, trng_props);
702*92192358STong Ho }
703*92192358STong Ho 
704*92192358STong Ho static const TypeInfo trng_info = {
705*92192358STong Ho     .name          = TYPE_XLNX_VERSAL_TRNG,
706*92192358STong Ho     .parent        = TYPE_SYS_BUS_DEVICE,
707*92192358STong Ho     .instance_size = sizeof(XlnxVersalTRng),
708*92192358STong Ho     .class_init    = trng_class_init,
709*92192358STong Ho     .instance_init = trng_init,
710*92192358STong Ho };
711*92192358STong Ho 
712*92192358STong Ho static void trng_register_types(void)
713*92192358STong Ho {
714*92192358STong Ho     type_register_static(&trng_info);
715*92192358STong Ho }
716*92192358STong Ho 
717*92192358STong Ho type_init(trng_register_types)
718