1461a6a6fSTong Ho /* 2461a6a6fSTong Ho * QEMU model of the Xilinx BBRAM Battery Backed RAM 3461a6a6fSTong Ho * 4461a6a6fSTong Ho * Copyright (c) 2014-2021 Xilinx Inc. 5213bf5c1STong Ho * Copyright (c) 2023 Advanced Micro Devices, Inc. 6461a6a6fSTong Ho * 7461a6a6fSTong Ho * Permission is hereby granted, free of charge, to any person obtaining a copy 8461a6a6fSTong Ho * of this software and associated documentation files (the "Software"), to deal 9461a6a6fSTong Ho * in the Software without restriction, including without limitation the rights 10461a6a6fSTong Ho * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11461a6a6fSTong Ho * copies of the Software, and to permit persons to whom the Software is 12461a6a6fSTong Ho * furnished to do so, subject to the following conditions: 13461a6a6fSTong Ho * 14461a6a6fSTong Ho * The above copyright notice and this permission notice shall be included in 15461a6a6fSTong Ho * all copies or substantial portions of the Software. 16461a6a6fSTong Ho * 17461a6a6fSTong Ho * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18461a6a6fSTong Ho * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19461a6a6fSTong Ho * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20461a6a6fSTong Ho * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21461a6a6fSTong Ho * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22461a6a6fSTong Ho * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23461a6a6fSTong Ho * THE SOFTWARE. 24461a6a6fSTong Ho */ 25461a6a6fSTong Ho 26461a6a6fSTong Ho #include "qemu/osdep.h" 27461a6a6fSTong Ho #include "hw/nvram/xlnx-bbram.h" 28461a6a6fSTong Ho 29461a6a6fSTong Ho #include "qemu/error-report.h" 30461a6a6fSTong Ho #include "qemu/log.h" 31461a6a6fSTong Ho #include "qapi/error.h" 32461a6a6fSTong Ho #include "sysemu/blockdev.h" 33461a6a6fSTong Ho #include "migration/vmstate.h" 34461a6a6fSTong Ho #include "hw/qdev-properties.h" 35461a6a6fSTong Ho #include "hw/qdev-properties-system.h" 36461a6a6fSTong Ho #include "hw/nvram/xlnx-efuse.h" 37461a6a6fSTong Ho 38461a6a6fSTong Ho #ifndef XLNX_BBRAM_ERR_DEBUG 39461a6a6fSTong Ho #define XLNX_BBRAM_ERR_DEBUG 0 40461a6a6fSTong Ho #endif 41461a6a6fSTong Ho 42461a6a6fSTong Ho REG32(BBRAM_STATUS, 0x0) 43461a6a6fSTong Ho FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1) 44461a6a6fSTong Ho FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1) 45461a6a6fSTong Ho FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1) 46461a6a6fSTong Ho FIELD(BBRAM_STATUS, PGM_MODE, 0, 1) 47461a6a6fSTong Ho REG32(BBRAM_CTRL, 0x4) 48461a6a6fSTong Ho FIELD(BBRAM_CTRL, ZEROIZE, 0, 1) 49461a6a6fSTong Ho REG32(PGM_MODE, 0x8) 50461a6a6fSTong Ho REG32(BBRAM_AES_CRC, 0xc) 51461a6a6fSTong Ho REG32(BBRAM_0, 0x10) 52461a6a6fSTong Ho REG32(BBRAM_1, 0x14) 53461a6a6fSTong Ho REG32(BBRAM_2, 0x18) 54461a6a6fSTong Ho REG32(BBRAM_3, 0x1c) 55461a6a6fSTong Ho REG32(BBRAM_4, 0x20) 56461a6a6fSTong Ho REG32(BBRAM_5, 0x24) 57461a6a6fSTong Ho REG32(BBRAM_6, 0x28) 58461a6a6fSTong Ho REG32(BBRAM_7, 0x2c) 59461a6a6fSTong Ho REG32(BBRAM_8, 0x30) 60461a6a6fSTong Ho REG32(BBRAM_SLVERR, 0x34) 61461a6a6fSTong Ho FIELD(BBRAM_SLVERR, ENABLE, 0, 1) 62461a6a6fSTong Ho REG32(BBRAM_ISR, 0x38) 63461a6a6fSTong Ho FIELD(BBRAM_ISR, APB_SLVERR, 0, 1) 64461a6a6fSTong Ho REG32(BBRAM_IMR, 0x3c) 65461a6a6fSTong Ho FIELD(BBRAM_IMR, APB_SLVERR, 0, 1) 66461a6a6fSTong Ho REG32(BBRAM_IER, 0x40) 67461a6a6fSTong Ho FIELD(BBRAM_IER, APB_SLVERR, 0, 1) 68461a6a6fSTong Ho REG32(BBRAM_IDR, 0x44) 69461a6a6fSTong Ho FIELD(BBRAM_IDR, APB_SLVERR, 0, 1) 70461a6a6fSTong Ho REG32(BBRAM_MSW_LOCK, 0x4c) 71461a6a6fSTong Ho FIELD(BBRAM_MSW_LOCK, VAL, 0, 1) 72461a6a6fSTong Ho 73461a6a6fSTong Ho #define R_MAX (R_BBRAM_MSW_LOCK + 1) 74461a6a6fSTong Ho 75461a6a6fSTong Ho #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0) 76461a6a6fSTong Ho 77461a6a6fSTong Ho #define BBRAM_PGM_MAGIC 0x757bdf0d 78461a6a6fSTong Ho 79461a6a6fSTong Ho QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs)); 80461a6a6fSTong Ho 81461a6a6fSTong Ho static bool bbram_msw_locked(XlnxBBRam *s) 82461a6a6fSTong Ho { 83461a6a6fSTong Ho return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0; 84461a6a6fSTong Ho } 85461a6a6fSTong Ho 86461a6a6fSTong Ho static bool bbram_pgm_enabled(XlnxBBRam *s) 87461a6a6fSTong Ho { 88461a6a6fSTong Ho return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0; 89461a6a6fSTong Ho } 90461a6a6fSTong Ho 91461a6a6fSTong Ho static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail) 92461a6a6fSTong Ho { 932e9ce532STong Ho Error *errp = NULL; 94461a6a6fSTong Ho 95461a6a6fSTong Ho error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.", 96461a6a6fSTong Ho blk_name(s->blk), detail); 97461a6a6fSTong Ho error_report("%s", error_get_pretty(errp)); 98461a6a6fSTong Ho error_free(errp); 99461a6a6fSTong Ho 100461a6a6fSTong Ho g_free(detail); 101461a6a6fSTong Ho } 102461a6a6fSTong Ho 103461a6a6fSTong Ho static void bbram_bdrv_read(XlnxBBRam *s, Error **errp) 104461a6a6fSTong Ho { 105461a6a6fSTong Ho uint32_t *ram = &s->regs[R_BBRAM_0]; 106461a6a6fSTong Ho int nr = RAM_MAX; 107461a6a6fSTong Ho 108461a6a6fSTong Ho if (!s->blk) { 109461a6a6fSTong Ho return; 110461a6a6fSTong Ho } 111461a6a6fSTong Ho 112461a6a6fSTong Ho s->blk_ro = !blk_supports_write_perm(s->blk); 113461a6a6fSTong Ho if (!s->blk_ro) { 114461a6a6fSTong Ho int rc; 115461a6a6fSTong Ho 116461a6a6fSTong Ho rc = blk_set_perm(s->blk, 117461a6a6fSTong Ho (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE), 118461a6a6fSTong Ho BLK_PERM_ALL, NULL); 119461a6a6fSTong Ho if (rc) { 120461a6a6fSTong Ho s->blk_ro = true; 121461a6a6fSTong Ho } 122461a6a6fSTong Ho } 123461a6a6fSTong Ho if (s->blk_ro) { 124461a6a6fSTong Ho warn_report("%s: Skip saving updates to read-only BBRAM backstore.", 125461a6a6fSTong Ho blk_name(s->blk)); 126461a6a6fSTong Ho } 127461a6a6fSTong Ho 128a9262f55SAlberto Faria if (blk_pread(s->blk, 0, nr, ram, 0) < 0) { 129461a6a6fSTong Ho error_setg(errp, 130461a6a6fSTong Ho "%s: Failed to read %u bytes from BBRAM backstore.", 131461a6a6fSTong Ho blk_name(s->blk), nr); 132461a6a6fSTong Ho return; 133461a6a6fSTong Ho } 134461a6a6fSTong Ho 135461a6a6fSTong Ho /* Convert from little-endian backstore for each 32-bit word */ 136461a6a6fSTong Ho nr /= 4; 137461a6a6fSTong Ho while (nr--) { 138461a6a6fSTong Ho ram[nr] = le32_to_cpu(ram[nr]); 139461a6a6fSTong Ho } 140461a6a6fSTong Ho } 141461a6a6fSTong Ho 142461a6a6fSTong Ho static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr) 143461a6a6fSTong Ho { 144461a6a6fSTong Ho uint32_t le32; 145461a6a6fSTong Ho unsigned offset; 146461a6a6fSTong Ho int rc; 147461a6a6fSTong Ho 148461a6a6fSTong Ho assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8); 149461a6a6fSTong Ho 150461a6a6fSTong Ho /* Backstore is always in little-endian */ 151461a6a6fSTong Ho le32 = cpu_to_le32(s->regs[hwaddr / 4]); 152461a6a6fSTong Ho 153461a6a6fSTong Ho /* Update zeroized flag */ 154461a6a6fSTong Ho if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) { 155461a6a6fSTong Ho ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0); 156461a6a6fSTong Ho } 157461a6a6fSTong Ho 158461a6a6fSTong Ho if (!s->blk || s->blk_ro) { 159461a6a6fSTong Ho return; 160461a6a6fSTong Ho } 161461a6a6fSTong Ho 162461a6a6fSTong Ho offset = hwaddr - A_BBRAM_0; 163a9262f55SAlberto Faria rc = blk_pwrite(s->blk, offset, 4, &le32, 0); 164461a6a6fSTong Ho if (rc < 0) { 165461a6a6fSTong Ho bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset)); 166461a6a6fSTong Ho } 167461a6a6fSTong Ho } 168461a6a6fSTong Ho 169461a6a6fSTong Ho static void bbram_bdrv_zero(XlnxBBRam *s) 170461a6a6fSTong Ho { 171461a6a6fSTong Ho int rc; 172461a6a6fSTong Ho 173461a6a6fSTong Ho ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1); 174461a6a6fSTong Ho 175461a6a6fSTong Ho if (!s->blk || s->blk_ro) { 176461a6a6fSTong Ho return; 177461a6a6fSTong Ho } 178461a6a6fSTong Ho 179461a6a6fSTong Ho rc = blk_make_zero(s->blk, 0); 180461a6a6fSTong Ho if (rc < 0) { 181461a6a6fSTong Ho bbram_bdrv_error(s, rc, g_strdup("zeroizing")); 182461a6a6fSTong Ho } 183461a6a6fSTong Ho 184461a6a6fSTong Ho /* Restore bbram8 if it is non-zero */ 185461a6a6fSTong Ho if (s->regs[R_BBRAM_8]) { 186461a6a6fSTong Ho bbram_bdrv_sync(s, A_BBRAM_8); 187461a6a6fSTong Ho } 188461a6a6fSTong Ho } 189461a6a6fSTong Ho 190461a6a6fSTong Ho static void bbram_zeroize(XlnxBBRam *s) 191461a6a6fSTong Ho { 192461a6a6fSTong Ho int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */ 193461a6a6fSTong Ho 194461a6a6fSTong Ho memset(&s->regs[R_BBRAM_0], 0, nr); 195461a6a6fSTong Ho bbram_bdrv_zero(s); 196461a6a6fSTong Ho } 197461a6a6fSTong Ho 198461a6a6fSTong Ho static void bbram_update_irq(XlnxBBRam *s) 199461a6a6fSTong Ho { 200461a6a6fSTong Ho bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR]; 201461a6a6fSTong Ho 202461a6a6fSTong Ho qemu_set_irq(s->irq_bbram, pending); 203461a6a6fSTong Ho } 204461a6a6fSTong Ho 205461a6a6fSTong Ho static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64) 206461a6a6fSTong Ho { 207461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 208461a6a6fSTong Ho uint32_t val = val64; 209461a6a6fSTong Ho 210461a6a6fSTong Ho if (val & R_BBRAM_CTRL_ZEROIZE_MASK) { 211461a6a6fSTong Ho bbram_zeroize(s); 212461a6a6fSTong Ho /* The bit is self clearing */ 213461a6a6fSTong Ho s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK; 214461a6a6fSTong Ho } 215461a6a6fSTong Ho } 216461a6a6fSTong Ho 217461a6a6fSTong Ho static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64) 218461a6a6fSTong Ho { 219461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 220461a6a6fSTong Ho uint32_t val = val64; 221461a6a6fSTong Ho 222461a6a6fSTong Ho if (val == BBRAM_PGM_MAGIC) { 223461a6a6fSTong Ho bbram_zeroize(s); 224461a6a6fSTong Ho 225461a6a6fSTong Ho /* The status bit is cleared only by POR */ 226461a6a6fSTong Ho ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1); 227461a6a6fSTong Ho } 228461a6a6fSTong Ho } 229461a6a6fSTong Ho 230461a6a6fSTong Ho static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64) 231461a6a6fSTong Ho { 232461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 233461a6a6fSTong Ho uint32_t calc_crc; 234461a6a6fSTong Ho 235461a6a6fSTong Ho if (!bbram_pgm_enabled(s)) { 236461a6a6fSTong Ho /* We are not in programming mode, don't do anything */ 237461a6a6fSTong Ho return; 238461a6a6fSTong Ho } 239461a6a6fSTong Ho 240461a6a6fSTong Ho /* Perform the AES integrity check */ 241461a6a6fSTong Ho s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK; 242461a6a6fSTong Ho 243461a6a6fSTong Ho /* 244461a6a6fSTong Ho * Set check status. 245461a6a6fSTong Ho * 246461a6a6fSTong Ho * ZynqMP BBRAM check has a zero-u32 prepended; see: 247461a6a6fSTong Ho * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311 248461a6a6fSTong Ho */ 249461a6a6fSTong Ho calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0], 250461a6a6fSTong Ho (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads); 251461a6a6fSTong Ho 252461a6a6fSTong Ho ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS, 253461a6a6fSTong Ho (s->regs[R_BBRAM_AES_CRC] == calc_crc)); 254461a6a6fSTong Ho } 255461a6a6fSTong Ho 256461a6a6fSTong Ho static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64) 257461a6a6fSTong Ho { 258461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 259461a6a6fSTong Ho uint32_t original_data = *(uint32_t *) reg->data; 260461a6a6fSTong Ho 261461a6a6fSTong Ho if (bbram_pgm_enabled(s)) { 262461a6a6fSTong Ho return val64; 263461a6a6fSTong Ho } else { 264461a6a6fSTong Ho /* We are not in programming mode, don't do anything */ 265461a6a6fSTong Ho qemu_log_mask(LOG_GUEST_ERROR, 266461a6a6fSTong Ho "Not in programming mode, dropping the write\n"); 267461a6a6fSTong Ho return original_data; 268461a6a6fSTong Ho } 269461a6a6fSTong Ho } 270461a6a6fSTong Ho 271461a6a6fSTong Ho static void bbram_key_postw(RegisterInfo *reg, uint64_t val64) 272461a6a6fSTong Ho { 273461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 274461a6a6fSTong Ho 275461a6a6fSTong Ho bbram_bdrv_sync(s, reg->access->addr); 276461a6a6fSTong Ho } 277461a6a6fSTong Ho 278461a6a6fSTong Ho static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val) 279461a6a6fSTong Ho { 280461a6a6fSTong Ho return 0; 281461a6a6fSTong Ho } 282461a6a6fSTong Ho 283461a6a6fSTong Ho static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val) 284461a6a6fSTong Ho { 285461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 286461a6a6fSTong Ho 287461a6a6fSTong Ho return s->bbram8_wo ? bbram_wo_postr(reg, val) : val; 288461a6a6fSTong Ho } 289461a6a6fSTong Ho 290461a6a6fSTong Ho static bool bbram_r8_readonly(XlnxBBRam *s) 291461a6a6fSTong Ho { 292461a6a6fSTong Ho return !bbram_pgm_enabled(s) || bbram_msw_locked(s); 293461a6a6fSTong Ho } 294461a6a6fSTong Ho 295461a6a6fSTong Ho static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64) 296461a6a6fSTong Ho { 297461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 298461a6a6fSTong Ho 299461a6a6fSTong Ho if (bbram_r8_readonly(s)) { 300461a6a6fSTong Ho val64 = *(uint32_t *)reg->data; 301461a6a6fSTong Ho } 302461a6a6fSTong Ho 303461a6a6fSTong Ho return val64; 304461a6a6fSTong Ho } 305461a6a6fSTong Ho 306461a6a6fSTong Ho static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64) 307461a6a6fSTong Ho { 308461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 309461a6a6fSTong Ho 310461a6a6fSTong Ho if (!bbram_r8_readonly(s)) { 311461a6a6fSTong Ho bbram_bdrv_sync(s, A_BBRAM_8); 312461a6a6fSTong Ho } 313461a6a6fSTong Ho } 314461a6a6fSTong Ho 315461a6a6fSTong Ho static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64) 316461a6a6fSTong Ho { 317461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 318461a6a6fSTong Ho 319461a6a6fSTong Ho /* Never lock if bbram8 is wo; and, only POR can clear the lock */ 320461a6a6fSTong Ho if (s->bbram8_wo) { 321461a6a6fSTong Ho val64 = 0; 322461a6a6fSTong Ho } else { 323461a6a6fSTong Ho val64 |= s->regs[R_BBRAM_MSW_LOCK]; 324461a6a6fSTong Ho } 325461a6a6fSTong Ho 326461a6a6fSTong Ho return val64; 327461a6a6fSTong Ho } 328461a6a6fSTong Ho 329461a6a6fSTong Ho static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64) 330461a6a6fSTong Ho { 331461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 332461a6a6fSTong Ho 333461a6a6fSTong Ho bbram_update_irq(s); 334461a6a6fSTong Ho } 335461a6a6fSTong Ho 336461a6a6fSTong Ho static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64) 337461a6a6fSTong Ho { 338461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 339461a6a6fSTong Ho uint32_t val = val64; 340461a6a6fSTong Ho 341461a6a6fSTong Ho s->regs[R_BBRAM_IMR] &= ~val; 342461a6a6fSTong Ho bbram_update_irq(s); 343461a6a6fSTong Ho return 0; 344461a6a6fSTong Ho } 345461a6a6fSTong Ho 346461a6a6fSTong Ho static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64) 347461a6a6fSTong Ho { 348461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(reg->opaque); 349461a6a6fSTong Ho uint32_t val = val64; 350461a6a6fSTong Ho 351461a6a6fSTong Ho s->regs[R_BBRAM_IMR] |= val; 352461a6a6fSTong Ho bbram_update_irq(s); 353461a6a6fSTong Ho return 0; 354461a6a6fSTong Ho } 355461a6a6fSTong Ho 356461a6a6fSTong Ho static RegisterAccessInfo bbram_ctrl_regs_info[] = { 357461a6a6fSTong Ho { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS, 358461a6a6fSTong Ho .rsvd = 0xee, 359461a6a6fSTong Ho .ro = 0x3ff, 360461a6a6fSTong Ho },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL, 361461a6a6fSTong Ho .post_write = bbram_ctrl_postw, 362461a6a6fSTong Ho },{ .name = "PGM_MODE", .addr = A_PGM_MODE, 363461a6a6fSTong Ho .post_write = bbram_pgm_mode_postw, 364461a6a6fSTong Ho },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC, 365461a6a6fSTong Ho .post_write = bbram_aes_crc_postw, 366461a6a6fSTong Ho .post_read = bbram_wo_postr, 367461a6a6fSTong Ho },{ .name = "BBRAM_0", .addr = A_BBRAM_0, 368461a6a6fSTong Ho .pre_write = bbram_key_prew, 369461a6a6fSTong Ho .post_write = bbram_key_postw, 370461a6a6fSTong Ho .post_read = bbram_wo_postr, 371461a6a6fSTong Ho },{ .name = "BBRAM_1", .addr = A_BBRAM_1, 372461a6a6fSTong Ho .pre_write = bbram_key_prew, 373461a6a6fSTong Ho .post_write = bbram_key_postw, 374461a6a6fSTong Ho .post_read = bbram_wo_postr, 375461a6a6fSTong Ho },{ .name = "BBRAM_2", .addr = A_BBRAM_2, 376461a6a6fSTong Ho .pre_write = bbram_key_prew, 377461a6a6fSTong Ho .post_write = bbram_key_postw, 378461a6a6fSTong Ho .post_read = bbram_wo_postr, 379461a6a6fSTong Ho },{ .name = "BBRAM_3", .addr = A_BBRAM_3, 380461a6a6fSTong Ho .pre_write = bbram_key_prew, 381461a6a6fSTong Ho .post_write = bbram_key_postw, 382461a6a6fSTong Ho .post_read = bbram_wo_postr, 383461a6a6fSTong Ho },{ .name = "BBRAM_4", .addr = A_BBRAM_4, 384461a6a6fSTong Ho .pre_write = bbram_key_prew, 385461a6a6fSTong Ho .post_write = bbram_key_postw, 386461a6a6fSTong Ho .post_read = bbram_wo_postr, 387461a6a6fSTong Ho },{ .name = "BBRAM_5", .addr = A_BBRAM_5, 388461a6a6fSTong Ho .pre_write = bbram_key_prew, 389461a6a6fSTong Ho .post_write = bbram_key_postw, 390461a6a6fSTong Ho .post_read = bbram_wo_postr, 391461a6a6fSTong Ho },{ .name = "BBRAM_6", .addr = A_BBRAM_6, 392461a6a6fSTong Ho .pre_write = bbram_key_prew, 393461a6a6fSTong Ho .post_write = bbram_key_postw, 394461a6a6fSTong Ho .post_read = bbram_wo_postr, 395461a6a6fSTong Ho },{ .name = "BBRAM_7", .addr = A_BBRAM_7, 396461a6a6fSTong Ho .pre_write = bbram_key_prew, 397461a6a6fSTong Ho .post_write = bbram_key_postw, 398461a6a6fSTong Ho .post_read = bbram_wo_postr, 399461a6a6fSTong Ho },{ .name = "BBRAM_8", .addr = A_BBRAM_8, 400461a6a6fSTong Ho .pre_write = bbram_r8_prew, 401461a6a6fSTong Ho .post_write = bbram_r8_postw, 402461a6a6fSTong Ho .post_read = bbram_r8_postr, 403461a6a6fSTong Ho },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR, 404461a6a6fSTong Ho .rsvd = ~1, 405461a6a6fSTong Ho },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR, 406461a6a6fSTong Ho .w1c = 0x1, 407461a6a6fSTong Ho .post_write = bbram_isr_postw, 408461a6a6fSTong Ho },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR, 409461a6a6fSTong Ho .ro = 0x1, 410461a6a6fSTong Ho },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER, 411461a6a6fSTong Ho .pre_write = bbram_ier_prew, 412461a6a6fSTong Ho },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR, 413461a6a6fSTong Ho .pre_write = bbram_idr_prew, 414461a6a6fSTong Ho },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK, 415461a6a6fSTong Ho .pre_write = bbram_msw_lock_prew, 416461a6a6fSTong Ho .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK, 417461a6a6fSTong Ho } 418461a6a6fSTong Ho }; 419461a6a6fSTong Ho 420ad80e367SPeter Maydell static void bbram_ctrl_reset_hold(Object *obj, ResetType type) 421461a6a6fSTong Ho { 422213bf5c1STong Ho XlnxBBRam *s = XLNX_BBRAM(obj); 423461a6a6fSTong Ho unsigned int i; 424461a6a6fSTong Ho 425461a6a6fSTong Ho for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { 426461a6a6fSTong Ho if (i < R_BBRAM_0 || i > R_BBRAM_8) { 427461a6a6fSTong Ho register_reset(&s->regs_info[i]); 428461a6a6fSTong Ho } 429461a6a6fSTong Ho } 430461a6a6fSTong Ho 431461a6a6fSTong Ho bbram_update_irq(s); 432461a6a6fSTong Ho } 433461a6a6fSTong Ho 434461a6a6fSTong Ho static const MemoryRegionOps bbram_ctrl_ops = { 435461a6a6fSTong Ho .read = register_read_memory, 436461a6a6fSTong Ho .write = register_write_memory, 437461a6a6fSTong Ho .endianness = DEVICE_LITTLE_ENDIAN, 438461a6a6fSTong Ho .valid = { 439461a6a6fSTong Ho .min_access_size = 4, 440461a6a6fSTong Ho .max_access_size = 4, 441461a6a6fSTong Ho }, 442461a6a6fSTong Ho }; 443461a6a6fSTong Ho 444461a6a6fSTong Ho static void bbram_ctrl_realize(DeviceState *dev, Error **errp) 445461a6a6fSTong Ho { 446461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(dev); 447461a6a6fSTong Ho 448461a6a6fSTong Ho if (s->crc_zpads) { 449461a6a6fSTong Ho s->bbram8_wo = true; 450461a6a6fSTong Ho } 451461a6a6fSTong Ho 452461a6a6fSTong Ho bbram_bdrv_read(s, errp); 453461a6a6fSTong Ho } 454461a6a6fSTong Ho 455461a6a6fSTong Ho static void bbram_ctrl_init(Object *obj) 456461a6a6fSTong Ho { 457461a6a6fSTong Ho XlnxBBRam *s = XLNX_BBRAM(obj); 458461a6a6fSTong Ho SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 459461a6a6fSTong Ho 460*4a87373fSPeter Maydell s->reg_array = 461461a6a6fSTong Ho register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, 462461a6a6fSTong Ho ARRAY_SIZE(bbram_ctrl_regs_info), 463461a6a6fSTong Ho s->regs_info, s->regs, 464461a6a6fSTong Ho &bbram_ctrl_ops, 465461a6a6fSTong Ho XLNX_BBRAM_ERR_DEBUG, 466461a6a6fSTong Ho R_MAX * 4); 467461a6a6fSTong Ho 468*4a87373fSPeter Maydell sysbus_init_mmio(sbd, &s->reg_array->mem); 469461a6a6fSTong Ho sysbus_init_irq(sbd, &s->irq_bbram); 470461a6a6fSTong Ho } 471461a6a6fSTong Ho 472*4a87373fSPeter Maydell static void bbram_ctrl_finalize(Object *obj) 473*4a87373fSPeter Maydell { 474*4a87373fSPeter Maydell XlnxBBRam *s = XLNX_BBRAM(obj); 475*4a87373fSPeter Maydell 476*4a87373fSPeter Maydell register_finalize_block(s->reg_array); 477*4a87373fSPeter Maydell } 478*4a87373fSPeter Maydell 479461a6a6fSTong Ho static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, 480461a6a6fSTong Ho void *opaque, Error **errp) 481461a6a6fSTong Ho { 482461a6a6fSTong Ho DeviceState *dev = DEVICE(obj); 483461a6a6fSTong Ho 484461a6a6fSTong Ho qdev_prop_drive.set(obj, v, name, opaque, errp); 485461a6a6fSTong Ho 486461a6a6fSTong Ho /* Fill initial data if backend is attached after realized */ 487461a6a6fSTong Ho if (dev->realized) { 488461a6a6fSTong Ho bbram_bdrv_read(XLNX_BBRAM(obj), errp); 489461a6a6fSTong Ho } 490461a6a6fSTong Ho } 491461a6a6fSTong Ho 492461a6a6fSTong Ho static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name, 493461a6a6fSTong Ho void *opaque, Error **errp) 494461a6a6fSTong Ho { 495461a6a6fSTong Ho qdev_prop_drive.get(obj, v, name, opaque, errp); 496461a6a6fSTong Ho } 497461a6a6fSTong Ho 498461a6a6fSTong Ho static void bbram_prop_release_drive(Object *obj, const char *name, 499461a6a6fSTong Ho void *opaque) 500461a6a6fSTong Ho { 501461a6a6fSTong Ho qdev_prop_drive.release(obj, name, opaque); 502461a6a6fSTong Ho } 503461a6a6fSTong Ho 504461a6a6fSTong Ho static const PropertyInfo bbram_prop_drive = { 505461a6a6fSTong Ho .name = "str", 506461a6a6fSTong Ho .description = "Node name or ID of a block device to use as BBRAM backend", 507461a6a6fSTong Ho .realized_set_allowed = true, 508461a6a6fSTong Ho .get = bbram_prop_get_drive, 509461a6a6fSTong Ho .set = bbram_prop_set_drive, 510461a6a6fSTong Ho .release = bbram_prop_release_drive, 511461a6a6fSTong Ho }; 512461a6a6fSTong Ho 513461a6a6fSTong Ho static const VMStateDescription vmstate_bbram_ctrl = { 514461a6a6fSTong Ho .name = TYPE_XLNX_BBRAM, 515461a6a6fSTong Ho .version_id = 1, 516461a6a6fSTong Ho .minimum_version_id = 1, 51718d10e61SRichard Henderson .fields = (const VMStateField[]) { 518461a6a6fSTong Ho VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX), 519461a6a6fSTong Ho VMSTATE_END_OF_LIST(), 520461a6a6fSTong Ho } 521461a6a6fSTong Ho }; 522461a6a6fSTong Ho 523461a6a6fSTong Ho static Property bbram_ctrl_props[] = { 524461a6a6fSTong Ho DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *), 525461a6a6fSTong Ho DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), 526461a6a6fSTong Ho DEFINE_PROP_END_OF_LIST(), 527461a6a6fSTong Ho }; 528461a6a6fSTong Ho 529461a6a6fSTong Ho static void bbram_ctrl_class_init(ObjectClass *klass, void *data) 530461a6a6fSTong Ho { 531461a6a6fSTong Ho DeviceClass *dc = DEVICE_CLASS(klass); 532213bf5c1STong Ho ResettableClass *rc = RESETTABLE_CLASS(klass); 533461a6a6fSTong Ho 534213bf5c1STong Ho rc->phases.hold = bbram_ctrl_reset_hold; 535461a6a6fSTong Ho dc->realize = bbram_ctrl_realize; 536461a6a6fSTong Ho dc->vmsd = &vmstate_bbram_ctrl; 537461a6a6fSTong Ho device_class_set_props(dc, bbram_ctrl_props); 538461a6a6fSTong Ho } 539461a6a6fSTong Ho 540461a6a6fSTong Ho static const TypeInfo bbram_ctrl_info = { 541461a6a6fSTong Ho .name = TYPE_XLNX_BBRAM, 542461a6a6fSTong Ho .parent = TYPE_SYS_BUS_DEVICE, 543461a6a6fSTong Ho .instance_size = sizeof(XlnxBBRam), 544461a6a6fSTong Ho .class_init = bbram_ctrl_class_init, 545461a6a6fSTong Ho .instance_init = bbram_ctrl_init, 546*4a87373fSPeter Maydell .instance_finalize = bbram_ctrl_finalize, 547461a6a6fSTong Ho }; 548461a6a6fSTong Ho 549461a6a6fSTong Ho static void bbram_ctrl_register_types(void) 550461a6a6fSTong Ho { 551461a6a6fSTong Ho type_register_static(&bbram_ctrl_info); 552461a6a6fSTong Ho } 553461a6a6fSTong Ho 554461a6a6fSTong Ho type_init(bbram_ctrl_register_types) 555