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