123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- /*
- * QEMU model of the Xilinx BBRAM Battery Backed RAM
- *
- * Copyright (c) 2014-2021 Xilinx Inc.
- * Copyright (c) 2023 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include "qemu/osdep.h"
- #include "hw/nvram/xlnx-bbram.h"
- #include "qemu/error-report.h"
- #include "qemu/log.h"
- #include "qapi/error.h"
- #include "system/blockdev.h"
- #include "migration/vmstate.h"
- #include "hw/qdev-properties.h"
- #include "hw/qdev-properties-system.h"
- #include "hw/nvram/xlnx-efuse.h"
- #ifndef XLNX_BBRAM_ERR_DEBUG
- #define XLNX_BBRAM_ERR_DEBUG 0
- #endif
- REG32(BBRAM_STATUS, 0x0)
- FIELD(BBRAM_STATUS, AES_CRC_PASS, 9, 1)
- FIELD(BBRAM_STATUS, AES_CRC_DONE, 8, 1)
- FIELD(BBRAM_STATUS, BBRAM_ZEROIZED, 4, 1)
- FIELD(BBRAM_STATUS, PGM_MODE, 0, 1)
- REG32(BBRAM_CTRL, 0x4)
- FIELD(BBRAM_CTRL, ZEROIZE, 0, 1)
- REG32(PGM_MODE, 0x8)
- REG32(BBRAM_AES_CRC, 0xc)
- REG32(BBRAM_0, 0x10)
- REG32(BBRAM_1, 0x14)
- REG32(BBRAM_2, 0x18)
- REG32(BBRAM_3, 0x1c)
- REG32(BBRAM_4, 0x20)
- REG32(BBRAM_5, 0x24)
- REG32(BBRAM_6, 0x28)
- REG32(BBRAM_7, 0x2c)
- REG32(BBRAM_8, 0x30)
- REG32(BBRAM_SLVERR, 0x34)
- FIELD(BBRAM_SLVERR, ENABLE, 0, 1)
- REG32(BBRAM_ISR, 0x38)
- FIELD(BBRAM_ISR, APB_SLVERR, 0, 1)
- REG32(BBRAM_IMR, 0x3c)
- FIELD(BBRAM_IMR, APB_SLVERR, 0, 1)
- REG32(BBRAM_IER, 0x40)
- FIELD(BBRAM_IER, APB_SLVERR, 0, 1)
- REG32(BBRAM_IDR, 0x44)
- FIELD(BBRAM_IDR, APB_SLVERR, 0, 1)
- REG32(BBRAM_MSW_LOCK, 0x4c)
- FIELD(BBRAM_MSW_LOCK, VAL, 0, 1)
- #define R_MAX (R_BBRAM_MSW_LOCK + 1)
- #define RAM_MAX (A_BBRAM_8 + 4 - A_BBRAM_0)
- #define BBRAM_PGM_MAGIC 0x757bdf0d
- QEMU_BUILD_BUG_ON(R_MAX != ARRAY_SIZE(((XlnxBBRam *)0)->regs));
- static bool bbram_msw_locked(XlnxBBRam *s)
- {
- return ARRAY_FIELD_EX32(s->regs, BBRAM_MSW_LOCK, VAL) != 0;
- }
- static bool bbram_pgm_enabled(XlnxBBRam *s)
- {
- return ARRAY_FIELD_EX32(s->regs, BBRAM_STATUS, PGM_MODE) != 0;
- }
- static void bbram_bdrv_error(XlnxBBRam *s, int rc, gchar *detail)
- {
- Error *errp = NULL;
- error_setg_errno(&errp, -rc, "%s: BBRAM backstore %s failed.",
- blk_name(s->blk), detail);
- error_report("%s", error_get_pretty(errp));
- error_free(errp);
- g_free(detail);
- }
- static void bbram_bdrv_read(XlnxBBRam *s, Error **errp)
- {
- uint32_t *ram = &s->regs[R_BBRAM_0];
- int nr = RAM_MAX;
- if (!s->blk) {
- return;
- }
- s->blk_ro = !blk_supports_write_perm(s->blk);
- if (!s->blk_ro) {
- int rc;
- rc = blk_set_perm(s->blk,
- (BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE),
- BLK_PERM_ALL, NULL);
- if (rc) {
- s->blk_ro = true;
- }
- }
- if (s->blk_ro) {
- warn_report("%s: Skip saving updates to read-only BBRAM backstore.",
- blk_name(s->blk));
- }
- if (blk_pread(s->blk, 0, nr, ram, 0) < 0) {
- error_setg(errp,
- "%s: Failed to read %u bytes from BBRAM backstore.",
- blk_name(s->blk), nr);
- return;
- }
- /* Convert from little-endian backstore for each 32-bit word */
- nr /= 4;
- while (nr--) {
- ram[nr] = le32_to_cpu(ram[nr]);
- }
- }
- static void bbram_bdrv_sync(XlnxBBRam *s, uint64_t hwaddr)
- {
- uint32_t le32;
- unsigned offset;
- int rc;
- assert(A_BBRAM_0 <= hwaddr && hwaddr <= A_BBRAM_8);
- /* Backstore is always in little-endian */
- le32 = cpu_to_le32(s->regs[hwaddr / 4]);
- /* Update zeroized flag */
- if (le32 && (hwaddr != A_BBRAM_8 || s->bbram8_wo)) {
- ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 0);
- }
- if (!s->blk || s->blk_ro) {
- return;
- }
- offset = hwaddr - A_BBRAM_0;
- rc = blk_pwrite(s->blk, offset, 4, &le32, 0);
- if (rc < 0) {
- bbram_bdrv_error(s, rc, g_strdup_printf("write to offset %u", offset));
- }
- }
- static void bbram_bdrv_zero(XlnxBBRam *s)
- {
- int rc;
- ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, BBRAM_ZEROIZED, 1);
- if (!s->blk || s->blk_ro) {
- return;
- }
- rc = blk_make_zero(s->blk, 0);
- if (rc < 0) {
- bbram_bdrv_error(s, rc, g_strdup("zeroizing"));
- }
- /* Restore bbram8 if it is non-zero */
- if (s->regs[R_BBRAM_8]) {
- bbram_bdrv_sync(s, A_BBRAM_8);
- }
- }
- static void bbram_zeroize(XlnxBBRam *s)
- {
- int nr = RAM_MAX - (s->bbram8_wo ? 0 : 4); /* only wo bbram8 is cleared */
- memset(&s->regs[R_BBRAM_0], 0, nr);
- bbram_bdrv_zero(s);
- }
- static void bbram_update_irq(XlnxBBRam *s)
- {
- bool pending = s->regs[R_BBRAM_ISR] & ~s->regs[R_BBRAM_IMR];
- qemu_set_irq(s->irq_bbram, pending);
- }
- static void bbram_ctrl_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t val = val64;
- if (val & R_BBRAM_CTRL_ZEROIZE_MASK) {
- bbram_zeroize(s);
- /* The bit is self clearing */
- s->regs[R_BBRAM_CTRL] &= ~R_BBRAM_CTRL_ZEROIZE_MASK;
- }
- }
- static void bbram_pgm_mode_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t val = val64;
- if (val == BBRAM_PGM_MAGIC) {
- bbram_zeroize(s);
- /* The status bit is cleared only by POR */
- ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, PGM_MODE, 1);
- }
- }
- static void bbram_aes_crc_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t calc_crc;
- if (!bbram_pgm_enabled(s)) {
- /* We are not in programming mode, don't do anything */
- return;
- }
- /* Perform the AES integrity check */
- s->regs[R_BBRAM_STATUS] |= R_BBRAM_STATUS_AES_CRC_DONE_MASK;
- /*
- * Set check status.
- *
- * ZynqMP BBRAM check has a zero-u32 prepended; see:
- * https://github.com/Xilinx/embeddedsw/blob/release-2019.2/lib/sw_services/xilskey/src/xilskey_bbramps_zynqmp.c#L311
- */
- calc_crc = xlnx_efuse_calc_crc(&s->regs[R_BBRAM_0],
- (R_BBRAM_8 - R_BBRAM_0), s->crc_zpads);
- ARRAY_FIELD_DP32(s->regs, BBRAM_STATUS, AES_CRC_PASS,
- (s->regs[R_BBRAM_AES_CRC] == calc_crc));
- }
- static uint64_t bbram_key_prew(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t original_data = *(uint32_t *) reg->data;
- if (bbram_pgm_enabled(s)) {
- return val64;
- } else {
- /* We are not in programming mode, don't do anything */
- qemu_log_mask(LOG_GUEST_ERROR,
- "Not in programming mode, dropping the write\n");
- return original_data;
- }
- }
- static void bbram_key_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- bbram_bdrv_sync(s, reg->access->addr);
- }
- static uint64_t bbram_wo_postr(RegisterInfo *reg, uint64_t val)
- {
- return 0;
- }
- static uint64_t bbram_r8_postr(RegisterInfo *reg, uint64_t val)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- return s->bbram8_wo ? bbram_wo_postr(reg, val) : val;
- }
- static bool bbram_r8_readonly(XlnxBBRam *s)
- {
- return !bbram_pgm_enabled(s) || bbram_msw_locked(s);
- }
- static uint64_t bbram_r8_prew(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- if (bbram_r8_readonly(s)) {
- val64 = *(uint32_t *)reg->data;
- }
- return val64;
- }
- static void bbram_r8_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- if (!bbram_r8_readonly(s)) {
- bbram_bdrv_sync(s, A_BBRAM_8);
- }
- }
- static uint64_t bbram_msw_lock_prew(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- /* Never lock if bbram8 is wo; and, only POR can clear the lock */
- if (s->bbram8_wo) {
- val64 = 0;
- } else {
- val64 |= s->regs[R_BBRAM_MSW_LOCK];
- }
- return val64;
- }
- static void bbram_isr_postw(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- bbram_update_irq(s);
- }
- static uint64_t bbram_ier_prew(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t val = val64;
- s->regs[R_BBRAM_IMR] &= ~val;
- bbram_update_irq(s);
- return 0;
- }
- static uint64_t bbram_idr_prew(RegisterInfo *reg, uint64_t val64)
- {
- XlnxBBRam *s = XLNX_BBRAM(reg->opaque);
- uint32_t val = val64;
- s->regs[R_BBRAM_IMR] |= val;
- bbram_update_irq(s);
- return 0;
- }
- static RegisterAccessInfo bbram_ctrl_regs_info[] = {
- { .name = "BBRAM_STATUS", .addr = A_BBRAM_STATUS,
- .rsvd = 0xee,
- .ro = 0x3ff,
- },{ .name = "BBRAM_CTRL", .addr = A_BBRAM_CTRL,
- .post_write = bbram_ctrl_postw,
- },{ .name = "PGM_MODE", .addr = A_PGM_MODE,
- .post_write = bbram_pgm_mode_postw,
- },{ .name = "BBRAM_AES_CRC", .addr = A_BBRAM_AES_CRC,
- .post_write = bbram_aes_crc_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_0", .addr = A_BBRAM_0,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_1", .addr = A_BBRAM_1,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_2", .addr = A_BBRAM_2,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_3", .addr = A_BBRAM_3,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_4", .addr = A_BBRAM_4,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_5", .addr = A_BBRAM_5,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_6", .addr = A_BBRAM_6,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_7", .addr = A_BBRAM_7,
- .pre_write = bbram_key_prew,
- .post_write = bbram_key_postw,
- .post_read = bbram_wo_postr,
- },{ .name = "BBRAM_8", .addr = A_BBRAM_8,
- .pre_write = bbram_r8_prew,
- .post_write = bbram_r8_postw,
- .post_read = bbram_r8_postr,
- },{ .name = "BBRAM_SLVERR", .addr = A_BBRAM_SLVERR,
- .rsvd = ~1,
- },{ .name = "BBRAM_ISR", .addr = A_BBRAM_ISR,
- .w1c = 0x1,
- .post_write = bbram_isr_postw,
- },{ .name = "BBRAM_IMR", .addr = A_BBRAM_IMR,
- .ro = 0x1,
- },{ .name = "BBRAM_IER", .addr = A_BBRAM_IER,
- .pre_write = bbram_ier_prew,
- },{ .name = "BBRAM_IDR", .addr = A_BBRAM_IDR,
- .pre_write = bbram_idr_prew,
- },{ .name = "BBRAM_MSW_LOCK", .addr = A_BBRAM_MSW_LOCK,
- .pre_write = bbram_msw_lock_prew,
- .ro = ~R_BBRAM_MSW_LOCK_VAL_MASK,
- }
- };
- static void bbram_ctrl_reset_hold(Object *obj, ResetType type)
- {
- XlnxBBRam *s = XLNX_BBRAM(obj);
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
- if (i < R_BBRAM_0 || i > R_BBRAM_8) {
- register_reset(&s->regs_info[i]);
- }
- }
- bbram_update_irq(s);
- }
- static const MemoryRegionOps bbram_ctrl_ops = {
- .read = register_read_memory,
- .write = register_write_memory,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- };
- static void bbram_ctrl_realize(DeviceState *dev, Error **errp)
- {
- XlnxBBRam *s = XLNX_BBRAM(dev);
- if (s->crc_zpads) {
- s->bbram8_wo = true;
- }
- bbram_bdrv_read(s, errp);
- }
- static void bbram_ctrl_init(Object *obj)
- {
- XlnxBBRam *s = XLNX_BBRAM(obj);
- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- s->reg_array =
- register_init_block32(DEVICE(obj), bbram_ctrl_regs_info,
- ARRAY_SIZE(bbram_ctrl_regs_info),
- s->regs_info, s->regs,
- &bbram_ctrl_ops,
- XLNX_BBRAM_ERR_DEBUG,
- R_MAX * 4);
- sysbus_init_mmio(sbd, &s->reg_array->mem);
- sysbus_init_irq(sbd, &s->irq_bbram);
- }
- static void bbram_ctrl_finalize(Object *obj)
- {
- XlnxBBRam *s = XLNX_BBRAM(obj);
- register_finalize_block(s->reg_array);
- }
- static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
- {
- DeviceState *dev = DEVICE(obj);
- qdev_prop_drive.set(obj, v, name, opaque, errp);
- /* Fill initial data if backend is attached after realized */
- if (dev->realized) {
- bbram_bdrv_read(XLNX_BBRAM(obj), errp);
- }
- }
- static void bbram_prop_get_drive(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
- {
- qdev_prop_drive.get(obj, v, name, opaque, errp);
- }
- static void bbram_prop_release_drive(Object *obj, const char *name,
- void *opaque)
- {
- qdev_prop_drive.release(obj, name, opaque);
- }
- static const PropertyInfo bbram_prop_drive = {
- .type = "str",
- .description = "Node name or ID of a block device to use as BBRAM backend",
- .realized_set_allowed = true,
- .get = bbram_prop_get_drive,
- .set = bbram_prop_set_drive,
- .release = bbram_prop_release_drive,
- };
- static const VMStateDescription vmstate_bbram_ctrl = {
- .name = TYPE_XLNX_BBRAM,
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (const VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, XlnxBBRam, R_MAX),
- VMSTATE_END_OF_LIST(),
- }
- };
- static const Property bbram_ctrl_props[] = {
- DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *),
- DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1),
- };
- static void bbram_ctrl_class_init(ObjectClass *klass, void *data)
- {
- DeviceClass *dc = DEVICE_CLASS(klass);
- ResettableClass *rc = RESETTABLE_CLASS(klass);
- rc->phases.hold = bbram_ctrl_reset_hold;
- dc->realize = bbram_ctrl_realize;
- dc->vmsd = &vmstate_bbram_ctrl;
- device_class_set_props(dc, bbram_ctrl_props);
- }
- static const TypeInfo bbram_ctrl_info = {
- .name = TYPE_XLNX_BBRAM,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(XlnxBBRam),
- .class_init = bbram_ctrl_class_init,
- .instance_init = bbram_ctrl_init,
- .instance_finalize = bbram_ctrl_finalize,
- };
- static void bbram_ctrl_register_types(void)
- {
- type_register_static(&bbram_ctrl_info);
- }
- type_init(bbram_ctrl_register_types)
|