| 1 | /* |
| 2 | * QEMU model of the Xilinx BBRAM Battery Backed RAM |
| 3 | * |
| 4 | * Copyright (c) 2014-2021 Xilinx Inc. |
| 5 | * Copyright (c) 2023 Advanced Micro Devices, Inc. |
| 6 | * |
| 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | * of this software and associated documentation files (the "Software"), to deal |
| 9 | * in the Software without restriction, including without limitation the rights |
| 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | * copies of the Software, and to permit persons to whom the Software is |
| 12 | * furnished to do so, subject to the following conditions: |
| 13 | * |
| 14 | * The above copyright notice and this permission notice shall be included in |
| 15 | * all copies or substantial portions of the Software. |
| 16 | * |
| 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 23 | * THE SOFTWARE. |
| 24 | */ |
| 25 | |
| 26 | #include "qemu/osdep.h" |
| 27 | #include "hw/nvram/xlnx-bbram.h" |
| 28 | |
| 29 | #include "qemu/error-report.h" |
| 30 | #include "qemu/log.h" |
| 31 | #include "qapi/error.h" |
| 32 | #include "system/blockdev.h" |
| 33 | #include "migration/vmstate.h" |
| 34 | #include "hw/core/qdev-properties.h" |
| 35 | #include "hw/core/qdev-properties-system.h" |
| 36 | #include "hw/nvram/xlnx-efuse.h" |
| 37 | |
| 38 | #ifndef XLNX_BBRAM_ERR_DEBUG |
| 39 | #define XLNX_BBRAM_ERR_DEBUG 0 |
| 40 | #endif |
| 41 | |
| 42 | REG32(BBRAM_STATUS, 0x0) |
| 43 | FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) |
| 44 | FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) |
| 45 | FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) |
| 46 | FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) |
| 47 | REG32(BBRAM_CTRL, 0x4) |
| 48 | FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) |
| 49 | REG32(PGM_MODE, 0x8) |
| 50 | REG32(BBRAM_AES_CRC, 0xc) |
| 51 | REG32(BBRAM_0, 0x10) |
| 52 | REG32(BBRAM_1, 0x14) |
| 53 | REG32(BBRAM_2, 0x18) |
| 54 | REG32(BBRAM_3, 0x1c) |
| 55 | REG32(BBRAM_4, 0x20) |
| 56 | REG32(BBRAM_5, 0x24) |
| 57 | REG32(BBRAM_6, 0x28) |
| 58 | REG32(BBRAM_7, 0x2c) |
| 59 | REG32(BBRAM_8, 0x30) |
| 60 | REG32(BBRAM_SLVERR, 0x34) |
| 61 | FIELD(BBRAM_SLVERR, ENABLE, 0, 1) |
| 62 | REG32(BBRAM_ISR, 0x38) |
| 63 | FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) |
| 64 | REG32(BBRAM_IMR, 0x3c) |
| 65 | FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) |
| 66 | REG32(BBRAM_IER, 0x40) |
| 67 | FIELD(BBRAM_IER, APB_SLVERR, 0, 1) |
| 68 | REG32(BBRAM_IDR, 0x44) |
| 69 | FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) |
| 70 | REG32(BBRAM_MSW_LOCK, 0x4c) |
| 71 | FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) |
| 72 | |
| 73 | #define R_MAX (R_BBRAM_MSW_LOCK + 1) |
| 74 | |
| 75 | #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) |
| 76 | |
| 77 | #define BBRAM_PGM_MAGIC 0x757bdf0d |
| 78 | |
| 79 | QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs)); |
| 80 | |
| 81 | static bool bbram_msw_locked(XlnxBBRam *s) |
| 82 | { |
| 83 | return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; |
| 84 | } |
| 85 | |
| 86 | static bool bbram_pgm_enabled(XlnxBBRam *s) |
| 87 | { |
| 88 | return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; |
| 89 | } |
| 90 | |
| 91 | static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) |
| 92 | { |
| 93 | uint32_t *ram = &s->regs[R_BBRAM_0]; |
| 94 | int nr = RAM_MAX; |
| 95 | |
| 96 | if (!s->blk) { |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | s->blk_ro = !blk_supports_write_perm(s->blk); |
| 101 | if (!s->blk_ro) { |
| 102 | int rc; |
| 103 | |
| 104 | rc = blk_set_perm(s->blk, |
| 105 | (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), |
| 106 | BLK_PERM_ALL, NULL); |
| 107 | if (rc) { |
| 108 | s->blk_ro = true; |
| 109 | } |
| 110 | } |
| 111 | if (s->blk_ro) { |
| 112 | warn_report("%s: Skip saving updates to read-only BBRAM backstore.", |
| 113 | blk_name(s->blk)); |
| 114 | } |
| 115 | |
| 116 | if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { |
| 117 | error_setg(errp, |
| 118 | "%s: Failed to read %u bytes from BBRAM backstore.", |
| 119 | blk_name(s->blk), nr); |
| 120 | return; |
| 121 | } |
| 122 | |
| 123 | /* Convert from little-endian backstore for each 32-bit word */ |
| 124 | nr /= 4; |
| 125 | while (nr--) { |
| 126 | ram[nr] = le32_to_cpu(ram[nr]); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) |
| 131 | { |
| 132 | uint32_t le32; |
| 133 | unsigned offset; |
| 134 | int rc; |
| 135 | |
| 136 | assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); |
| 137 | |
| 138 | /* Backstore is always in little-endian */ |
| 139 | le32 = cpu_to_le32(s->regs[hwaddr / 4]); |
| 140 | |
| 141 | /* Update zeroized flag */ |
| 142 | if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { |
| 143 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); |
| 144 | } |
| 145 | |
| 146 | if (!s->blk || s->blk_ro) { |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | offset = hwaddr - A_BBRAM_0; |
| 151 | rc = blk_pwrite(s->blk, offset, 4, &le32, 0); |
| 152 | if (rc < 0) { |
| 153 | error_report("%s: BBRAM backstore write to offset %u failed: %s", |
| 154 | blk_name(s->blk), offset, strerror(-rc)); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | static void bbram_bdrv_zero(XlnxBBRam *s) |
| 159 | { |
| 160 | int rc; |
| 161 | |
| 162 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); |
| 163 | |
| 164 | if (!s->blk || s->blk_ro) { |
| 165 | return; |
| 166 | } |
| 167 | |
| 168 | rc = blk_make_zero(s->blk, 0); |
| 169 | if (rc < 0) { |
| 170 | error_report("%s: BBRAM backstore zeroizing failed: %s", |
| 171 | blk_name(s->blk), strerror(-rc)); |
| 172 | } |
| 173 | |
| 174 | /* Restore bbram8 if it is non-zero */ |
| 175 | if (s->regs[R_BBRAM_8]) { |
| 176 | bbram_bdrv_sync(s, A_BBRAM_8); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | static void bbram_zeroize(XlnxBBRam *s) |
| 181 | { |
| 182 | int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ |
| 183 | |
| 184 | memset(&s->regs[R_BBRAM_0], 0, nr); |
| 185 | bbram_bdrv_zero(s); |
| 186 | } |
| 187 | |
| 188 | static void bbram_update_irq(XlnxBBRam *s) |
| 189 | { |
| 190 | bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; |
| 191 | |
| 192 | qemu_set_irq(s->irq_bbram, pending); |
| 193 | } |
| 194 | |
| 195 | static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) |
| 196 | { |
| 197 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 198 | uint32_t val = val64; |
| 199 | |
| 200 | if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { |
| 201 | bbram_zeroize(s); |
| 202 | /* The bit is self clearing */ |
| 203 | s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) |
| 208 | { |
| 209 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 210 | uint32_t val = val64; |
| 211 | |
| 212 | if (val == BBRAM_PGM_MAGIC) { |
| 213 | bbram_zeroize(s); |
| 214 | |
| 215 | /* The status bit is cleared only by POR */ |
| 216 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) |
| 221 | { |
| 222 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 223 | uint32_t calc_crc; |
| 224 | |
| 225 | if (!bbram_pgm_enabled(s)) { |
| 226 | /* We are not in programming mode, don't do anything */ |
| 227 | return; |
| 228 | } |
| 229 | |
| 230 | /* Perform the AES integrity check */ |
| 231 | s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; |
| 232 | |
| 233 | /* |
| 234 | * Set check status. |
| 235 | * |
| 236 | * ZynqMP BBRAM check has a zero-u32 prepended; see: |
| 237 | * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311 |
| 238 | */ |
| 239 | calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0], |
| 240 | (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads); |
| 241 | |
| 242 | ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, |
| 243 | (s->regs[R_BBRAM_AES_CRC] == calc_crc)); |
| 244 | } |
| 245 | |
| 246 | static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) |
| 247 | { |
| 248 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 249 | uint32_t original_data = *(uint32_t *) reg->data; |
| 250 | |
| 251 | if (bbram_pgm_enabled(s)) { |
| 252 | return val64; |
| 253 | } else { |
| 254 | /* We are not in programming mode, don't do anything */ |
| 255 | qemu_log_mask(LOG_GUEST_ERROR, |
| 256 | "Not in programming mode, dropping the write\n"); |
| 257 | return original_data; |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) |
| 262 | { |
| 263 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 264 | |
| 265 | bbram_bdrv_sync(s, reg->access->addr); |
| 266 | } |
| 267 | |
| 268 | static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) |
| 269 | { |
| 270 | return 0; |
| 271 | } |
| 272 | |
| 273 | static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) |
| 274 | { |
| 275 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 276 | |
| 277 | return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; |
| 278 | } |
| 279 | |
| 280 | static bool bbram_r8_readonly(XlnxBBRam *s) |
| 281 | { |
| 282 | return !bbram_pgm_enabled(s) || bbram_msw_locked(s); |
| 283 | } |
| 284 | |
| 285 | static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) |
| 286 | { |
| 287 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 288 | |
| 289 | if (bbram_r8_readonly(s)) { |
| 290 | val64 = *(uint32_t *)reg->data; |
| 291 | } |
| 292 | |
| 293 | return val64; |
| 294 | } |
| 295 | |
| 296 | static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) |
| 297 | { |
| 298 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 299 | |
| 300 | if (!bbram_r8_readonly(s)) { |
| 301 | bbram_bdrv_sync(s, A_BBRAM_8); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) |
| 306 | { |
| 307 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 308 | |
| 309 | /* Never lock if bbram8 is wo; and, only POR can clear the lock */ |
| 310 | if (s->bbram8_wo) { |
| 311 | val64 = 0; |
| 312 | } else { |
| 313 | val64 |= s->regs[R_BBRAM_MSW_LOCK]; |
| 314 | } |
| 315 | |
| 316 | return val64; |
| 317 | } |
| 318 | |
| 319 | static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) |
| 320 | { |
| 321 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 322 | |
| 323 | bbram_update_irq(s); |
| 324 | } |
| 325 | |
| 326 | static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) |
| 327 | { |
| 328 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 329 | uint32_t val = val64; |
| 330 | |
| 331 | s->regs[R_BBRAM_IMR] &= ~val; |
| 332 | bbram_update_irq(s); |
| 333 | return 0; |
| 334 | } |
| 335 | |
| 336 | static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) |
| 337 | { |
| 338 | XlnxBBRam *s = XLNX_BBRAM(reg->opaque); |
| 339 | uint32_t val = val64; |
| 340 | |
| 341 | s->regs[R_BBRAM_IMR] |= val; |
| 342 | bbram_update_irq(s); |
| 343 | return 0; |
| 344 | } |
| 345 | |
| 346 | static RegisterAccessInfo bbram_ctrl_regs_info[] = { |
| 347 | { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, |
| 348 | .rsvd = 0xee, |
| 349 | .ro = 0x3ff, |
| 350 | },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, |
| 351 | .post_write = bbram_ctrl_postw, |
| 352 | },{ .name = "PGM_MODE", .addr = A_PGM_MODE, |
| 353 | .post_write = bbram_pgm_mode_postw, |
| 354 | },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, |
| 355 | .post_write = bbram_aes_crc_postw, |
| 356 | .post_read = bbram_wo_postr, |
| 357 | },{ .name = "BBRAM_0", .addr = A_BBRAM_0, |
| 358 | .pre_write = bbram_key_prew, |
| 359 | .post_write = bbram_key_postw, |
| 360 | .post_read = bbram_wo_postr, |
| 361 | },{ .name = "BBRAM_1", .addr = A_BBRAM_1, |
| 362 | .pre_write = bbram_key_prew, |
| 363 | .post_write = bbram_key_postw, |
| 364 | .post_read = bbram_wo_postr, |
| 365 | },{ .name = "BBRAM_2", .addr = A_BBRAM_2, |
| 366 | .pre_write = bbram_key_prew, |
| 367 | .post_write = bbram_key_postw, |
| 368 | .post_read = bbram_wo_postr, |
| 369 | },{ .name = "BBRAM_3", .addr = A_BBRAM_3, |
| 370 | .pre_write = bbram_key_prew, |
| 371 | .post_write = bbram_key_postw, |
| 372 | .post_read = bbram_wo_postr, |
| 373 | },{ .name = "BBRAM_4", .addr = A_BBRAM_4, |
| 374 | .pre_write = bbram_key_prew, |
| 375 | .post_write = bbram_key_postw, |
| 376 | .post_read = bbram_wo_postr, |
| 377 | },{ .name = "BBRAM_5", .addr = A_BBRAM_5, |
| 378 | .pre_write = bbram_key_prew, |
| 379 | .post_write = bbram_key_postw, |
| 380 | .post_read = bbram_wo_postr, |
| 381 | },{ .name = "BBRAM_6", .addr = A_BBRAM_6, |
| 382 | .pre_write = bbram_key_prew, |
| 383 | .post_write = bbram_key_postw, |
| 384 | .post_read = bbram_wo_postr, |
| 385 | },{ .name = "BBRAM_7", .addr = A_BBRAM_7, |
| 386 | .pre_write = bbram_key_prew, |
| 387 | .post_write = bbram_key_postw, |
| 388 | .post_read = bbram_wo_postr, |
| 389 | },{ .name = "BBRAM_8", .addr = A_BBRAM_8, |
| 390 | .pre_write = bbram_r8_prew, |
| 391 | .post_write = bbram_r8_postw, |
| 392 | .post_read = bbram_r8_postr, |
| 393 | },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, |
| 394 | .rsvd = ~1, |
| 395 | },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, |
| 396 | .w1c = 0x1, |
| 397 | .post_write = bbram_isr_postw, |
| 398 | },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, |
| 399 | .ro = 0x1, |
| 400 | },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, |
| 401 | .pre_write = bbram_ier_prew, |
| 402 | },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, |
| 403 | .pre_write = bbram_idr_prew, |
| 404 | },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, |
| 405 | .pre_write = bbram_msw_lock_prew, |
| 406 | .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, |
| 407 | } |
| 408 | }; |
| 409 | |
| 410 | static void bbram_ctrl_reset_hold(Object *obj, ResetType type) |
| 411 | { |
| 412 | XlnxBBRam *s = XLNX_BBRAM(obj); |
| 413 | unsigned int i; |
| 414 | |
| 415 | for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { |
| 416 | if (i < R_BBRAM_0 || i > R_BBRAM_8) { |
| 417 | register_reset(&s->regs_info[i]); |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | bbram_update_irq(s); |
| 422 | } |
| 423 | |
| 424 | static const MemoryRegionOps bbram_ctrl_ops = { |
| 425 | .read = register_read_memory, |
| 426 | .write = register_write_memory, |
| 427 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 428 | .valid = { |
| 429 | .min_access_size = 4, |
| 430 | .max_access_size = 4, |
| 431 | }, |
| 432 | }; |
| 433 | |
| 434 | static void bbram_ctrl_realize(DeviceState *dev, Error **errp) |
| 435 | { |
| 436 | XlnxBBRam *s = XLNX_BBRAM(dev); |
| 437 | |
| 438 | if (s->crc_zpads) { |
| 439 | s->bbram8_wo = true; |
| 440 | } |
| 441 | |
| 442 | bbram_bdrv_read(s, errp); |
| 443 | } |
| 444 | |
| 445 | static void bbram_ctrl_init(Object *obj) |
| 446 | { |
| 447 | XlnxBBRam *s = XLNX_BBRAM(obj); |
| 448 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
| 449 | RegisterInfoArray *reg_array; |
| 450 | |
| 451 | reg_array = |
| 452 | register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, |
| 453 | ARRAY_SIZE(bbram_ctrl_regs_info), |
| 454 | s->regs_info, s->regs, |
| 455 | &bbram_ctrl_ops, |
| 456 | XLNX_BBRAM_ERR_DEBUG, |
| 457 | R_MAX * 4); |
| 458 | |
| 459 | sysbus_init_mmio(sbd, ®_array->mem); |
| 460 | sysbus_init_irq(sbd, &s->irq_bbram); |
| 461 | } |
| 462 | |
| 463 | static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, |
| 464 | void *opaque, Error **errp) |
| 465 | { |
| 466 | DeviceState *dev = DEVICE(obj); |
| 467 | |
| 468 | qdev_prop_drive.set(obj, v, name, opaque, errp); |
| 469 | |
| 470 | /* Fill initial data if backend is attached after realized */ |
| 471 | if (qdev_is_realized(dev)) { |
| 472 | bbram_bdrv_read(XLNX_BBRAM(obj), errp); |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name, |
| 477 | void *opaque, Error **errp) |
| 478 | { |
| 479 | qdev_prop_drive.get(obj, v, name, opaque, errp); |
| 480 | } |
| 481 | |
| 482 | static void bbram_prop_release_drive(Object *obj, const char *name, |
| 483 | void *opaque) |
| 484 | { |
| 485 | qdev_prop_drive.release(obj, name, opaque); |
| 486 | } |
| 487 | |
| 488 | static const PropertyInfo bbram_prop_drive = { |
| 489 | .type = "str", |
| 490 | .description = "Node name or ID of a block device to use as BBRAM backend", |
| 491 | .realized_set_allowed = true, |
| 492 | .get = bbram_prop_get_drive, |
| 493 | .set = bbram_prop_set_drive, |
| 494 | .release = bbram_prop_release_drive, |
| 495 | }; |
| 496 | |
| 497 | static const VMStateDescription vmstate_bbram_ctrl = { |
| 498 | .name = TYPE_XLNX_BBRAM, |
| 499 | .version_id = 1, |
| 500 | .minimum_version_id = 1, |
| 501 | .fields = (const VMStateField[]) { |
| 502 | VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), |
| 503 | VMSTATE_END_OF_LIST(), |
| 504 | } |
| 505 | }; |
| 506 | |
| 507 | static const Property bbram_ctrl_props[] = { |
| 508 | DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), |
| 509 | DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), |
| 510 | }; |
| 511 | |
| 512 | static void bbram_ctrl_class_init(ObjectClass *klass, const void *data) |
| 513 | { |
| 514 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 515 | ResettableClass *rc = RESETTABLE_CLASS(klass); |
| 516 | |
| 517 | rc->phases.hold = bbram_ctrl_reset_hold; |
| 518 | dc->realize = bbram_ctrl_realize; |
| 519 | dc->vmsd = &vmstate_bbram_ctrl; |
| 520 | device_class_set_props(dc, bbram_ctrl_props); |
| 521 | } |
| 522 | |
| 523 | static const TypeInfo bbram_ctrl_info = { |
| 524 | .name = TYPE_XLNX_BBRAM, |
| 525 | .parent = TYPE_SYS_BUS_DEVICE, |
| 526 | .instance_size = sizeof(XlnxBBRam), |
| 527 | .class_init = bbram_ctrl_class_init, |
| 528 | .instance_init = bbram_ctrl_init, |
| 529 | }; |
| 530 | |
| 531 | static void bbram_ctrl_register_types(void) |
| 532 | { |
| 533 | type_register_static(&bbram_ctrl_info); |
| 534 | } |
| 535 | |
| 536 | type_init(bbram_ctrl_register_types) |