qemu

Форк
0
/
npcm7xx_otp.c 
440 строк · 12.9 Кб
1
/*
2
 * Nuvoton NPCM7xx OTP (Fuse Array) Interface
3
 *
4
 * Copyright 2020 Google LLC
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
 * for more details.
15
 */
16

17
#include "qemu/osdep.h"
18

19
#include "hw/nvram/npcm7xx_otp.h"
20
#include "migration/vmstate.h"
21
#include "qapi/error.h"
22
#include "qemu/bitops.h"
23
#include "qemu/log.h"
24
#include "qemu/module.h"
25
#include "qemu/units.h"
26

27
/* Each module has 4 KiB of register space. Only a fraction of it is used. */
28
#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
29

30
/* 32-bit register indices. */
31
typedef enum NPCM7xxOTPRegister {
32
    NPCM7XX_OTP_FST,
33
    NPCM7XX_OTP_FADDR,
34
    NPCM7XX_OTP_FDATA,
35
    NPCM7XX_OTP_FCFG,
36
    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
37
    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
38
    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
39
    NPCM7XX_OTP_FCTL,
40
    NPCM7XX_OTP_REGS_END,
41
} NPCM7xxOTPRegister;
42

43
/* Register field definitions. */
44
#define FST_RIEN BIT(2)
45
#define FST_RDST BIT(1)
46
#define FST_RDY BIT(0)
47
#define FST_RO_MASK (FST_RDST | FST_RDY)
48

49
#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
50
#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
51

52
#define FDATA_CLEAR 0x00000001
53

54
#define FCFG_FDIS BIT(31)
55
#define FCFG_FCFGLK_MASK 0x00ff0000
56

57
#define FCTL_PROG_CMD1 0x00000001
58
#define FCTL_PROG_CMD2 0xbf79e5d0
59
#define FCTL_READ_CMD 0x00000002
60

61
/**
62
 * struct NPCM7xxOTPClass - OTP module class.
63
 * @parent: System bus device class.
64
 * @mmio_ops: MMIO register operations for this type of module.
65
 *
66
 * The two OTP modules (key-storage and fuse-array) have slightly different
67
 * behavior, so we give them different MMIO register operations.
68
 */
69
struct NPCM7xxOTPClass {
70
    SysBusDeviceClass parent;
71

72
    const MemoryRegionOps *mmio_ops;
73
};
74

75
#define NPCM7XX_OTP_CLASS(klass) \
76
    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
77
#define NPCM7XX_OTP_GET_CLASS(obj) \
78
    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
79

80
static uint8_t ecc_encode_nibble(uint8_t n)
81
{
82
    uint8_t result = n;
83

84
    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
85
    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
86
    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
87
    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
88

89
    return result;
90
}
91

92
void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
93
                             unsigned int offset, unsigned int len)
94
{
95
    const uint8_t *src = data;
96
    uint8_t *dst = &s->array[offset];
97

98
    while (len-- > 0) {
99
        uint8_t c = *src++;
100

101
        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
102
        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
103
    }
104
}
105

106
/* Common register read handler for both OTP classes. */
107
static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
108
{
109
    uint32_t value = 0;
110

111
    switch (reg) {
112
    case NPCM7XX_OTP_FST:
113
    case NPCM7XX_OTP_FADDR:
114
    case NPCM7XX_OTP_FDATA:
115
    case NPCM7XX_OTP_FCFG:
116
        value = s->regs[reg];
117
        break;
118

119
    case NPCM7XX_OTP_FCTL:
120
        qemu_log_mask(LOG_GUEST_ERROR,
121
                      "%s: read from write-only FCTL register\n",
122
                      DEVICE(s)->canonical_path);
123
        break;
124

125
    default:
126
        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
127
                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
128
        break;
129
    }
130

131
    return value;
132
}
133

134
/* Read a byte from the OTP array into the data register. */
135
static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
136
{
137
    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
138

139
    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
140
    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
141
}
142

143
/* Program a byte from the data register into the OTP array. */
144
static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
145
{
146
    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
147

148
    /* Bits can only go 0->1, never 1->0. */
149
    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
150
    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
151
}
152

153
/* Compute the next value of the FCFG register. */
154
static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
155
{
156
    uint32_t lock_mask;
157
    uint32_t value;
158

159
    /*
160
     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
161
     * and FRDLK (0..7) that are read-only.
162
     */
163
    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
164
    lock_mask |= lock_mask >> 8;
165
    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
166
    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
167
    /* Preserve read-only bits in FPRGLK and FRDLK */
168
    value |= cur_value & lock_mask;
169
    /* Set all bits that aren't read-only. */
170
    value |= new_value & ~lock_mask;
171

172
    return value;
173
}
174

175
/* Common register write handler for both OTP classes. */
176
static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
177
                              uint32_t value)
178
{
179
    switch (reg) {
180
    case NPCM7XX_OTP_FST:
181
        /* RDST is cleared by writing 1 to it. */
182
        if (value & FST_RDST) {
183
            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
184
        }
185
        /* Preserve read-only and write-one-to-clear bits */
186
        value &= ~FST_RO_MASK;
187
        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
188
        break;
189

190
    case NPCM7XX_OTP_FADDR:
191
        break;
192

193
    case NPCM7XX_OTP_FDATA:
194
        /*
195
         * This register is cleared by writing a magic value to it; no other
196
         * values can be written.
197
         */
198
        if (value == FDATA_CLEAR) {
199
            value = 0;
200
        } else {
201
            value = s->regs[NPCM7XX_OTP_FDATA];
202
        }
203
        break;
204

205
    case NPCM7XX_OTP_FCFG:
206
        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
207
        break;
208

209
    case NPCM7XX_OTP_FCTL:
210
        switch (value) {
211
        case FCTL_READ_CMD:
212
            npcm7xx_otp_read_array(s);
213
            break;
214

215
        case FCTL_PROG_CMD1:
216
            /*
217
             * Programming requires writing two separate magic values to this
218
             * register; this is the first one. Just store it so it can be
219
             * verified later when the second magic value is received.
220
             */
221
            break;
222

223
        case FCTL_PROG_CMD2:
224
            /*
225
             * Only initiate programming if we received the first half of the
226
             * command immediately before this one.
227
             */
228
            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
229
                npcm7xx_otp_program_array(s);
230
            }
231
            break;
232

233
        default:
234
            qemu_log_mask(LOG_GUEST_ERROR,
235
                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
236
                          DEVICE(s)->canonical_path, value);
237
            break;
238
        }
239
        if (value != FCTL_PROG_CMD1) {
240
            value = 0;
241
        }
242
        break;
243

244
    default:
245
        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
246
                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
247
        return;
248
    }
249

250
    s->regs[reg] = value;
251
}
252

253
/* Register read handler specific to the fuse array OTP module. */
254
static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
255
                                        unsigned int size)
256
{
257
    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
258
    NPCM7xxOTPState *s = opaque;
259
    uint32_t value;
260

261
    /*
262
     * Only the Fuse Strap register needs special handling; all other registers
263
     * work the same way for both kinds of OTP modules.
264
     */
265
    if (reg != NPCM7XX_OTP_FUSTRAP) {
266
        value = npcm7xx_otp_read(s, reg);
267
    } else {
268
        /* FUSTRAP is stored as three copies in the OTP array. */
269
        uint32_t fustrap[3];
270

271
        memcpy(fustrap, &s->array[0], sizeof(fustrap));
272

273
        /* Determine value by a majority vote on each bit. */
274
        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
275
                (fustrap[1] & fustrap[2]);
276
    }
277

278
    return value;
279
}
280

281
/* Register write handler specific to the fuse array OTP module. */
282
static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
283
                                     unsigned int size)
284
{
285
    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
286
    NPCM7xxOTPState *s = opaque;
287

288
    /*
289
     * The Fuse Strap register is read-only. Other registers are handled by
290
     * common code.
291
     */
292
    if (reg != NPCM7XX_OTP_FUSTRAP) {
293
        npcm7xx_otp_write(s, reg, v);
294
    }
295
}
296

297
static const MemoryRegionOps npcm7xx_fuse_array_ops = {
298
    .read       = npcm7xx_fuse_array_read,
299
    .write      = npcm7xx_fuse_array_write,
300
    .endianness = DEVICE_LITTLE_ENDIAN,
301
    .valid      = {
302
        .min_access_size        = 4,
303
        .max_access_size        = 4,
304
        .unaligned              = false,
305
    },
306
};
307

308
/* Register read handler specific to the key storage OTP module. */
309
static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
310
                                         unsigned int size)
311
{
312
    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
313
    NPCM7xxOTPState *s = opaque;
314

315
    /*
316
     * Only the Fuse Key Index register needs special handling; all other
317
     * registers work the same way for both kinds of OTP modules.
318
     */
319
    if (reg != NPCM7XX_OTP_FKEYIND) {
320
        return npcm7xx_otp_read(s, reg);
321
    }
322

323
    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
324

325
    return s->regs[NPCM7XX_OTP_FKEYIND];
326
}
327

328
/* Register write handler specific to the key storage OTP module. */
329
static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
330
                                      unsigned int size)
331
{
332
    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
333
    NPCM7xxOTPState *s = opaque;
334

335
    /*
336
     * Only the Fuse Key Index register needs special handling; all other
337
     * registers work the same way for both kinds of OTP modules.
338
     */
339
    if (reg != NPCM7XX_OTP_FKEYIND) {
340
        npcm7xx_otp_write(s, reg, v);
341
        return;
342
    }
343

344
    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
345

346
    s->regs[NPCM7XX_OTP_FKEYIND] = v;
347
}
348

349
static const MemoryRegionOps npcm7xx_key_storage_ops = {
350
    .read       = npcm7xx_key_storage_read,
351
    .write      = npcm7xx_key_storage_write,
352
    .endianness = DEVICE_LITTLE_ENDIAN,
353
    .valid      = {
354
        .min_access_size        = 4,
355
        .max_access_size        = 4,
356
        .unaligned              = false,
357
    },
358
};
359

360
static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
361
{
362
    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
363

364
    memset(s->regs, 0, sizeof(s->regs));
365

366
    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
367
    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
368
}
369

370
static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
371
{
372
    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
373
    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
374
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
375

376
    memset(s->array, 0, sizeof(s->array));
377

378
    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
379
                          NPCM7XX_OTP_REGS_SIZE);
380
    sysbus_init_mmio(sbd, &s->mmio);
381
}
382

383
static const VMStateDescription vmstate_npcm7xx_otp = {
384
    .name = "npcm7xx-otp",
385
    .version_id = 0,
386
    .minimum_version_id = 0,
387
    .fields = (const VMStateField[]) {
388
        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
389
        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
390
        VMSTATE_END_OF_LIST(),
391
    },
392
};
393

394
static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
395
{
396
    ResettableClass *rc = RESETTABLE_CLASS(klass);
397
    DeviceClass *dc = DEVICE_CLASS(klass);
398

399
    QEMU_BUILD_BUG_ON(NPCM7XX_OTP_REGS_END > NPCM7XX_OTP_NR_REGS);
400

401
    dc->realize = npcm7xx_otp_realize;
402
    dc->vmsd = &vmstate_npcm7xx_otp;
403
    rc->phases.enter = npcm7xx_otp_enter_reset;
404
}
405

406
static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
407
{
408
    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
409

410
    oc->mmio_ops = &npcm7xx_key_storage_ops;
411
}
412

413
static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
414
{
415
    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
416

417
    oc->mmio_ops = &npcm7xx_fuse_array_ops;
418
}
419

420
static const TypeInfo npcm7xx_otp_types[] = {
421
    {
422
        .name = TYPE_NPCM7XX_OTP,
423
        .parent = TYPE_SYS_BUS_DEVICE,
424
        .instance_size = sizeof(NPCM7xxOTPState),
425
        .class_size = sizeof(NPCM7xxOTPClass),
426
        .class_init = npcm7xx_otp_class_init,
427
        .abstract = true,
428
    },
429
    {
430
        .name = TYPE_NPCM7XX_KEY_STORAGE,
431
        .parent = TYPE_NPCM7XX_OTP,
432
        .class_init = npcm7xx_key_storage_class_init,
433
    },
434
    {
435
        .name = TYPE_NPCM7XX_FUSE_ARRAY,
436
        .parent = TYPE_NPCM7XX_OTP,
437
        .class_init = npcm7xx_fuse_array_class_init,
438
    },
439
};
440
DEFINE_TYPES(npcm7xx_otp_types);
441

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.