| 1 | /* |
| 2 | * QEMU model of the EFUSE eFuse |
| 3 | * |
| 4 | * Copyright (c) 2015 Xilinx Inc. |
| 5 | * |
| 6 | * Written by Edgar E. Iglesias <edgari@xilinx.com> |
| 7 | * |
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | * of this software and associated documentation files (the "Software"), to deal |
| 10 | * in the Software without restriction, including without limitation the rights |
| 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | * copies of the Software, and to permit persons to whom the Software is |
| 13 | * furnished to do so, subject to the following conditions: |
| 14 | * |
| 15 | * The above copyright notice and this permission notice shall be included in |
| 16 | * all copies or substantial portions of the Software. |
| 17 | * |
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | * THE SOFTWARE. |
| 25 | */ |
| 26 | |
| 27 | #include "qemu/osdep.h" |
| 28 | #include "hw/nvram/xlnx-efuse.h" |
| 29 | |
| 30 | #include "qemu/bswap.h" |
| 31 | #include "qemu/error-report.h" |
| 32 | #include "qemu/log.h" |
| 33 | #include "qapi/error.h" |
| 34 | #include "system/blockdev.h" |
| 35 | #include "hw/core/qdev-properties.h" |
| 36 | #include "hw/core/qdev-properties-system.h" |
| 37 | |
| 38 | #define TBIT0_OFFSET 28 |
| 39 | #define TBIT1_OFFSET 29 |
| 40 | #define TBIT2_OFFSET 30 |
| 41 | #define TBIT3_OFFSET 31 |
| 42 | #define TBITS_PATTERN (0x0AU << TBIT0_OFFSET) |
| 43 | #define TBITS_MASK (0x0FU << TBIT0_OFFSET) |
| 44 | |
| 45 | bool xlnx_efuse_get_bit(XlnxEFuse *s, unsigned int bit) |
| 46 | { |
| 47 | bool b = s->fuse32[bit / 32] & (1 << (bit % 32)); |
| 48 | return b; |
| 49 | } |
| 50 | |
| 51 | static int efuse_bytes(XlnxEFuse *s) |
| 52 | { |
| 53 | return ROUND_UP((s->efuse_nr * s->efuse_size) / 8, 4); |
| 54 | } |
| 55 | |
| 56 | static int efuse_bdrv_read(XlnxEFuse *s, Error **errp) |
| 57 | { |
| 58 | uint32_t *ram = s->fuse32; |
| 59 | int nr = efuse_bytes(s); |
| 60 | |
| 61 | if (!s->blk) { |
| 62 | return 0; |
| 63 | } |
| 64 | |
| 65 | s->blk_ro = !blk_supports_write_perm(s->blk); |
| 66 | if (!s->blk_ro) { |
| 67 | int rc; |
| 68 | |
| 69 | rc = blk_set_perm(s->blk, |
| 70 | (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), |
| 71 | BLK_PERM_ALL, NULL); |
| 72 | if (rc) { |
| 73 | s->blk_ro = true; |
| 74 | } |
| 75 | } |
| 76 | if (s->blk_ro) { |
| 77 | warn_report("%s: Skip saving updates to read-only eFUSE backstore.", |
| 78 | blk_name(s->blk)); |
| 79 | } |
| 80 | |
| 81 | if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { |
| 82 | error_setg(errp, "%s: Failed to read %u bytes from eFUSE backstore.", |
| 83 | blk_name(s->blk), nr); |
| 84 | return -1; |
| 85 | } |
| 86 | |
| 87 | /* Convert from little-endian backstore for each 32-bit row */ |
| 88 | nr /= 4; |
| 89 | while (nr--) { |
| 90 | ram[nr] = le32_to_cpu(ram[nr]); |
| 91 | } |
| 92 | |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | static void efuse_bdrv_sync(XlnxEFuse *s, unsigned int bit) |
| 97 | { |
| 98 | unsigned int row_offset; |
| 99 | uint32_t le32; |
| 100 | |
| 101 | if (!s->blk || s->blk_ro) { |
| 102 | return; /* Silent on read-only backend to avoid message flood */ |
| 103 | } |
| 104 | |
| 105 | /* Backstore is always in little-endian */ |
| 106 | le32 = cpu_to_le32(xlnx_efuse_get_row(s, bit)); |
| 107 | |
| 108 | row_offset = (bit / 32) * 4; |
| 109 | if (blk_pwrite(s->blk, row_offset, 4, &le32, 0) < 0) { |
| 110 | error_report("%s: Failed to write offset %u of eFUSE backstore.", |
| 111 | blk_name(s->blk), row_offset); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | static int efuse_ro_bits_cmp(const void *a, const void *b) |
| 116 | { |
| 117 | uint32_t i = *(const uint32_t *)a; |
| 118 | uint32_t j = *(const uint32_t *)b; |
| 119 | |
| 120 | return (i > j) - (i < j); |
| 121 | } |
| 122 | |
| 123 | static void efuse_ro_bits_sort(XlnxEFuse *s) |
| 124 | { |
| 125 | uint32_t *ary = s->ro_bits; |
| 126 | const uint32_t cnt = s->ro_bits_cnt; |
| 127 | |
| 128 | if (ary && cnt > 1) { |
| 129 | qsort(ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | static bool efuse_ro_bits_find(XlnxEFuse *s, uint32_t k) |
| 134 | { |
| 135 | const uint32_t *ary = s->ro_bits; |
| 136 | const uint32_t cnt = s->ro_bits_cnt; |
| 137 | |
| 138 | if (!ary || !cnt) { |
| 139 | return false; |
| 140 | } |
| 141 | |
| 142 | return bsearch(&k, ary, cnt, sizeof(ary[0]), efuse_ro_bits_cmp) != NULL; |
| 143 | } |
| 144 | |
| 145 | bool xlnx_efuse_set_bit(XlnxEFuse *s, unsigned int bit) |
| 146 | { |
| 147 | uint32_t set, *row; |
| 148 | |
| 149 | if (efuse_ro_bits_find(s, bit)) { |
| 150 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); |
| 151 | |
| 152 | qemu_log_mask(LOG_GUEST_ERROR, "%s: WARN: " |
| 153 | "Ignored setting of readonly efuse bit<%u,%u>!\n", |
| 154 | path, (bit / 32), (bit % 32)); |
| 155 | return false; |
| 156 | } |
| 157 | |
| 158 | /* Avoid back-end write unless there is a real update */ |
| 159 | row = &s->fuse32[bit / 32]; |
| 160 | set = 1 << (bit % 32); |
| 161 | if (!(set & *row)) { |
| 162 | *row |= set; |
| 163 | efuse_bdrv_sync(s, bit); |
| 164 | } |
| 165 | return true; |
| 166 | } |
| 167 | |
| 168 | bool xlnx_efuse_k256_check(XlnxEFuse *s, uint32_t crc, unsigned start) |
| 169 | { |
| 170 | uint32_t calc; |
| 171 | |
| 172 | /* A key always occupies multiple of whole rows */ |
| 173 | assert((start % 32) == 0); |
| 174 | |
| 175 | calc = xlnx_efuse_calc_crc(&s->fuse32[start / 32], (256 / 32), 0); |
| 176 | return calc == crc; |
| 177 | } |
| 178 | |
| 179 | uint32_t xlnx_efuse_tbits_check(XlnxEFuse *s) |
| 180 | { |
| 181 | int nr; |
| 182 | uint32_t check = 0; |
| 183 | |
| 184 | for (nr = s->efuse_nr; nr-- > 0; ) { |
| 185 | int efuse_start_row_num = (s->efuse_size * nr) / 32; |
| 186 | uint32_t data = s->fuse32[efuse_start_row_num]; |
| 187 | |
| 188 | /* |
| 189 | * If the option is on, auto-init blank T-bits. |
| 190 | * (non-blank will still be reported as '0' in the check, e.g., |
| 191 | * for error-injection tests) |
| 192 | */ |
| 193 | if ((data & TBITS_MASK) == 0 && s->init_tbits) { |
| 194 | data |= TBITS_PATTERN; |
| 195 | |
| 196 | s->fuse32[efuse_start_row_num] = data; |
| 197 | efuse_bdrv_sync(s, (efuse_start_row_num * 32 + TBIT0_OFFSET)); |
| 198 | } |
| 199 | |
| 200 | check = (check << 1) | ((data & TBITS_MASK) == TBITS_PATTERN); |
| 201 | } |
| 202 | |
| 203 | return check; |
| 204 | } |
| 205 | |
| 206 | static void efuse_realize(DeviceState *dev, Error **errp) |
| 207 | { |
| 208 | XlnxEFuse *s = XLNX_EFUSE(dev); |
| 209 | |
| 210 | /* Sort readonly-list for bsearch lookup */ |
| 211 | efuse_ro_bits_sort(s); |
| 212 | |
| 213 | if ((s->efuse_size % 32) != 0) { |
| 214 | g_autofree char *path = object_get_canonical_path(OBJECT(s)); |
| 215 | |
| 216 | error_setg(errp, |
| 217 | "%s.efuse-size: %u: property value not multiple of 32.", |
| 218 | path, s->efuse_size); |
| 219 | return; |
| 220 | } |
| 221 | |
| 222 | s->fuse32 = g_malloc0(efuse_bytes(s)); |
| 223 | if (efuse_bdrv_read(s, errp)) { |
| 224 | g_free(s->fuse32); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name, |
| 229 | void *opaque, Error **errp) |
| 230 | { |
| 231 | DeviceState *dev = DEVICE(obj); |
| 232 | |
| 233 | qdev_prop_drive.set(obj, v, name, opaque, errp); |
| 234 | |
| 235 | /* Fill initial data if backend is attached after realized */ |
| 236 | if (qdev_is_realized(dev)) { |
| 237 | efuse_bdrv_read(XLNX_EFUSE(obj), errp); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | static void efuse_prop_get_drive(Object *obj, Visitor *v, const char *name, |
| 242 | void *opaque, Error **errp) |
| 243 | { |
| 244 | qdev_prop_drive.get(obj, v, name, opaque, errp); |
| 245 | } |
| 246 | |
| 247 | static void efuse_prop_release_drive(Object *obj, const char *name, |
| 248 | void *opaque) |
| 249 | { |
| 250 | qdev_prop_drive.release(obj, name, opaque); |
| 251 | } |
| 252 | |
| 253 | static const PropertyInfo efuse_prop_drive = { |
| 254 | .type = "str", |
| 255 | .description = "Node name or ID of a block device to use as eFUSE backend", |
| 256 | .realized_set_allowed = true, |
| 257 | .get = efuse_prop_get_drive, |
| 258 | .set = efuse_prop_set_drive, |
| 259 | .release = efuse_prop_release_drive, |
| 260 | }; |
| 261 | |
| 262 | static const Property efuse_properties[] = { |
| 263 | DEFINE_PROP("drive", XlnxEFuse, blk, efuse_prop_drive, BlockBackend *), |
| 264 | DEFINE_PROP_UINT8("efuse-nr", XlnxEFuse, efuse_nr, 3), |
| 265 | DEFINE_PROP_UINT32("efuse-size", XlnxEFuse, efuse_size, 64 * 32), |
| 266 | DEFINE_PROP_BOOL("init-factory-tbits", XlnxEFuse, init_tbits, true), |
| 267 | DEFINE_PROP_ARRAY("read-only", XlnxEFuse, ro_bits_cnt, ro_bits, |
| 268 | qdev_prop_uint32, uint32_t), |
| 269 | }; |
| 270 | |
| 271 | static void efuse_class_init(ObjectClass *klass, const void *data) |
| 272 | { |
| 273 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 274 | |
| 275 | dc->realize = efuse_realize; |
| 276 | device_class_set_props(dc, efuse_properties); |
| 277 | /* Reason: Part of Xilinx SoC */ |
| 278 | dc->user_creatable = false; |
| 279 | } |
| 280 | |
| 281 | static const TypeInfo efuse_info = { |
| 282 | .name = TYPE_XLNX_EFUSE, |
| 283 | .parent = TYPE_DEVICE, |
| 284 | .instance_size = sizeof(XlnxEFuse), |
| 285 | .class_init = efuse_class_init, |
| 286 | }; |
| 287 | |
| 288 | static void efuse_register_types(void) |
| 289 | { |
| 290 | type_register_static(&efuse_info); |
| 291 | } |
| 292 | type_init(efuse_register_types) |