qemu/hw/nvram/xlnx-bbram.c
Peter Maydell ad80e36744 hw, target: Add ResetType argument to hold and exit phase methods
We pass a ResetType argument to the Resettable class enter
phase method, but we don't pass it to hold and exit, even though
the callsites have it readily available. This means that if
a device cared about the ResetType it would need to record it
in the enter phase method to use later on. Pass the type to
all three of the phase methods to avoid having to do that.

Commit created with

  for dir in hw target include; do \
      spatch --macro-file scripts/cocci-macro-file.h \
             --sp-file scripts/coccinelle/reset-type.cocci \
             --keep-comments --smpl-spacing --in-place \
             --include-headers --dir $dir; done

and no manual edits.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Luc Michel <luc.michel@amd.com>
Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
2024-04-25 10:21:06 +01:00

548 lines
15 KiB
C

/*
* 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 "sysemu/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);
RegisterInfoArray *reg_array;
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, &reg_array->mem);
sysbus_init_irq(sbd, &s->irq_bbram);
}
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 = {
.name = "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 Property bbram_ctrl_props[] = {
DEFINE_PROP("drive", XlnxBBRam, blk, bbram_prop_drive, BlockBackend *),
DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1),
DEFINE_PROP_END_OF_LIST(),
};
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,
};
static void bbram_ctrl_register_types(void)
{
type_register_static(&bbram_ctrl_info);
}
type_init(bbram_ctrl_register_types)