xref: /openbmc/qemu/hw/nvram/xlnx-efuse.c (revision c2c1c4a3)
168fbcc34STong Ho /*
268fbcc34STong Ho  * QEMU model of the EFUSE eFuse
368fbcc34STong Ho  *
468fbcc34STong Ho  * Copyright (c) 2015 Xilinx Inc.
568fbcc34STong Ho  *
668fbcc34STong Ho  * Written by Edgar E. Iglesias <edgari@xilinx.com>
768fbcc34STong Ho  *
868fbcc34STong Ho  * Permission is hereby granted, free of charge, to any person obtaining a copy
968fbcc34STong Ho  * of this software and associated documentation files (the "Software"), to deal
1068fbcc34STong Ho  * in the Software without restriction, including without limitation the rights
1168fbcc34STong Ho  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1268fbcc34STong Ho  * copies of the Software, and to permit persons to whom the Software is
1368fbcc34STong Ho  * furnished to do so, subject to the following conditions:
1468fbcc34STong Ho  *
1568fbcc34STong Ho  * The above copyright notice and this permission notice shall be included in
1668fbcc34STong Ho  * all copies or substantial portions of the Software.
1768fbcc34STong Ho  *
1868fbcc34STong Ho  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1968fbcc34STong Ho  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2068fbcc34STong Ho  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2168fbcc34STong Ho  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2268fbcc34STong Ho  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2368fbcc34STong Ho  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2468fbcc34STong Ho  * THE SOFTWARE.
2568fbcc34STong Ho  */
2668fbcc34STong Ho 
2768fbcc34STong Ho #include "qemu/osdep.h"
2868fbcc34STong Ho #include "hw/nvram/xlnx-efuse.h"
2968fbcc34STong Ho 
3068fbcc34STong Ho #include "qemu/error-report.h"
3168fbcc34STong Ho #include "qemu/log.h"
3268fbcc34STong Ho #include "qapi/error.h"
3368fbcc34STong Ho #include "sysemu/blockdev.h"
3468fbcc34STong Ho #include "hw/qdev-properties.h"
3568fbcc34STong Ho #include "hw/qdev-properties-system.h"
3668fbcc34STong Ho 
3768fbcc34STong Ho #define TBIT0_OFFSET     28
3868fbcc34STong Ho #define TBIT1_OFFSET     29
3968fbcc34STong Ho #define TBIT2_OFFSET     30
4068fbcc34STong Ho #define TBIT3_OFFSET     31
4168fbcc34STong Ho #define TBITS_PATTERN    (0x0AU << TBIT0_OFFSET)
4268fbcc34STong Ho #define TBITS_MASK       (0x0FU << TBIT0_OFFSET)
4368fbcc34STong Ho 
4468fbcc34STong Ho bool xlnx_efuse_get_bit(XlnxEFuse *s, unsigned int bit)
4568fbcc34STong Ho {
4668fbcc34STong Ho     bool b = s->fuse32[bit / 32] & (1 << (bit % 32));
4768fbcc34STong Ho     return b;
4868fbcc34STong Ho }
4968fbcc34STong Ho 
5068fbcc34STong Ho static int efuse_bytes(XlnxEFuse *s)
5168fbcc34STong Ho {
5268fbcc34STong Ho     return ROUND_UP((s->efuse_nr * s->efuse_size) / 8, 4);
5368fbcc34STong Ho }
5468fbcc34STong Ho 
5568fbcc34STong Ho static int efuse_bdrv_read(XlnxEFuse *s, Error **errp)
5668fbcc34STong Ho {
5768fbcc34STong Ho     uint32_t *ram = s->fuse32;
5868fbcc34STong Ho     int nr = efuse_bytes(s);
5968fbcc34STong Ho 
6068fbcc34STong Ho     if (!s->blk) {
6168fbcc34STong Ho         return 0;
6268fbcc34STong Ho     }
6368fbcc34STong Ho 
6468fbcc34STong Ho     s->blk_ro = !blk_supports_write_perm(s->blk);
6568fbcc34STong Ho     if (!s->blk_ro) {
6668fbcc34STong Ho         int rc;
6768fbcc34STong Ho 
6868fbcc34STong Ho         rc = blk_set_perm(s->blk,
6968fbcc34STong Ho                           (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE),
7068fbcc34STong Ho                           BLK_PERM_ALL, NULL);
7168fbcc34STong Ho         if (rc) {
7268fbcc34STong Ho             s->blk_ro = true;
7368fbcc34STong Ho         }
7468fbcc34STong Ho     }
7568fbcc34STong Ho     if (s->blk_ro) {
7668fbcc34STong Ho         warn_report("%s: Skip saving updates to read-only eFUSE backstore.",
7768fbcc34STong Ho                     blk_name(s->blk));
7868fbcc34STong Ho     }
7968fbcc34STong Ho 
80a9262f55SAlberto Faria     if (blk_pread(s->blk, 0, nr, ram, 0) < 0) {
8168fbcc34STong Ho         error_setg(errp, "%s: Failed to read %u bytes from eFUSE backstore.",
8268fbcc34STong Ho                    blk_name(s->blk), nr);
8368fbcc34STong Ho         return -1;
8468fbcc34STong Ho     }
8568fbcc34STong Ho 
8668fbcc34STong Ho     /* Convert from little-endian backstore for each 32-bit row */
8768fbcc34STong Ho     nr /= 4;
8868fbcc34STong Ho     while (nr--) {
8968fbcc34STong Ho         ram[nr] = le32_to_cpu(ram[nr]);
9068fbcc34STong Ho     }
9168fbcc34STong Ho 
9268fbcc34STong Ho     return 0;
9368fbcc34STong Ho }
9468fbcc34STong Ho 
9568fbcc34STong Ho static void efuse_bdrv_sync(XlnxEFuse *s, unsigned int bit)
9668fbcc34STong Ho {
9768fbcc34STong Ho     unsigned int row_offset;
9868fbcc34STong Ho     uint32_t le32;
9968fbcc34STong Ho 
10068fbcc34STong Ho     if (!s->blk || s->blk_ro) {
10168fbcc34STong Ho         return;  /* Silent on read-only backend to avoid message flood */
10268fbcc34STong Ho     }
10368fbcc34STong Ho 
10468fbcc34STong Ho     /* Backstore is always in little-endian */
10568fbcc34STong Ho     le32 = cpu_to_le32(xlnx_efuse_get_row(s, bit));
10668fbcc34STong Ho 
10768fbcc34STong Ho     row_offset = (bit / 32) * 4;
108a9262f55SAlberto Faria     if (blk_pwrite(s->blk, row_offset, 4, &le32, 0) < 0) {
10968fbcc34STong Ho         error_report("%s: Failed to write offset %u of eFUSE backstore.",
11068fbcc34STong Ho                      blk_name(s->blk), row_offset);
11168fbcc34STong Ho     }
11268fbcc34STong Ho }
11368fbcc34STong Ho 
11468fbcc34STong Ho static int efuse_ro_bits_cmp(const void *a, const void *b)
11568fbcc34STong Ho {
11668fbcc34STong Ho     uint32_t i = *(const uint32_t *)a;
11768fbcc34STong Ho     uint32_t j = *(const uint32_t *)b;
11868fbcc34STong Ho 
11968fbcc34STong Ho     return (i > j) - (i < j);
12068fbcc34STong Ho }
12168fbcc34STong Ho 
12268fbcc34STong Ho static void efuse_ro_bits_sort(XlnxEFuse *s)
12368fbcc34STong Ho {
12468fbcc34STong Ho     uint32_t *ary = s->ro_bits;
12568fbcc34STong Ho     const uint32_t cnt = s->ro_bits_cnt;
12668fbcc34STong Ho 
12768fbcc34STong Ho     if (ary && cnt > 1) {
12868fbcc34STong Ho         qsort(ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp);
12968fbcc34STong Ho     }
13068fbcc34STong Ho }
13168fbcc34STong Ho 
13268fbcc34STong Ho static bool efuse_ro_bits_find(XlnxEFuse *s, uint32_t k)
13368fbcc34STong Ho {
13468fbcc34STong Ho     const uint32_t *ary = s->ro_bits;
13568fbcc34STong Ho     const uint32_t cnt = s->ro_bits_cnt;
13668fbcc34STong Ho 
13768fbcc34STong Ho     if (!ary || !cnt) {
13868fbcc34STong Ho         return false;
13968fbcc34STong Ho     }
14068fbcc34STong Ho 
14168fbcc34STong Ho     return bsearch(&k, ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp) != NULL;
14268fbcc34STong Ho }
14368fbcc34STong Ho 
14468fbcc34STong Ho bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit)
14568fbcc34STong Ho {
146*c2c1c4a3STong Ho     uint32_t set, *row;
147*c2c1c4a3STong Ho 
14868fbcc34STong Ho     if (efuse_ro_bits_find(s, bit)) {
149c4e4d0d9STong Ho         g_autofree char *path = object_get_canonical_path(OBJECT(s));
150c4e4d0d9STong Ho 
15168fbcc34STong Ho         qemu_log_mask(LOG_GUEST_ERROR, "%s: WARN: "
15268fbcc34STong Ho                       "Ignored setting of readonly efuse bit<%u,%u>!\n",
153c4e4d0d9STong Ho                       path, (bit / 32), (bit % 32));
15468fbcc34STong Ho         return false;
15568fbcc34STong Ho     }
15668fbcc34STong Ho 
157*c2c1c4a3STong Ho     /* Avoid back-end write unless there is a real update */
158*c2c1c4a3STong Ho     row = &s->fuse32[bit / 32];
159*c2c1c4a3STong Ho     set = 1 << (bit % 32);
160*c2c1c4a3STong Ho     if (!(set & *row)) {
161*c2c1c4a3STong Ho         *row |= set;
16268fbcc34STong Ho         efuse_bdrv_sync(s, bit);
163*c2c1c4a3STong Ho     }
16468fbcc34STong Ho     return true;
16568fbcc34STong Ho }
16668fbcc34STong Ho 
16768fbcc34STong Ho bool xlnx_efuse_k256_check(XlnxEFuse *s, uint32_t crc, unsigned start)
16868fbcc34STong Ho {
16968fbcc34STong Ho     uint32_t calc;
17068fbcc34STong Ho 
17168fbcc34STong Ho     /* A key always occupies multiple of whole rows */
17268fbcc34STong Ho     assert((start % 32) == 0);
17368fbcc34STong Ho 
17468fbcc34STong Ho     calc = xlnx_efuse_calc_crc(&s->fuse32[start / 32], (256 / 32), 0);
17568fbcc34STong Ho     return calc == crc;
17668fbcc34STong Ho }
17768fbcc34STong Ho 
17868fbcc34STong Ho uint32_t xlnx_efuse_tbits_check(XlnxEFuse *s)
17968fbcc34STong Ho {
18068fbcc34STong Ho     int nr;
18168fbcc34STong Ho     uint32_t check = 0;
18268fbcc34STong Ho 
18368fbcc34STong Ho     for (nr = s->efuse_nr; nr-- > 0; ) {
18468fbcc34STong Ho         int efuse_start_row_num = (s->efuse_size * nr) / 32;
18568fbcc34STong Ho         uint32_t data = s->fuse32[efuse_start_row_num];
18668fbcc34STong Ho 
18768fbcc34STong Ho         /*
18868fbcc34STong Ho          * If the option is on, auto-init blank T-bits.
18968fbcc34STong Ho          * (non-blank will still be reported as '0' in the check, e.g.,
19068fbcc34STong Ho          *  for error-injection tests)
19168fbcc34STong Ho          */
19268fbcc34STong Ho         if ((data & TBITS_MASK) == 0 && s->init_tbits) {
19368fbcc34STong Ho             data |= TBITS_PATTERN;
19468fbcc34STong Ho 
19568fbcc34STong Ho             s->fuse32[efuse_start_row_num] = data;
19668fbcc34STong Ho             efuse_bdrv_sync(s, (efuse_start_row_num * 32 + TBIT0_OFFSET));
19768fbcc34STong Ho         }
19868fbcc34STong Ho 
19968fbcc34STong Ho         check = (check << 1) | ((data & TBITS_MASK) == TBITS_PATTERN);
20068fbcc34STong Ho     }
20168fbcc34STong Ho 
20268fbcc34STong Ho     return check;
20368fbcc34STong Ho }
20468fbcc34STong Ho 
20568fbcc34STong Ho static void efuse_realize(DeviceState *dev, Error **errp)
20668fbcc34STong Ho {
20768fbcc34STong Ho     XlnxEFuse *s = XLNX_EFUSE(dev);
20868fbcc34STong Ho 
20968fbcc34STong Ho     /* Sort readonly-list for bsearch lookup */
21068fbcc34STong Ho     efuse_ro_bits_sort(s);
21168fbcc34STong Ho 
21268fbcc34STong Ho     if ((s->efuse_size % 32) != 0) {
213c4e4d0d9STong Ho         g_autofree char *path = object_get_canonical_path(OBJECT(s));
214c4e4d0d9STong Ho 
21568fbcc34STong Ho         error_setg(errp,
21668fbcc34STong Ho                    "%s.efuse-size: %u: property value not multiple of 32.",
217c4e4d0d9STong Ho                    path, s->efuse_size);
21868fbcc34STong Ho         return;
21968fbcc34STong Ho     }
22068fbcc34STong Ho 
22168fbcc34STong Ho     s->fuse32 = g_malloc0(efuse_bytes(s));
22268fbcc34STong Ho     if (efuse_bdrv_read(s, errp)) {
22368fbcc34STong Ho         g_free(s->fuse32);
22468fbcc34STong Ho     }
22568fbcc34STong Ho }
22668fbcc34STong Ho 
22768fbcc34STong Ho static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name,
22868fbcc34STong Ho                                  void *opaque, Error **errp)
22968fbcc34STong Ho {
23068fbcc34STong Ho     DeviceState *dev = DEVICE(obj);
23168fbcc34STong Ho 
23268fbcc34STong Ho     qdev_prop_drive.set(obj, v, name, opaque, errp);
23368fbcc34STong Ho 
23468fbcc34STong Ho     /* Fill initial data if backend is attached after realized */
23568fbcc34STong Ho     if (dev->realized) {
23668fbcc34STong Ho         efuse_bdrv_read(XLNX_EFUSE(obj), errp);
23768fbcc34STong Ho     }
23868fbcc34STong Ho }
23968fbcc34STong Ho 
24068fbcc34STong Ho static void efuse_prop_get_drive(Object *obj, Visitor *v, const char *name,
24168fbcc34STong Ho                                  void *opaque, Error **errp)
24268fbcc34STong Ho {
24368fbcc34STong Ho     qdev_prop_drive.get(obj, v, name, opaque, errp);
24468fbcc34STong Ho }
24568fbcc34STong Ho 
24668fbcc34STong Ho static void efuse_prop_release_drive(Object *obj, const char *name,
24768fbcc34STong Ho                                      void *opaque)
24868fbcc34STong Ho {
24968fbcc34STong Ho     qdev_prop_drive.release(obj, name, opaque);
25068fbcc34STong Ho }
25168fbcc34STong Ho 
25268fbcc34STong Ho static const PropertyInfo efuse_prop_drive = {
25368fbcc34STong Ho     .name  = "str",
25468fbcc34STong Ho     .description = "Node name or ID of a block device to use as eFUSE backend",
25568fbcc34STong Ho     .realized_set_allowed = true,
25668fbcc34STong Ho     .get = efuse_prop_get_drive,
25768fbcc34STong Ho     .set = efuse_prop_set_drive,
25868fbcc34STong Ho     .release = efuse_prop_release_drive,
25968fbcc34STong Ho };
26068fbcc34STong Ho 
26168fbcc34STong Ho static Property efuse_properties[] = {
26268fbcc34STong Ho     DEFINE_PROP("drive", XlnxEFuse, blk, efuse_prop_drive, BlockBackend *),
26368fbcc34STong Ho     DEFINE_PROP_UINT8("efuse-nr", XlnxEFuse, efuse_nr, 3),
26468fbcc34STong Ho     DEFINE_PROP_UINT32("efuse-size", XlnxEFuse, efuse_size, 64 * 32),
26568fbcc34STong Ho     DEFINE_PROP_BOOL("init-factory-tbits", XlnxEFuse, init_tbits, true),
26668fbcc34STong Ho     DEFINE_PROP_ARRAY("read-only", XlnxEFuse, ro_bits_cnt, ro_bits,
26768fbcc34STong Ho                       qdev_prop_uint32, uint32_t),
26868fbcc34STong Ho     DEFINE_PROP_END_OF_LIST(),
26968fbcc34STong Ho };
27068fbcc34STong Ho 
27168fbcc34STong Ho static void efuse_class_init(ObjectClass *klass, void *data)
27268fbcc34STong Ho {
27368fbcc34STong Ho     DeviceClass *dc = DEVICE_CLASS(klass);
27468fbcc34STong Ho 
27568fbcc34STong Ho     dc->realize = efuse_realize;
27668fbcc34STong Ho     device_class_set_props(dc, efuse_properties);
27768fbcc34STong Ho }
27868fbcc34STong Ho 
27968fbcc34STong Ho static const TypeInfo efuse_info = {
28068fbcc34STong Ho     .name          = TYPE_XLNX_EFUSE,
28168fbcc34STong Ho     .parent        = TYPE_DEVICE,
28268fbcc34STong Ho     .instance_size = sizeof(XlnxEFuse),
28368fbcc34STong Ho     .class_init    = efuse_class_init,
28468fbcc34STong Ho };
28568fbcc34STong Ho 
28668fbcc34STong Ho static void efuse_register_types(void)
28768fbcc34STong Ho {
28868fbcc34STong Ho     type_register_static(&efuse_info);
28968fbcc34STong Ho }
29068fbcc34STong Ho type_init(efuse_register_types)
291