summaryrefslogtreecommitdiffstats
path: root/hw/misc/imx_rngc.c
blob: 632c03779cbbf4bab04b138150bf66de809639d4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
 * Freescale i.MX RNGC emulation
 *
 * Copyright (C) 2020 Martin Kaiser <martin@kaiser.cx>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 *
 * This driver provides the minimum functionality to initialize and seed
 * an rngc and to read random numbers. The rngb that is found in imx25
 * chipsets is also supported.
 */

#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/guest-random.h"
#include "hw/irq.h"
#include "hw/misc/imx_rngc.h"
#include "migration/vmstate.h"

#define RNGC_NAME "i.MX RNGC"

#define RNGC_VER_ID  0x00
#define RNGC_COMMAND 0x04
#define RNGC_CONTROL 0x08
#define RNGC_STATUS  0x0C
#define RNGC_FIFO    0x14

/* These version info are reported by the rngb in an imx258 chip. */
#define RNG_TYPE_RNGB 0x1
#define V_MAJ 0x2
#define V_MIN 0x40

#define RNGC_CMD_BIT_SW_RST    0x40
#define RNGC_CMD_BIT_CLR_ERR   0x20
#define RNGC_CMD_BIT_CLR_INT   0x10
#define RNGC_CMD_BIT_SEED      0x02
#define RNGC_CMD_BIT_SELF_TEST 0x01

#define RNGC_CTRL_BIT_MASK_ERR  0x40
#define RNGC_CTRL_BIT_MASK_DONE 0x20
#define RNGC_CTRL_BIT_AUTO_SEED 0x10

/* the current status for self-test and seed operations */
#define OP_IDLE 0
#define OP_RUN  1
#define OP_DONE 2

static uint64_t imx_rngc_read(void *opaque, hwaddr offset, unsigned size)
{
    IMXRNGCState *s = IMX_RNGC(opaque);
    uint64_t val = 0;

    switch (offset) {
    case RNGC_VER_ID:
        val |= RNG_TYPE_RNGB << 28 | V_MAJ << 8 | V_MIN;
        break;

    case RNGC_COMMAND:
        if (s->op_seed == OP_RUN) {
            val |= RNGC_CMD_BIT_SEED;
        }
        if (s->op_self_test == OP_RUN) {
            val |= RNGC_CMD_BIT_SELF_TEST;
        }
        break;

    case RNGC_CONTROL:
        /*
         * The CTL_ACC and VERIF_MODE bits are not supported yet.
         * They read as 0.
         */
        val |= s->mask;
        if (s->auto_seed) {
            val |= RNGC_CTRL_BIT_AUTO_SEED;
        }
        /*
         * We don't have an internal fifo like the real hardware.
         * There's no need for strategy to handle fifo underflows.
         * We return the FIFO_UFLOW_RESPONSE bits as 0.
         */
        break;

    case RNGC_STATUS:
        /*
         * We never report any statistics test or self-test errors or any
         * other errors. STAT_TEST_PF, ST_PF and ERROR are always 0.
         */

        /*
         * We don't have an internal fifo, see above. Therefore, we
         * report back the default fifo size (5 32-bit words) and
         * indicate that our fifo is always full.
         */
        val |= 5 << 12 | 5 << 8;

        /* We always have a new seed available. */
        val |= 1 << 6;

        if (s->op_seed == OP_DONE) {
            val |= 1 << 5;
        }
        if (s->op_self_test == OP_DONE) {
            val |= 1 << 4;
        }
        if (s->op_seed == OP_RUN || s->op_self_test == OP_RUN) {
            /*
             * We're busy if self-test is running or if we're
             * seeding the prng.
             */
            val |= 1 << 1;
        } else {
            /*
             * We're ready to provide secure random numbers whenever
             * we're not busy.
             */
            val |= 1;
        }
        break;

    case RNGC_FIFO:
        qemu_guest_getrandom_nofail(&val, sizeof(val));
        break;
    }

    return val;
}

static void imx_rngc_do_reset(IMXRNGCState *s)
{
    s->op_self_test = OP_IDLE;
    s->op_seed = OP_IDLE;
    s->mask = 0;
    s->auto_seed = false;
}

static void imx_rngc_write(void *opaque, hwaddr offset, uint64_t value,
                           unsigned size)
{
    IMXRNGCState *s = IMX_RNGC(opaque);

    switch (offset) {
    case RNGC_COMMAND:
        if (value & RNGC_CMD_BIT_SW_RST) {
            imx_rngc_do_reset(s);
        }

        /*
         * For now, both CLR_ERR and CLR_INT clear the interrupt. We
         * don't report any errors yet.
         */
        if (value & (RNGC_CMD_BIT_CLR_ERR | RNGC_CMD_BIT_CLR_INT)) {
            qemu_irq_lower(s->irq);
        }

        if (value & RNGC_CMD_BIT_SEED) {
            s->op_seed = OP_RUN;
            qemu_bh_schedule(s->seed_bh);
        }

        if (value & RNGC_CMD_BIT_SELF_TEST) {
            s->op_self_test = OP_RUN;
            qemu_bh_schedule(s->self_test_bh);
        }
        break;

    case RNGC_CONTROL:
        /*
         * The CTL_ACC and VERIF_MODE bits are not supported yet.
         * We ignore them if they're set by the caller.
         */

        if (value & RNGC_CTRL_BIT_MASK_ERR) {
            s->mask |= RNGC_CTRL_BIT_MASK_ERR;
        } else {
            s->mask &= ~RNGC_CTRL_BIT_MASK_ERR;
        }

        if (value & RNGC_CTRL_BIT_MASK_DONE) {
            s->mask |= RNGC_CTRL_BIT_MASK_DONE;
        } else {
            s->mask &= ~RNGC_CTRL_BIT_MASK_DONE;
        }

        if (value & RNGC_CTRL_BIT_AUTO_SEED) {
            s->auto_seed = true;
        } else {
            s->auto_seed = false;
        }
        break;
    }
}

static const MemoryRegionOps imx_rngc_ops = {
    .read  = imx_rngc_read,
    .write = imx_rngc_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

static void imx_rngc_self_test(void *opaque)
{
    IMXRNGCState *s = IMX_RNGC(opaque);

    s->op_self_test = OP_DONE;
    if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) {
        qemu_irq_raise(s->irq);
    }
}

static void imx_rngc_seed(void *opaque)
{
    IMXRNGCState *s = IMX_RNGC(opaque);

    s->op_seed = OP_DONE;
    if (!(s->mask & RNGC_CTRL_BIT_MASK_DONE)) {
        qemu_irq_raise(s->irq);
    }
}

static void imx_rngc_realize(DeviceState *dev, Error **errp)
{
    IMXRNGCState *s = IMX_RNGC(dev);
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);

    memory_region_init_io(&s->iomem, OBJECT(s), &imx_rngc_ops, s,
                          TYPE_IMX_RNGC, 0x1000);
    sysbus_init_mmio(sbd, &s->iomem);

    sysbus_init_irq(sbd, &s->irq);
    s->self_test_bh = qemu_bh_new(imx_rngc_self_test, s);
    s->seed_bh = qemu_bh_new(imx_rngc_seed, s);
}

static void imx_rngc_reset(DeviceState *dev)
{
    IMXRNGCState *s = IMX_RNGC(dev);

    imx_rngc_do_reset(s);
}

static const VMStateDescription vmstate_imx_rngc = {
    .name = RNGC_NAME,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT8(op_self_test, IMXRNGCState),
        VMSTATE_UINT8(op_seed, IMXRNGCState),
        VMSTATE_UINT8(mask, IMXRNGCState),
        VMSTATE_BOOL(auto_seed, IMXRNGCState),
        VMSTATE_END_OF_LIST()
    }
};

static void imx_rngc_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->realize = imx_rngc_realize;
    dc->reset = imx_rngc_reset;
    dc->desc = RNGC_NAME,
    dc->vmsd = &vmstate_imx_rngc;
}

static const TypeInfo imx_rngc_info = {
    .name          = TYPE_IMX_RNGC,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(IMXRNGCState),
    .class_init    = imx_rngc_class_init,
};

static void imx_rngc_register_types(void)
{
    type_register_static(&imx_rngc_info);
}

type_init(imx_rngc_register_types)