From b0021ebf4f2d5a9a230275c323ade6843628d466 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 Jan 2025 18:33:19 +0100 Subject: [PATCH 01/69] [ot] scripts/opentitan: spidevflash.py: add a connection idle option This may be useful to delay initial connection to the QEMU SPI device socket when QEMU start up / initial FW boot time is slow. Signed-off-by: Emmanuel Blot --- docs/opentitan/spidevflash.md | 9 +++++++-- scripts/opentitan/spidevflash.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/opentitan/spidevflash.md b/docs/opentitan/spidevflash.md index 1b6f24df5d007..26e4c9d317e40 100644 --- a/docs/opentitan/spidevflash.md +++ b/docs/opentitan/spidevflash.md @@ -6,8 +6,8 @@ ````text usage: spidevflash.py [-h] -f FILE [-a ADDRESS] [-S SOCKET] [-R RETRY_COUNT] - [-T SYNC_TIME] [-r HOST] [-p PORT] [--log-udp UDP_PORT] - [-v] [-d] + [-T SYNC_TIME] [--idle DELAY] [-t] [-r HOST] [-p PORT] + [--log-udp UDP_PORT] [-v] [-d] SPI device flasher tool. @@ -21,6 +21,8 @@ options: connection retry count (default: 1) -T, --sync-time SYNC_TIME synchronization max time (default: 5.0s) + --idle DELAY stay idle before establish connection + -t, --terminate terminate QEMU VM on completion -r, --host HOST remote host name (default: localhost) -p, --port PORT remote host TCP port (default: 8004) --log-udp UDP_PORT Log to a local UDP logger @@ -52,6 +54,9 @@ options: * `-v` can be repeated to increase verbosity of the script, mostly for debug purpose. +* `--idle` wait for the specified delay in seconds before attempting a connection to the QEMU SPI + device socket + * `--log-udp` specify a UDP port on the local host to redirect log messages, ### Examples diff --git a/scripts/opentitan/spidevflash.py b/scripts/opentitan/spidevflash.py index 7225fb0fbfd45..f19e8725cf7af 100755 --- a/scripts/opentitan/spidevflash.py +++ b/scripts/opentitan/spidevflash.py @@ -142,6 +142,8 @@ def main(): help=f'synchronization max time (default: ' f'{SpiDeviceFlasher.DEFAULT_SYNC_TIME:.1f}' f's)') + argparser.add_argument('--idle', type=float, metavar='DELAY', + help='stay idle before establish connection') argparser.add_argument('-t', '--terminate', action='store_true', help='terminate QEMU VM on completion') argparser.add_argument('-r', '--host', @@ -158,8 +160,12 @@ def main(): args = argparser.parse_args() debug = args.debug - configure_loggers(args.verbose, 'spidev', -1, 'spidev.dev', - udplog=args.log_udp) + log = configure_loggers(args.verbose, 'spidev', -1, 'spidev.dev', + udplog=args.log_udp)[0] + + if args.idle: + log.info('Idling for %.1f seconds', args.idle) + sleep(args.idle) flasher = SpiDeviceFlasher() if args.socket: From 2ff99a45088114774eac315acec8b085257ec813 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 11 Feb 2025 14:27:03 +0100 Subject: [PATCH 02/69] [ot] scripts/opentitan: timelog.py: new script to filter log time info Tiny tool to convert time absolute log time into relative log time. It enables comparing different runs time-wise with log files produced with OT scripts using the --log-time option. timelog.py < pyot.log > pyot-rel.log Signed-off-by: Emmanuel Blot --- scripts/opentitan/timelog.py | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 scripts/opentitan/timelog.py diff --git a/scripts/opentitan/timelog.py b/scripts/opentitan/timelog.py new file mode 100755 index 0000000000000..c0f77b0e8b1a0 --- /dev/null +++ b/scripts/opentitan/timelog.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2025 Rivos, Inc. +# SPDX-License-Identifier: Apache2 + +"""Tiny tool to convert time absolute log time into relative log time. + It enables comparing different runs time-wise with log files produced + with OT scripts using the --log-time option. + + timelog.py < pyot.log > pyot-rel.log + + :author: Emmanuel Blot +""" + +from time import mktime, strptime +from typing import Optional +import sys + + +def main(): + """Main routine.""" + ts_base: Optional[float] = None + for line in sys.stdin: + line = line.strip() + ts_str, msg = line.split(' ', 1) + ts_hms, ts_ms = ts_str.split('.', 1) + ts_f = mktime(strptime(ts_hms, '%H:%M:%S')) + # for some reason, strptime does not parse %f millisecond string + ts_f += float(f'.{ts_ms}') + if ts_base is None: + ts_base = ts_f + ts_rel = ts_f - ts_base + ts_srel = f'{ts_rel:.3f}' + print(f'{ts_srel:<8s} {msg}') + + +if __name__ == '__main__': + main() From c0faf82c610ead76612587b615e9a88e763f7fb4 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 10 Feb 2025 14:32:15 +0000 Subject: [PATCH 03/69] [ot] Revert "[ot] target/riscv: fix circular dependency of header file inclusion" There are header guards on both cpu.h and pmp.h which avoids any conflict on duplicated header inclusion. Signed-off-by: Rob Bradford --- target/riscv/pmp.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index 0ab0a26dd86b7..e0530a17a39bb 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -22,9 +22,7 @@ #ifndef RISCV_PMP_H #define RISCV_PMP_H -#ifndef RISCV_CPU_H -#error must include cpu.h header -#endif +#include "cpu.h" typedef enum { PMP_READ = 1 << 0, From eaa9fb8c2f16f173b97bb9945c860469e4b01254 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 3 Apr 2025 10:06:00 +0200 Subject: [PATCH 04/69] [ot] meson: disable check for strchrnul on macOS strchrnul function has been introduced in macOS 15.4, with a warning message that shows up on almost every single C file being built as: "note: 'strchrnul' has been marked as being introduced in macOS 15.4 here, but the deployment target is macOS 15.0.0" This commit hides the presence of strchrnul on macOS, so the default QEMU implementation is used instead, as it was on previous macOS versions. This is a workaround till this issue is addressed upstream. Signed-off-by: Emmanuel Blot --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5292755a3c8c1..80e8c49240f7f 100644 --- a/meson.build +++ b/meson.build @@ -2625,7 +2625,7 @@ config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice) config_host_data.set('HAVE_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) -config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) +config_host_data.set('HAVE_STRCHRNUL', host_os != 'darwin' and cc.has_function('strchrnul')) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) if rbd.found() config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS', From 041f09e4aa82bc57fb8397d7f28eeaac33bc3e71 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 8 Apr 2025 14:42:09 +0200 Subject: [PATCH 05/69] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: disable dedicated Ibex Wrapper implementation Signed-off-by: Emmanuel Blot --- hw/riscv/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 8d369e6298305..f1fb6f98699e7 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -34,7 +34,7 @@ config OT_DARJEELING select OT_GPIO_DJ select OT_HMAC select OT_I2C_DJ - select OT_IBEX_WRAPPER_DJ + select OT_IBEX_WRAPPER select OT_KMAC select OT_LC_CTRL select OT_MBX @@ -75,7 +75,7 @@ config OT_EARLGREY select OT_FLASH select OT_GPIO_EG select OT_HMAC - select OT_IBEX_WRAPPER_EG + select OT_IBEX_WRAPPER select OT_KMAC select OT_LC_CTRL select OT_OTBN From 60993bd39336fc9982dde8aeea618bd24df8b9fb Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 7 Apr 2025 19:22:03 +0200 Subject: [PATCH 06/69] [ot] hw/opentitan: ot_ibex_wrapper: unify EarlGrey and Darjeeling implementations. Merge both implementation into a single one with dynamic register definition. The code is less readable, but there is no longer two concurrent implementations of the same IP for the sake of managing different number of remapping regions. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper.c | 1471 +++++++++++++++++++++++- hw/opentitan/trace-events | 2 +- include/hw/opentitan/ot_ibex_wrapper.h | 11 +- 3 files changed, 1467 insertions(+), 17 deletions(-) diff --git a/hw/opentitan/ot_ibex_wrapper.c b/hw/opentitan/ot_ibex_wrapper.c index 793ff1aa3842b..4dab792ef93db 100644 --- a/hw/opentitan/ot_ibex_wrapper.c +++ b/hw/opentitan/ot_ibex_wrapper.c @@ -1,10 +1,11 @@ /* * QEMU OpenTitan Ibex wrapper device * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): + * Emmanuel Blot * Loïc Lefort * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -27,14 +28,1472 @@ */ #include "qemu/osdep.h" +#include +#include "qemu/log.h" +#include "qemu/typedefs.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_edn.h" #include "hw/opentitan/ot_ibex_wrapper.h" +#include "hw/qdev-properties-system.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ibex_irq.h" +#include "hw/sysbus.h" +#include "sysemu/runstate.h" +#include "trace.h" -OT_OBJECT_DEFINE_ABSTRACT_TYPE(OtIbexWrapperState, OtIbexWrapperClass, - ot_ibex_wrapper, OT_IBEX_WRAPPER, SYS_BUS_DEVICE) +/* DEBUG: define to print the full memory view on remap */ +#undef PRINT_MTREE +/* DEBUG: define to print the enumerated list of registers */ +#undef PRINT_REGNAMES -static void ot_ibex_wrapper_class_init(ObjectClass *oc, void *data) {} +#define NUM_SW_ALERTS 2u +#define NUM_ALERTS 4u -static void ot_ibex_wrapper_init(Object *obj) {} +/* clang-format off */ +REG32(ALERT_TEST, 0x0u) + FIELD(ALERT_TEST, FATAL_SW, 0u, 1u) + FIELD(ALERT_TEST, RECOV_SW, 1u, 1u) + FIELD(ALERT_TEST, FATAL_HW, 2u, 1u) + FIELD(ALERT_TEST, RECOV_HW, 3u, 1u) +REG32(SW_RECOV_ERR, 0x4u) + FIELD(SW_RECOV_ERR, VAL, 0u, 4u) +REG32(SW_FATAL_ERR, 0x8u) + FIELD(SW_FATAL_ERR, VAL, 0u, 4u) +SHARED_FIELD(REGWEN_EN, 0u, 1u) +SHARED_FIELD(ADDR_EN, 0u, 1u) +SHARED_FIELD(NMI_ALERT_EN, 0u, 1u) +SHARED_FIELD(NMI_WDOG_EN, 1u, 1u) +SHARED_FIELD(ERR_STATUS_REG_INTG, 0u, 1u) +SHARED_FIELD(ERR_STATUS_FATAL_INTG, 8u, 1u) +SHARED_FIELD(ERR_STATUS_FATAL_CORE, 9u, 1u) +SHARED_FIELD(ERR_STATUS_RECOV_CORE, 10u, 1u) +SHARED_FIELD(RND_STATUS_RND_DATA_VALID, 0u, 1u) +SHARED_FIELD(RND_STATUS_RND_DATA_FIPS, 1u, 1u) +SHARED_FIELD(DV_SIM_STATUS_CODE, 0u, 16u) +SHARED_FIELD(DV_SIM_STATUS_INFO, 16u, 16u) +/* clang-format on */ -static void ot_ibex_wrapper_finalize(Object *obj) {} +#define ALERT_TEST_MASK \ + (R_ALERT_TEST_FATAL_SW_MASK | R_ALERT_TEST_RECOV_SW_MASK | \ + R_ALERT_TEST_FATAL_HW_MASK | R_ALERT_TEST_RECOV_HW_MASK) + +#define ERR_STATUS_MASK \ + (ERR_STATUS_REG_INTG_MASK | ERR_STATUS_FATAL_INTG_MASK | \ + ERR_STATUS_FATAL_CORE_MASK | ERR_STATUS_RECOV_CORE_MASK) + +#define NMI_MASK (NMI_ALERT_EN_MASK | NMI_WDOG_EN_MASK) + +/* OpenTitan SW log severities. */ +typedef enum { + TEST_LOG_SEVERITY_INFO, + TEST_LOG_SEVERITY_WARN, + TEST_LOG_SEVERITY_ERROR, + TEST_LOG_SEVERITY_FATAL, +} OtIbexTestLogLevel; + +/* OpenTitan SW log metadata used to format a log line. */ +typedef struct { + OtIbexTestLogLevel severity; + uint32_t file_name_ptr; /* const char * in RV32 */ + uint32_t line; + uint32_t nargs; + uint32_t format_ptr; /* const char * in RV32 */ +} OtIbexTestLogFields; + +typedef enum { + TEST_LOG_STATE_IDLE, + TEST_LOG_STATE_ARG, + TEST_LOG_STATE_ERROR, +} OtIbexTestLogState; + +/* + * These enumerated values are not HW values, however the two last values are + * documented by DV SW as:"This is a terminal state. Any code appearing after + * this value is set is unreachable." + * + * There are therefore handled as special HW-SW case that triggers explicit + * QEMU termination with a special exit code. + */ +typedef enum { + TEST_STATUS_IN_BOOT_ROM = 0xb090, /* 'bogo', BOotrom GO */ + TEST_STATUS_IN_BOOT_ROM_HALT = 0xb057, /* 'bost', BOotrom STop */ + TEST_STATUS_IN_TEST = 0x4354, /* 'test' */ + TEST_STATUS_IN_WFI = 0x1d1e, /* 'idle' */ + TEST_STATUS_PASSED = 0x900d, /* 'good' */ + TEST_STATUS_FAILED = 0xbaad /* 'baad' */ +} OtIbexTestStatus; + +typedef enum { + ACCESS_INSN, + ACCESS_DATA, + ACCESS_COUNT, +} OtIbexRemapAccess; + +enum { + DV_SIM_STATUS, + DV_SIM_LOG, + DV_SIM_WIN2, + DV_SIM_WIN3, + DV_SIM_WIN4, + DV_SIM_WIN5, + DV_SIM_WIN6, + DV_SIM_WIN7, + DV_SIM_COUNT, +}; + +typedef struct { + OtIbexTestLogState state; + AddressSpace *as; + OtIbexTestLogFields fields; + unsigned arg_count; + uintptr_t *args; /* arguments */ + bool *strargs; /* whether slot should be freed or a not */ + const char *fmtptr; /* current pointer in format string */ + char *filename; + char *format; +} OtIbexTestLogEngine; + +typedef uint32_t (*ot_ibex_wrapper_reg_read_fn)(OtIbexWrapperState *s, + unsigned reg); +typedef void (*ot_ibex_wrapper_reg_write_fn)(OtIbexWrapperState *s, + unsigned reg, uint32_t value); + +typedef struct { + uint32_t regwen; + uint32_t addr_en; + uint32_t addr_matching; + uint32_t remap_addr; +} OtIbexRemap; + +typedef struct { + uint32_t alert_test; + uint32_t sw_recov_err; + uint32_t sw_fatal_err; + /* physical location for remapper regs are moved to the end of struct */ + uint32_t nmi_enable; + uint32_t nmi_state; + uint32_t err_status; + uint32_t rnd_data; + uint32_t rnd_status; + uint32_t fpga_info; + uint32_t _reserved[7u]; + uint32_t dv_sim_win[DV_SIM_COUNT]; + /* note: remap pointer introduce padding here */ + OtIbexRemap *remap[ACCESS_COUNT]; /* num_regions */ +} OtIbexWrapperRegs; + +typedef struct { + ot_ibex_wrapper_reg_read_fn read; + ot_ibex_wrapper_reg_write_fn write; + uint32_t mask; /* the mask to apply to the written value */ +} OtIbexWrapperAccess; + +struct OtIbexWrapperState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + MemoryRegion *remappers; + MemoryRegion *sys_mem; + IbexIRQ alerts[NUM_ALERTS]; + + OtIbexTestLogEngine *log_engine; + OtIbexWrapperRegs regs; /* not ordered by register index */ + OtIbexWrapperAccess *access_table; /* ordered by register index */ + uint32_t **access_regs; /* ordered by register index */ + char **reg_names; /* ordered by register index */ + CPUState *cpu; + unsigned reg_count; /* total count of registers */ + unsigned remap_reg_count; /* count of remapping registers */ + uint8_t cpu_en_bm; + bool entropy_requested; + bool edn_connected; + bool esc_rx; + + char *ot_id; + char *lc_ignore_ids; + OtEDNState *edn; + uint8_t num_regions; + uint8_t edn_ep; + uint8_t qemu_version; + bool lc_ignore; + CharBackend chr; +}; + +struct OtIbexWrapperClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + +#define OT_IBEX_CPU_EN_MASK (((1u << OT_IBEX_CPU_EN_COUNT)) - 1u) + +static const char MISSING_LOG_STRING[] = "(?)"; + +#define CASE_RANGE(_reg_, _cnt_) (_reg_)...((_reg_) + (_cnt_) - (1u)) + +#define xtrace_ot_ibex_wrapper_info(_s_, _msg_) \ + trace_ot_ibex_wrapper_info((_s_)->ot_id, __func__, __LINE__, _msg_) +#define xtrace_ot_ibex_wrapper_error(_s_, _msg_) \ + trace_ot_ibex_wrapper_error((_s_)->ot_id, __func__, __LINE__, _msg_) + +#define REG_NAME_LENGTH 24u /* > "DBUS_ADDR_MATCHING_31" */ + +#define LAST_STATIC_REG sw_fatal_err +#define FIRST_REMAP_REG_POS ((LAST_STATIC_REG_POS) + 1u) +#define LAST_STATIC_REG_POS R32_POS(LAST_STATIC_REG) +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) +#define R32_POS(_r_) (offsetof(OtIbexWrapperRegs, _r_) / sizeof(uint32_t)) +#define R32_DYN_OFFSET(_s_, _r_) \ + (R32_POS(_r_) <= LAST_STATIC_REG_POS ? 0 : s->remap_reg_count) +#define R32_DYN_POS(_s_, _r_) (R32_POS(_r_) + R32_DYN_OFFSET(_s_, _r_)) +#define REG_NAME(_s_, _reg_) \ + ((_reg_) < (_s_)->reg_count ? (_s_)->reg_names[(_reg_)] : "?") +#define CREATE_NAME_REG_AT(_s_, _off_, _reg_) \ + strncpy((_s_)->reg_names[_off_], stringify(_reg_), REG_NAME_LENGTH - 1) +#define CREATE_NAME_REG(_s_, _reg_) CREATE_NAME_REG_AT(_s_, R_##_reg_, _reg_) +#define CREATE_NAME_REG_IX_AT(_s_, _pfx_, _off_, _ix_, _reg_) \ + do { \ + int l = snprintf((_s_)->reg_names[(_off_)], REG_NAME_LENGTH, \ + "%s" stringify(_reg_) "_%u", (_pfx_), (_ix_)); \ + g_assert((unsigned)l < REG_NAME_LENGTH); \ + } while (0) + +/* + * Alert management + */ + +static void ot_ibex_wrapper_update_alerts(OtIbexWrapperState *s) +{ + uint32_t level = s->regs.alert_test; + + if (s->regs.sw_fatal_err != OT_MULTIBITBOOL4_FALSE) { + level |= R_SW_FATAL_ERR_VAL_MASK; + } + + for (unsigned ix = 0; ix < NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + +/* + * Entropy management (RND) + */ + +static void ot_ibex_wrapper_fill_entropy(void *opaque, uint32_t bits, bool fips) +{ + OtIbexWrapperState *s = opaque; + + trace_ot_ibex_wrapper_fill_entropy(s->ot_id, bits, fips); + + s->regs.rnd_data = bits; + s->regs.rnd_status = RND_STATUS_RND_DATA_VALID_MASK; + if (fips) { + s->regs.rnd_status |= RND_STATUS_RND_DATA_FIPS_MASK; + } + + s->entropy_requested = false; +} + +static bool ot_ibex_wrapper_has_edn(OtIbexWrapperState *s) +{ + return (s->edn != NULL) && (s->edn_ep != UINT8_MAX); +} + +static void ot_ibex_wrapper_request_entropy(OtIbexWrapperState *s) +{ + if (!s->entropy_requested && ot_ibex_wrapper_has_edn(s)) { + if (unlikely(!s->edn_connected)) { + ot_edn_connect_endpoint(s->edn, s->edn_ep, + &ot_ibex_wrapper_fill_entropy, s); + s->edn_connected = true; + } + s->entropy_requested = true; + trace_ot_ibex_wrapper_request_entropy(s->ot_id, s->entropy_requested); + if (ot_edn_request_entropy(s->edn, s->edn_ep)) { + s->entropy_requested = false; + xtrace_ot_ibex_wrapper_error(s, "failed to request entropy"); + } + } +} + +/* + * Address translation management (legacy mode using memory region aliases) + */ + +static void +ot_ibex_wrapper_remapper_destroy(OtIbexWrapperState *s, unsigned slot) +{ + g_assert(slot < (unsigned)s->num_regions); + MemoryRegion *mr = &s->remappers[slot]; + if (memory_region_is_mapped(mr)) { + trace_ot_ibex_wrapper_unmap(s->ot_id, slot); + memory_region_transaction_begin(); + memory_region_set_enabled(mr, false); + /* QEMU memory model enables unparenting alias regions */ + memory_region_del_subregion(s->sys_mem, mr); + memory_region_transaction_commit(); + } +} + +/* NOLINTNEXTLINE */ +static bool ot_ibex_wrapper_mr_map_offset(hwaddr *offset, + const MemoryRegion *root, hwaddr dst, + size_t size, const MemoryRegion *tmr) +{ + if (root == tmr) { + return true; + } + + const MemoryRegion *mr; + + QTAILQ_FOREACH(mr, &root->subregions, subregions_link) { + if (dst < mr->addr || + (dst + size) > (mr->addr + int128_getlo(mr->size))) { + continue; + } + + bool ret; + + if (mr->alias) { + hwaddr alias_offset = mr->addr - mr->alias_offset; + dst -= alias_offset; + + ret = ot_ibex_wrapper_mr_map_offset(offset, mr->alias, dst, size, + tmr); + if (ret) { + /* + * the selected MR tree leads to the target region, so update + * the alias offset with the local offset + */ + *offset += alias_offset; + } + } else { + ret = ot_ibex_wrapper_mr_map_offset(offset, mr, dst, size, tmr); + if (ret) { + *offset += mr->addr; + } + } + + return ret; + } + + return false; +} + +static void ot_ibex_wrapper_remapper_create( + OtIbexWrapperState *s, unsigned slot, hwaddr dst, hwaddr src, size_t size) +{ + g_assert(slot < (unsigned)s->num_regions); + MemoryRegion *mr = &s->remappers[slot]; + g_assert(!memory_region_is_mapped(mr)); + + int priority = (int)((unsigned)s->num_regions - slot); + + MemoryRegion *mr_dst; + + char *name = g_strdup_printf(TYPE_OT_IBEX_WRAPPER "-remap[%u]", slot); + + memory_region_transaction_begin(); + /* + * try to map onto the actual device if there's a single one, otherwise + * map on the whole address space. + */ + MemoryRegionSection mrs; + mrs = memory_region_find(s->sys_mem, dst, (uint64_t)size); + size_t mrs_lsize = int128_getlo(mrs.size); + mr_dst = (mrs.mr && mrs_lsize >= size) ? mrs.mr : s->sys_mem; + + /* + * adjust the offset if the memory region target for the mapping + * is itself mapped through memory region(s) + */ + hwaddr offset = 0; + if (ot_ibex_wrapper_mr_map_offset(&offset, s->sys_mem, dst, size, mr_dst)) { + offset = dst - offset; + } + + trace_ot_ibex_wrapper_map(s->ot_id, slot, src, dst, size, mr_dst->name, + (uint32_t)offset); + memory_region_init_alias(mr, OBJECT(s), name, mr_dst, offset, + (uint64_t)size); + memory_region_add_subregion_overlap(s->sys_mem, src, mr, priority); + memory_region_set_enabled(mr, true); + memory_region_transaction_commit(); + g_free(name); + +#ifdef PRINT_MTREE + mtree_info(false, false, false, true); +#endif +} + +static void ot_ibex_wrapper_update_remap_mr( + OtIbexWrapperState *s, OtIbexRemapAccess access, unsigned slot) +{ + g_assert(slot < (unsigned)s->num_regions); + /* + * Warning: + * for now, QEMU is unable to distinguish instruction or data access. + * in this implementation, we chose to enable remap whenever either D or I + * remapping is selected, and both D & I configuration match; we disable + * translation when both D & I are remapping are disabled + */ + (void)access; + + bool en_remap_i = s->regs.remap[ACCESS_INSN][slot].addr_en; + bool en_remap_d = s->regs.remap[ACCESS_DATA][slot].addr_en; + if (!en_remap_i && !en_remap_d) { + /* disable */ + ot_ibex_wrapper_remapper_destroy(s, slot); + } else { + uint32_t src_match_i = s->regs.remap[ACCESS_INSN][slot].addr_matching; + uint32_t src_match_d = s->regs.remap[ACCESS_DATA][slot].addr_matching; + if (src_match_i != src_match_d) { + /* I and D do not match, do nothing */ + xtrace_ot_ibex_wrapper_info(s, "src remapping do not match"); + return; + } + uint32_t remap_addr_i = s->regs.remap[ACCESS_INSN][slot].remap_addr; + uint32_t remap_addr_d = s->regs.remap[ACCESS_DATA][slot].remap_addr; + if (remap_addr_i != remap_addr_d) { + /* I and D do not match, do nothing */ + xtrace_ot_ibex_wrapper_info(s, "dst remapping do not match"); + return; + } + /* enable */ + uint32_t map_size = (-src_match_i & (src_match_i + 1u)) << 1u; + uint32_t map_mask = ~(map_size - 1u); + uint32_t src_base = src_match_i & map_mask; + uint32_t dst_base = remap_addr_i & map_mask; + + ot_ibex_wrapper_remapper_destroy(s, slot); + ot_ibex_wrapper_remapper_create(s, slot, (hwaddr)dst_base, + (hwaddr)src_base, (size_t)map_size); + } +} + +/* + * DV logging management + */ + +static bool +ot_ibex_wrapper_log_load_string(OtIbexWrapperState *s, hwaddr addr, char **str) +{ + OtIbexTestLogEngine *eng = s->log_engine; + + /* + * Logging needs to access strings that are stored in guest memory. + * This function adopts a "best effort" strategy: it may fails to retrieve + * a log string argument. + */ + bool res = false; + MemoryRegionSection mrs; + + /* + * Find the region where the string may reside, using a small size as the + * length of the string is not known, and memory_region_find would fail if + * look up is performed behing the end of the containing memory region + */ + mrs = memory_region_find(eng->as->root, addr, 4u); + MemoryRegion *mr = mrs.mr; + if (!mr) { + xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); + goto end; + } + + if (!memory_region_is_ram(mr)) { + xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); + goto end; + } + + uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); + if (!src) { + xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); + goto end; + } + src += mrs.offset_within_region; + + size_t size = int128_getlo(mrs.size) - mrs.offset_within_region; + size = MIN(size, 4096u); + + const void *end = memchr((const void *)src, '\0', size); + if (!end) { + xtrace_ot_ibex_wrapper_error(s, "cannot compute strlen"); + goto end; + } + size_t slen = (uintptr_t)end - (uintptr_t)src; + + char *tstr = g_malloc(slen + 1); + memcpy(tstr, (const void *)src, slen); + tstr[slen] = '\0'; + + *str = tstr; + res = true; + +end: + if (mr) { + memory_region_unref(mr); + } + return res; +} + +static bool ot_ibex_wrapper_log_load_fields(OtIbexWrapperState *s, hwaddr addr) +{ + OtIbexTestLogEngine *eng = s->log_engine; + + MemoryRegionSection mrs; + mrs = memory_region_find(eng->as->root, addr, sizeof(eng->fields)); + + MemoryRegion *mr = mrs.mr; + bool res = false; + + if (!mr) { + xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); + goto end; + } + + if (!memory_region_is_ram(mr)) { + xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); + goto end; + } + + uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); + if (!src) { + xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); + goto end; + } + src += mrs.offset_within_region; + + memcpy(&eng->fields, (const void *)src, sizeof(eng->fields)); + + if (eng->fields.file_name_ptr) { + if (!ot_ibex_wrapper_log_load_string(s, + (uintptr_t) + eng->fields.file_name_ptr, + &eng->filename)) { + xtrace_ot_ibex_wrapper_error(s, "cannot get filename"); + goto end; + } + } + + if (eng->fields.format_ptr) { + if (!ot_ibex_wrapper_log_load_string(s, + (uintptr_t)eng->fields.format_ptr, + &eng->format)) { + xtrace_ot_ibex_wrapper_error(s, "cannot get format string"); + goto end; + } + } + + eng->arg_count = 0; + eng->fmtptr = eng->format; + if (eng->fields.nargs) { + eng->args = g_new0(uintptr_t, eng->fields.nargs); + eng->strargs = g_new0(bool, eng->fields.nargs); + } else { + eng->args = NULL; + eng->strargs = NULL; + } + + res = true; + +end: + if (mr) { + memory_region_unref(mr); + } + return res; +} + +static bool ot_ibex_wrapper_log_load_arg(OtIbexWrapperState *s, uint32_t value) +{ + OtIbexTestLogEngine *eng = s->log_engine; + + if (!eng->fmtptr) { + xtrace_ot_ibex_wrapper_error(s, "invalid fmtptr"); + return false; + } + + bool cont; + do { + cont = false; + eng->fmtptr = strchr(eng->fmtptr, '%'); + if (!eng->fmtptr) { + xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); + return false; + } + eng->fmtptr++; + switch (*eng->fmtptr) { + case '%': + eng->fmtptr++; + cont = true; + continue; + case '\0': + xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); + return false; + case 's': + if (!ot_ibex_wrapper_log_load_string(s, (hwaddr)value, + (char **)&eng + ->args[eng->arg_count])) { + xtrace_ot_ibex_wrapper_error(s, "cannot load string arg"); + /* use a default string, best effort strategy */ + eng->args[eng->arg_count] = (uintptr_t)&MISSING_LOG_STRING[0]; + } else { + /* string has been dynamically allocated, and should be freed */ + eng->strargs[eng->arg_count] = true; + } + break; + default: + eng->args[eng->arg_count] = (uintptr_t)value; + break; + } + } while (cont); + + eng->arg_count++; + + return true; +} + +static void ot_ibex_wrapper_log_cleanup(OtIbexWrapperState *s) +{ + OtIbexTestLogEngine *eng = s->log_engine; + + if (eng->strargs && eng->args) { + for (unsigned ix = 0; ix < eng->fields.nargs; ix++) { + if (eng->strargs[ix]) { + if (eng->args[ix]) { + g_free((void *)eng->args[ix]); + } + } + } + } + g_free(eng->format); + g_free(eng->filename); + g_free(eng->strargs); + g_free(eng->args); + eng->format = NULL; + eng->filename = NULL; + eng->fmtptr = NULL; + eng->strargs = NULL; + eng->args = NULL; +} + +static void ot_ibex_wrapper_log_emit(OtIbexWrapperState *s) +{ + OtIbexTestLogEngine *eng = s->log_engine; + + const char *level; + switch (eng->fields.severity) { + case TEST_LOG_SEVERITY_INFO: + level = "INFO"; + break; + case TEST_LOG_SEVERITY_WARN: + level = "WARN "; + break; + case TEST_LOG_SEVERITY_ERROR: + level = "ERROR "; + break; + case TEST_LOG_SEVERITY_FATAL: + level = "FATAL "; + break; + default: + level = "DEBUG "; + break; + } + + /* discard the path of the stored file to reduce log message length */ + const char *basename = eng->filename ? strrchr(eng->filename, '/') : NULL; + basename = basename ? basename + 1u : eng->filename; + + char *logfmt = g_strdup_printf("%s %s:%d %s\n", level, basename, + eng->fields.line, eng->format); + +/* hack ahead: use the uintptr_t array as a va_list */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" + char *logmsg = g_strdup_vprintf(logfmt, (char *)eng->args); +#pragma GCC diagnostic pop + + if (!qemu_chr_fe_backend_connected(&s->chr)) { + qemu_log_mask(LOG_STRACE, "%s", logmsg); + } else { + qemu_chr_fe_write(&s->chr, (const uint8_t *)logmsg, + (int)strlen(logmsg)); + } + + g_free(logmsg); + g_free(logfmt); + + ot_ibex_wrapper_log_cleanup(s); +} + +static void ot_ibex_wrapper_status_report(OtIbexWrapperState *s, uint32_t value) +{ + const char *msg; + switch (value) { + case TEST_STATUS_IN_BOOT_ROM: + msg = "IN_BOOT_ROM"; + break; + case TEST_STATUS_IN_BOOT_ROM_HALT: + msg = "IN_BOOT_ROM_HALT"; + break; + case TEST_STATUS_IN_TEST: + msg = "IN_TEST"; + break; + case TEST_STATUS_IN_WFI: + msg = "IN_BOOT_WFI"; + break; + case TEST_STATUS_PASSED: + msg = "PASSED"; + break; + case TEST_STATUS_FAILED: + msg = "FAILED"; + break; + default: + msg = "UNKNOWN"; + break; + } + + if (!qemu_chr_fe_backend_connected(&s->chr)) { + qemu_log_mask(LOG_STRACE, "%s\n", msg); + } else { + qemu_chr_fe_write(&s->chr, (const uint8_t *)msg, (int)strlen(msg)); + uint8_t eol[] = { '\n' }; + qemu_chr_fe_write(&s->chr, eol, (int)sizeof(eol)); + } +} + +static void ot_ibex_wrapper_log_handle(OtIbexWrapperState *s, uint32_t value) +{ + /* + * Note about logging: + * + * For OT DV logging to work, the "fields" should not be placed in the + * default linker-discarded sections such as ".logs.fields" + * i.e. __attribute__((section(".logs.fields"))) should be removed from + * the "LOG()"" macro. + */ + OtIbexTestLogEngine *eng = s->log_engine; + + switch (eng->state) { + case TEST_LOG_STATE_IDLE: + if (!ot_ibex_wrapper_log_load_fields(s, (hwaddr)value)) { + eng->state = TEST_LOG_STATE_ERROR; + ot_ibex_wrapper_log_cleanup(s); + break; + } + if (eng->fields.nargs) { + eng->state = TEST_LOG_STATE_ARG; + } else { + ot_ibex_wrapper_log_emit(s); + eng->state = TEST_LOG_STATE_IDLE; + } + break; + case TEST_LOG_STATE_ARG: + if (!ot_ibex_wrapper_log_load_arg(s, value)) { + ot_ibex_wrapper_log_cleanup(s); + eng->state = TEST_LOG_STATE_ERROR; + } + if (eng->arg_count == eng->fields.nargs) { + ot_ibex_wrapper_log_emit(s); + eng->state = TEST_LOG_STATE_IDLE; + } + break; + case TEST_LOG_STATE_ERROR: + default: + qemu_log_mask(LOG_GUEST_ERROR, "Can no longer handle DV log, in error"); + break; + } +} + +/* + * vCPU management + */ + +static void ot_ibex_wrapper_update_exec(OtIbexWrapperState *s) +{ + /* + * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and + * power manager CPU enable are all enabled." + */ + bool enable = + ((s->cpu_en_bm & OT_IBEX_CPU_EN_MASK) == OT_IBEX_CPU_EN_MASK) && + !s->esc_rx; + trace_ot_ibex_wrapper_update_exec(s->ot_id ?: "", s->cpu_en_bm, s->esc_rx, + enable); + + if (enable) { + s->cpu->halted = 0; + if (s->cpu->held_in_reset) { + resettable_release_reset(OBJECT(s->cpu), RESET_TYPE_COLD); + } + cpu_resume(s->cpu); + } else { + if (!s->cpu->halted) { + s->cpu->halted = 1; + cpu_exit(s->cpu); + } + } +} + +static void ot_ibex_wrapper_cpu_enable_recv(void *opaque, int n, int level) +{ + OtIbexWrapperState *s = opaque; + + g_assert((unsigned)n < OT_IBEX_CPU_EN_COUNT); + + if (level) { + s->cpu_en_bm |= 1u << (unsigned)n; + } else { + s->cpu_en_bm &= ~(1u << (unsigned)n); + } + + /* + * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and + * power manager CPU enable are all enabled." + */ + trace_ot_ibex_wrapper_cpu_enable(s->ot_id ?: "", n ? "PWR" : "LC", + (bool)level); + + ot_ibex_wrapper_update_exec(s); +} + +static void ot_ibex_wrapper_escalate_rx(void *opaque, int n, int level) +{ + OtIbexWrapperState *s = opaque; + + g_assert(n == 0); + + trace_ot_ibex_wrapper_escalate_rx(s->ot_id ?: "", (bool)level); + + s->esc_rx = (bool)level; + + ot_ibex_wrapper_update_exec(s); +} + +/* + * I/O + */ + +static uint32_t ot_ibex_wrapper_read_zero(OtIbexWrapperState *s, unsigned reg) +{ + (void)s; + (void)reg; + + return 0u; +} + +static uint32_t ot_ibex_wrapper_read_reg(OtIbexWrapperState *s, unsigned reg) +{ + g_assert(reg < s->reg_count); + + return *s->access_regs[reg]; +} + +static uint32_t +ot_ibex_wrapper_read_rnd_data(OtIbexWrapperState *s, unsigned reg) +{ + (void)reg; + + if (!ot_ibex_wrapper_has_edn(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No EDN connection\n", __func__); + return 0; + } + + uint32_t value = s->regs.rnd_data; + if (!(s->regs.rnd_status & RND_STATUS_RND_DATA_VALID_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read invalid entropy data 0x%08x\n", + __func__, value); + } + s->regs.rnd_data = 0; + s->regs.rnd_status = 0; + + ot_ibex_wrapper_request_entropy(s); + + return *s->access_regs[reg]; +} + +static uint32_t +ot_ibex_wrapper_read_rnd_status(OtIbexWrapperState *s, unsigned reg) +{ + (void)reg; + + if (!ot_ibex_wrapper_has_edn(s)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No EDN connection\n", __func__); + return 0; + } + + if (!(s->regs.rnd_status & RND_STATUS_RND_DATA_VALID_MASK)) { + ot_ibex_wrapper_request_entropy(s); + } + + return s->regs.rnd_status; +} + +static void ot_ibex_wrapper_write_reg(OtIbexWrapperState *s, unsigned reg, + uint32_t value) +{ + *s->access_regs[reg] = value; +} + +static void ot_ibex_wrapper_write_ro(OtIbexWrapperState *s, unsigned reg, + uint32_t value) +{ + (void)value; + + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: %s is a R/O register\n", __func__, + s->ot_id, REG_NAME(s, reg)); +} + +static void ot_ibex_wrapper_write_alert_test(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + (void)reg; + + s->regs.alert_test = value; + ot_ibex_wrapper_update_alerts(s); + s->regs.alert_test = 0u; + ot_ibex_wrapper_update_alerts(s); +} + +static void ot_ibex_wrapper_write_sw_recov_err(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + (void)reg; + + s->regs.sw_recov_err = value; + ot_ibex_wrapper_update_alerts(s); + s->regs.sw_recov_err = OT_MULTIBITBOOL4_FALSE; + ot_ibex_wrapper_update_alerts(s); +} + +static void ot_ibex_wrapper_write_sw_fatal_err(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + (void)reg; + + /* QEMU extenson */ + if ((value >> 16u) == 0xC0DEu) { + /* guest should now use DV_SIM_STATUS register */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: QEMU exit on SW_FATAL_ERR is deprecated", + __func__, s->ot_id); + /* discard MSB magic */ + value &= UINT16_MAX; + /* discard multibool4false mark */ + value >>= 4u; + /* std exit code should be in [0..127] range */ + if (value > 127u) { + value = 127u; + } + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + (int)value); + } + + /* real HW */ + value &= R_SW_FATAL_ERR_VAL_MASK; + s->regs.sw_fatal_err = + ot_multibitbool_w1s_write(s->regs.sw_fatal_err, value, 4u); + ot_ibex_wrapper_update_alerts(s); +} + +static void ot_ibex_wrapper_write_regwen(OtIbexWrapperState *s, unsigned reg, + uint32_t value) +{ + g_assert(reg >= FIRST_REMAP_REG_POS); + reg -= FIRST_REMAP_REG_POS; + unsigned access = reg / (R32_OFF(sizeof(OtIbexRemap)) * s->num_regions); + unsigned group = reg % (R32_OFF(sizeof(OtIbexRemap)) * s->num_regions); + unsigned region = group % s->num_regions; + g_assert(access < ACCESS_COUNT); + g_assert(region < s->num_regions); + + s->regs.remap[access][region].regwen &= value; /* RW0C */ +} + +static void +ot_ibex_wrapper_write_remap(OtIbexWrapperState *s, unsigned reg, uint32_t value) +{ + g_assert(reg >= FIRST_REMAP_REG_POS); + reg -= FIRST_REMAP_REG_POS; + unsigned access = reg / (R32_OFF(sizeof(OtIbexRemap)) * s->num_regions); + unsigned group = reg % (R32_OFF(sizeof(OtIbexRemap)) * s->num_regions); + unsigned offset = group / s->num_regions; + unsigned region = group % s->num_regions; + g_assert(access < ACCESS_COUNT); + + if (offset != offsetof(OtIbexRemap, regwen) && + !s->regs.remap[access][region].regwen) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: %s protected w/ REGWEN\n", + __func__, s->ot_id, REG_NAME(s, reg)); + return; + } + + switch (offset) { + case R32_OFF(offsetof(OtIbexRemap, addr_en)): + s->regs.remap[access][region].addr_en = value; + break; + case R32_OFF(offsetof(OtIbexRemap, addr_matching)): + s->regs.remap[access][region].addr_matching = value; + break; + case R32_OFF(offsetof(OtIbexRemap, remap_addr)): + s->regs.remap[access][region].remap_addr = value; + break; + case R32_OFF(offsetof(OtIbexRemap, regwen)): + default: + g_assert_not_reached(); + return; + } + + ot_ibex_wrapper_update_remap_mr(s, (OtIbexRemapAccess)access, region); +} + +static void ot_ibex_wrapper_write_nmi_enable(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + (void)reg; + + s->regs.nmi_enable |= value; /* RW1S */ +} + +static void ot_ibex_wrapper_write_nmi_state(OtIbexWrapperState *s, unsigned reg, + uint32_t value) +{ + (void)reg; + + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(s, reg)); + + s->regs.nmi_state &= ~value; /* RW1C */ +} + +static void ot_ibex_wrapper_write_err_status(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + qemu_log_mask(LOG_UNIMP, "%s: %s: %s is not supported\n", __func__, + s->ot_id, REG_NAME(s, reg)); + + s->regs.err_status &= ~value; /* RW1C */ +} + +static void ot_ibex_wrapper_write_dv_sim_status(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + (void)reg; + + ot_ibex_wrapper_status_report(s, value); + switch (value & DV_SIM_STATUS_CODE_MASK) { + case TEST_STATUS_PASSED: + trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM success, exiting", 0); + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + 0); + break; + case TEST_STATUS_FAILED: { + uint32_t info = SHARED_FIELD_EX32(value, DV_SIM_STATUS_INFO); + int ret; + if (info == 0) { + /* no extra info */ + ret = 1; + } else { + ret = (int)(info & 0x7fu); + } + trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM failure, exiting", ret); + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + ret); + break; + } + default: + s->regs.dv_sim_win[DV_SIM_STATUS] = value; + break; + } +} + +static void ot_ibex_wrapper_write_dv_sim_log(OtIbexWrapperState *s, + unsigned reg, uint32_t value) +{ + g_assert(reg < s->reg_count); + reg -= R32_DYN_POS(s, dv_sim_win[0]); + g_assert(reg < ARRAY_SIZE(((OtIbexWrapperRegs *)0)->dv_sim_win)); + /* NOLINTNEXTLINE */ + switch (reg) { + case 0u: + ot_ibex_wrapper_log_handle(s, value); + break; + default: + s->regs.dv_sim_win[reg] = value; + break; + } +} + +static uint64_t +ot_ibex_wrapper_regs_read(void *opaque, hwaddr addr, unsigned size) +{ + OtIbexWrapperState *s = opaque; + (void)size; + uint32_t val32; + + hwaddr reg = R32_OFF(addr); + + if (reg >= s->reg_count) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Invalid register 0x%03" HWADDR_PRIx "\n", + __func__, s->ot_id, addr); + return 0; + } + + g_assert(s->access_table[reg].read); + val32 = (*s->access_table[reg].read)(s, reg); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_ibex_wrapper_io_read_out(s->ot_id, (uint32_t)addr, + REG_NAME(s, reg), val32, pc); + + return (uint64_t)val32; +}; + +static void ot_ibex_wrapper_regs_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned size) +{ + OtIbexWrapperState *s = opaque; + (void)size; + uint32_t val32 = (uint32_t)val64; + + hwaddr reg = R32_OFF(addr); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_ibex_wrapper_io_write(s->ot_id, (uint32_t)addr, REG_NAME(s, reg), + val32, pc); + + if (reg >= s->reg_count) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Invalid register 0x%03" HWADDR_PRIx "\n", + __func__, s->ot_id, addr); + return; + } + + val32 &= s->access_table[reg].mask; + + g_assert(s->access_table[reg].write); + (*s->access_table[reg].write)(s, reg, val32); +}; + +static void ot_ibex_wrapper_fill_tables(OtIbexWrapperState *s) +{ + uint32_t *regs; + + CREATE_NAME_REG(s, ALERT_TEST); + CREATE_NAME_REG(s, SW_RECOV_ERR); + CREATE_NAME_REG(s, SW_FATAL_ERR); + + regs = &s->regs.alert_test; + unsigned rix; + for (rix = 0; rix <= LAST_STATIC_REG_POS; rix++) { + s->access_regs[rix] = ®s[rix]; + } + + for (unsigned aix = 0; aix < ACCESS_COUNT; aix++) { + char prefix[6] = " BUS_"; + prefix[0] = aix ? 'D' : 'I'; + for (unsigned mix = 0; mix < s->num_regions; mix++) { + CREATE_NAME_REG_IX_AT(s, prefix, rix, mix, REGWEN); + s->access_regs[rix++] = &s->regs.remap[aix][mix].regwen; + } + for (unsigned mix = 0; mix < s->num_regions; mix++) { + CREATE_NAME_REG_IX_AT(s, prefix, rix, mix, ADDR_EN); + s->access_regs[rix++] = &s->regs.remap[aix][mix].addr_en; + } + for (unsigned mix = 0; mix < s->num_regions; mix++) { + CREATE_NAME_REG_IX_AT(s, prefix, rix, mix, ADDR_MATCHING); + s->access_regs[rix++] = &s->regs.remap[aix][mix].addr_matching; + } + for (unsigned mix = 0; mix < s->num_regions; mix++) { + CREATE_NAME_REG_IX_AT(s, prefix, rix, mix, REMAP_ADDR); + s->access_regs[rix++] = &s->regs.remap[aix][mix].remap_addr; + } + } + g_assert(rix == R32_DYN_POS(s, nmi_enable)); + + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, nmi_enable), NMI_ENABLE); + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, nmi_state), NMI_STATE); + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, err_status), ERR_STATUS); + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, rnd_data), RND_DATA); + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, rnd_status), RND_STATUS); + CREATE_NAME_REG_AT(s, R32_DYN_POS(s, fpga_info), FPGA_INFO); + CREATE_NAME_REG_AT(s, + s->remap_reg_count + R32_POS(dv_sim_win[DV_SIM_STATUS]), + DV_SIM_STATUS); + CREATE_NAME_REG_AT(s, s->remap_reg_count + R32_POS(dv_sim_win[DV_SIM_LOG]), + DV_SIM_LOG); + + for (unsigned wix = DV_SIM_WIN2; wix <= DV_SIM_WIN7; wix++) { + CREATE_NAME_REG_IX_AT(s, "", R32_DYN_POS(s, dv_sim_win[wix]), wix, + DV_SIM_WIN); + } + + regs = &s->regs.nmi_enable; + while (rix < R32_DYN_POS(s, dv_sim_win[DV_SIM_COUNT])) { + s->access_regs[rix++] = regs++; + } + + /* assign readers */ + for (rix = 0; rix < s->reg_count; rix++) { + s->access_table[rix].read = &ot_ibex_wrapper_read_reg; + } + + s->access_table[R32_DYN_POS(s, rnd_data)].read = + &ot_ibex_wrapper_read_rnd_data; + s->access_table[R32_DYN_POS(s, rnd_status)].read = + &ot_ibex_wrapper_read_rnd_status; + s->access_table[R32_DYN_POS(s, dv_sim_win[0u])].read = + &ot_ibex_wrapper_read_zero; + + /* assign writers */ + for (rix = 0; rix < s->reg_count; rix++) { + s->access_table[rix].write = &ot_ibex_wrapper_write_reg; + s->access_table[rix].mask = UINT32_MAX; + } + + s->access_table[R32_DYN_POS(s, alert_test)].write = + &ot_ibex_wrapper_write_alert_test; + s->access_table[R32_DYN_POS(s, sw_recov_err)].write = + &ot_ibex_wrapper_write_sw_recov_err; + s->access_table[R32_DYN_POS(s, sw_fatal_err)].write = + &ot_ibex_wrapper_write_sw_fatal_err; + s->access_table[R32_DYN_POS(s, err_status)].write = + &ot_ibex_wrapper_write_err_status; + s->access_table[R32_DYN_POS(s, nmi_enable)].write = + &ot_ibex_wrapper_write_nmi_enable; + s->access_table[R32_DYN_POS(s, nmi_state)].write = + &ot_ibex_wrapper_write_nmi_state; + s->access_table[R32_DYN_POS(s, rnd_data)].write = &ot_ibex_wrapper_write_ro; + s->access_table[R32_DYN_POS(s, rnd_status)].write = + &ot_ibex_wrapper_write_ro; + s->access_table[R32_DYN_POS(s, fpga_info)].write = + &ot_ibex_wrapper_write_ro; + s->access_table[R32_DYN_POS(s, dv_sim_win[DV_SIM_STATUS])].write = + &ot_ibex_wrapper_write_dv_sim_status; + s->access_table[R32_DYN_POS(s, dv_sim_win[DV_SIM_LOG])].write = + &ot_ibex_wrapper_write_dv_sim_log; + + s->access_table[R32_DYN_POS(s, alert_test)].mask = ALERT_TEST_MASK; + s->access_table[R32_DYN_POS(s, sw_recov_err)].mask = + R_SW_RECOV_ERR_VAL_MASK; + /* this register is extended in QEMU, HW mask is applied in HW handler */ + s->access_table[R32_DYN_POS(s, sw_fatal_err)].mask = UINT32_MAX; + s->access_table[R32_DYN_POS(s, nmi_enable)].mask = NMI_MASK; + s->access_table[R32_DYN_POS(s, nmi_state)].mask = NMI_MASK; + s->access_table[R32_DYN_POS(s, err_status)].mask = ERR_STATUS_MASK; + + unsigned base = FIRST_REMAP_REG_POS; + for (unsigned aix = 0; aix < ACCESS_COUNT; aix++) { + for (unsigned mix = 0; mix < s->num_regions; mix++) { + s->access_table[base + mix].write = &ot_ibex_wrapper_write_regwen; + s->access_table[base + mix].mask = REGWEN_EN_MASK; + } + base += s->num_regions; + for (unsigned mix = 0; mix < s->num_regions; mix++) { + s->access_table[base + mix].write = &ot_ibex_wrapper_write_remap; + s->access_table[base + mix].mask = UINT32_MAX; + } + base += s->num_regions; + for (unsigned mix = 0; mix < s->num_regions; mix++) { + s->access_table[base + mix].write = &ot_ibex_wrapper_write_remap; + s->access_table[base + mix].mask = UINT32_MAX; + } + base += s->num_regions; + for (unsigned mix = 0; mix < s->num_regions; mix++) { + s->access_table[base + mix].write = &ot_ibex_wrapper_write_remap; + s->access_table[base + mix].mask = UINT32_MAX; + } + base += s->num_regions; + } + + +#ifdef PRINT_REGNAMES + for (unsigned ix = 0; ix < s->reg_count; ix++) { + qemu_log("%s: %s: REG[%2u] = %s\n", __func__, s->ot_id, ix, + REG_NAME(s, ix)); + } +#endif +} + +/* all properties are optional */ +static Property ot_ibex_wrapper_properties[] = { + DEFINE_PROP_STRING("ot_id", OtIbexWrapperState, ot_id), + DEFINE_PROP_LINK("edn", OtIbexWrapperState, edn, TYPE_OT_EDN, OtEDNState *), + DEFINE_PROP_UINT8("num-regions", OtIbexWrapperState, num_regions, 0), + DEFINE_PROP_UINT8("edn-ep", OtIbexWrapperState, edn_ep, UINT8_MAX), + DEFINE_PROP_BOOL("lc-ignore", OtIbexWrapperState, lc_ignore, false), + DEFINE_PROP_UINT8("qemu_version", OtIbexWrapperState, qemu_version, 0), + DEFINE_PROP_STRING("lc-ignore-ids", OtIbexWrapperState, lc_ignore_ids), + DEFINE_PROP_CHR("logdev", OtIbexWrapperState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static const MemoryRegionOps ot_ibex_wrapper_regs_ops = { + .read = &ot_ibex_wrapper_regs_read, + .write = &ot_ibex_wrapper_regs_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static void ot_ibex_wrapper_reset_enter(Object *obj, ResetType type) +{ + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_GET_CLASS(obj); + OtIbexWrapperState *s = OT_IBEX_WRAPPER(obj); + + trace_ot_ibex_wrapper_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + if (s->lc_ignore_ids) { + char *ign = g_strdup(s->lc_ignore_ids); + char *token = strtok(ign, ","); + while (token) { + if (!strcmp(token, s->ot_id)) { + s->lc_ignore = true; + } + token = strtok(NULL, ","); + } + g_free(ign); + } + + if (!s->cpu) { + CPUState *cpu = ot_common_get_local_cpu(DEVICE(s)); + if (!cpu) { + error_setg(&error_fatal, "Could not find the associated vCPU"); + g_assert_not_reached(); + } + s->cpu = cpu; + } + + for (unsigned slot = 0; slot < (unsigned)s->num_regions; slot++) { + ot_ibex_wrapper_remapper_destroy(s, slot); + } + + static_assert(offsetof(OtIbexWrapperRegs, remap) == + sizeof(OtIbexWrapperRegs) - + sizeof(((OtIbexWrapperRegs *)0)->remap), + "Offset of remap field is incorrect"); + memset(&s->regs, 0, offsetof(OtIbexWrapperRegs, remap)); + s->regs.sw_recov_err = OT_MULTIBITBOOL4_FALSE; + s->regs.sw_fatal_err = OT_MULTIBITBOOL4_FALSE; + for (unsigned aix = 0; aix < ACCESS_COUNT; aix++) { + for (unsigned rix = 0; rix < (unsigned)s->num_regions; rix++) { + s->regs.remap[aix][rix].regwen = 0x1u; + } + } + + /* 'QMU_' in LE, _ is the QEMU version stored in the MSB */ + s->regs.fpga_info = 0x00554d51u + (((uint32_t)s->qemu_version) << 24u); + s->entropy_requested = false; + s->cpu_en_bm = s->lc_ignore ? (1u << OT_IBEX_LC_CTRL_CPU_EN) : 0; + + memset(s->log_engine, 0, sizeof(*s->log_engine)); +} + +static void ot_ibex_wrapper_reset_exit(Object *obj, ResetType type) +{ + OtIbexWrapperClass *c = OT_IBEX_WRAPPER_GET_CLASS(obj); + OtIbexWrapperState *s = OT_IBEX_WRAPPER(obj); + + trace_ot_ibex_wrapper_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); + + /* "Upon reset the data will be invalid with a new EDN request pending." */ + ot_ibex_wrapper_request_entropy(s); +} + +static void ot_ibex_wrapper_realize(DeviceState *dev, Error **errp) +{ + OtIbexWrapperState *s = OT_IBEX_WRAPPER(dev); + (void)errp; + + s->sys_mem = ot_common_get_local_address_space(dev)->root; + g_assert(s->sys_mem); + g_assert(s->ot_id); + g_assert(s->num_regions); + + s->remap_reg_count = + s->num_regions * ACCESS_COUNT * sizeof(OtIbexRemap) / sizeof(uint32_t); + s->reg_count = offsetof(OtIbexWrapperRegs, dv_sim_win) / sizeof(uint32_t) + + DV_SIM_COUNT + s->remap_reg_count; + + memory_region_init_io(&s->mmio, OBJECT(dev), &ot_ibex_wrapper_regs_ops, s, + TYPE_OT_IBEX_WRAPPER, + s->reg_count * sizeof(uint32_t)); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); + + s->remappers = g_new0(MemoryRegion, s->num_regions); + s->access_table = g_new0(OtIbexWrapperAccess, s->reg_count); + s->access_regs = g_new0(uint32_t *, s->reg_count); + s->reg_names = g_new0(char *, s->reg_count); + for (unsigned ix = 0; ix < ACCESS_COUNT; ix++) { + s->regs.remap[ix] = g_new0(OtIbexRemap, s->num_regions); + } + char *name_buf = g_new0(char, (size_t)(REG_NAME_LENGTH * s->reg_count)); + for (unsigned ix = 0; ix < s->reg_count; + ix++, name_buf += REG_NAME_LENGTH) { + s->reg_names[ix] = name_buf; + } + + ot_ibex_wrapper_fill_tables(s); +} + +static void ot_ibex_wrapper_init(Object *obj) +{ + OtIbexWrapperState *s = OT_IBEX_WRAPPER(obj); + + for (unsigned ix = 0; ix < NUM_ALERTS; ix++) { + ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); + } + + qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_cpu_enable_recv, + OT_IBEX_WRAPPER_CPU_EN, OT_IBEX_CPU_EN_COUNT); + qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_escalate_rx, + OT_ALERT_ESCALATE, 1); + + s->log_engine = g_new0(OtIbexTestLogEngine, 1u); +} + +static void ot_ibex_wrapper_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_ibex_wrapper_realize; + device_class_set_props(dc, ot_ibex_wrapper_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_reset_enter, NULL, + &ot_ibex_wrapper_reset_exit, + &ic->parent_phases); +} + +static const TypeInfo ot_ibex_wrapper_info = { + .name = TYPE_OT_IBEX_WRAPPER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtIbexWrapperState), + .instance_init = &ot_ibex_wrapper_init, + .class_size = sizeof(OtIbexWrapperClass), + .class_init = &ot_ibex_wrapper_class_init, +}; + +static void ot_ibex_wrapper_register_types(void) +{ + type_register_static(&ot_ibex_wrapper_info); +} + +type_init(ot_ibex_wrapper_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index f26e003119e1c..029923a15528d 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -256,7 +256,7 @@ ot_ibex_wrapper_map(const char *id, unsigned slot, uint32_t src, uint32_t dst, u ot_ibex_wrapper_request_entropy(const char *id, bool again) "%s: %u" ot_ibex_wrapper_reset(const char *id, const char *phase) "%s: %s" ot_ibex_wrapper_unmap(const char *id, unsigned slot) "%s: region %u" -ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool cpu_en) "%s: 0x%x %u-> CPU enable %u" +ot_ibex_wrapper_update_exec(const char *id, uint32_t bm, bool esc_rx, bool cpu_en) "%s: bm:0x%x esc:%u -> CPU enable %u" # ot_kmac.c diff --git a/include/hw/opentitan/ot_ibex_wrapper.h b/include/hw/opentitan/ot_ibex_wrapper.h index aa4c5a4f187b6..67adb0685f848 100644 --- a/include/hw/opentitan/ot_ibex_wrapper.h +++ b/include/hw/opentitan/ot_ibex_wrapper.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Ibex Wrapper device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -37,15 +37,6 @@ #define TYPE_OT_IBEX_WRAPPER "ot-ibex_wrapper" OBJECT_DECLARE_TYPE(OtIbexWrapperState, OtIbexWrapperClass, OT_IBEX_WRAPPER) -struct OtIbexWrapperState { - SysBusDevice parent_obj; -}; - -struct OtIbexWrapperClass { - SysBusDeviceClass parent_class; - ResettablePhases parent_phases; -}; - #define OT_IBEX_WRAPPER_CPU_EN TYPE_OT_IBEX_WRAPPER "-cpu-en" typedef enum { From da259062986440db27713cb63f6bdff0d2f5f29d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 8 Apr 2025 14:42:00 +0200 Subject: [PATCH 07/69] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: use the unified Ibex Wrapper implementation Instantiate and configure the virtual remapper. Signed-off-by: Emmanuel Blot --- docs/opentitan/darjeeling.md | 8 ++++---- hw/riscv/ot_darjeeling.c | 9 ++++++--- hw/riscv/ot_earlgrey.c | 9 ++++++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/opentitan/darjeeling.md b/docs/opentitan/darjeeling.md index 64019a055104b..f7087fe0773af 100644 --- a/docs/opentitan/darjeeling.md +++ b/docs/opentitan/darjeeling.md @@ -104,10 +104,10 @@ any useful feature (only allow guest test code to execute as expected). ````sh qemu-system-riscv32 -M ot-darjeeling,no_epmp_cfg=true -display none -serial mon:stdio \ - -global ot-ibex_wrapper-dj.lc-ignore=on -kernel hello.elf + -global ot-ibex_wrapper.lc-ignore=on -kernel hello.elf ```` See the section "Useful execution options" for documentation about the `no_epmp_cfg` and -`ot-ibex_wrapper-dj.lc-ignore=on` option. +`ot-ibex_wrapper.lc-ignore=on` option. ### Boot sequence ROM, ROM_EXT, BLO @@ -149,14 +149,14 @@ See [`tools.md`](tools.md) to update the vCPU reset vector at startup. When this option is used, with `-kernel` option for example, the application is loaded in memory but the default machine reset vector is used. -* `-global ot-ibex_wrapper-dj.lc-ignore=on` should be used whenever no OTP image is provided, or if +* `-global ot-ibex_wrapper.lc-ignore=on` should be used whenever no OTP image is provided, or if the current LifeCycle state stored in the OTP image does not allow the Ibex core to fetch data. This switch forces the Ibex core to execute whatever the LifeCycle broadcasted signal, which departs from the HW behavior but maybe helpful to run the machine without a full OTP set up. The alternative to allow the Ibex core to execute guest code is to provide a valid OTP image with one of the expected LifeCycle state, such as TestUnlock*, Dev, Prod or RMA. -* `-global ot-ibex_wrapper-dj.lc-ignore-ids=` acts as `lc-ignore`, enabling the selection of +* `-global ot-ibex_wrapper.lc-ignore-ids=` acts as `lc-ignore`, enabling the selection of specific ibex wrapper instance based on their unique identifiers. See `ot_id` property in the machine definition file for a list of valid identifiers. `` should be defined as a comma- separated list of valid identifiers. It is only possible to ignore LifeCycle states with this diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index d8f99216c21e9..1fa3d992176af 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -50,7 +50,7 @@ #include "hw/opentitan/ot_gpio_dj.h" #include "hw/opentitan/ot_hmac.h" #include "hw/opentitan/ot_i2c_dj.h" -#include "hw/opentitan/ot_ibex_wrapper_dj.h" +#include "hw/opentitan/ot_ibex_wrapper.h" #include "hw/opentitan/ot_kmac.h" #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_mbx.h" @@ -277,6 +277,8 @@ enum OtDjPinmuxMioOut { #define OT_DJ_PRIVATE_REGION_OFFSET 0x00000000u #define OT_DJ_PRIVATE_REGION_SIZE (1u << 30u) +#define OT_IBEX_WRAPPER_NUM_REGIONS 32u + /* CTN address space */ #define OT_DJ_CTN_REGION_OFFSET 0x40000000u #define OT_DJ_CTN_REGION_SIZE (1u << 30u) @@ -784,7 +786,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), }, [OT_DJ_SOC_DEV_IBEX_WRAPPER] = { - .type = TYPE_OT_IBEX_WRAPPER_DJ, + .type = TYPE_OT_IBEX_WRAPPER, .memmap = MEMMAPENTRIES( { .base = 0x211f0000u } ), @@ -798,7 +800,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_DEVLINK("edn", EDN0) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("edn-ep", 7u) + IBEX_DEV_UINT_PROP("edn-ep", 7u), + IBEX_DEV_UINT_PROP("num-regions", OT_IBEX_WRAPPER_NUM_REGIONS) ), }, [OT_DJ_SOC_DEV_RV_DM] = { diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 8faba62f75422..ca0fc798621b0 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -49,7 +49,7 @@ #include "hw/opentitan/ot_flash.h" #include "hw/opentitan/ot_gpio_eg.h" #include "hw/opentitan/ot_hmac.h" -#include "hw/opentitan/ot_ibex_wrapper_eg.h" +#include "hw/opentitan/ot_ibex_wrapper.h" #include "hw/opentitan/ot_kmac.h" #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_otbn.h" @@ -194,6 +194,8 @@ enum OtEGBoardDevice { /* Verilator AON clock is 125 kHz */ #define OT_EG_VERILATOR_AON_CLK_HZ OT_EG_VERILATOR_PERIPHERAL_CLK_HZ +#define OT_IBEX_WRAPPER_NUM_REGIONS 2u + static const uint8_t ot_eg_pmp_cfgs[] = { /* clang-format off */ IBEX_PMP_CFG(0, IBEX_PMP_MODE_OFF, 0, 0, 0), @@ -1127,7 +1129,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { ), }, [OT_EG_SOC_DEV_IBEX_WRAPPER] = { - .type = TYPE_OT_IBEX_WRAPPER_EG, + .type = TYPE_OT_IBEX_WRAPPER, .memmap = MEMMAPENTRIES( { .base = 0x411f0000u } ), @@ -1141,7 +1143,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_DEVLINK("edn", EDN0) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_UINT_PROP("edn-ep", 7u) + IBEX_DEV_UINT_PROP("edn-ep", 7u), + IBEX_DEV_UINT_PROP("num-regions", OT_IBEX_WRAPPER_NUM_REGIONS), ), }, [OT_EG_SOC_DEV_RV_DM] = { From b4c29656382bad82f363350bdc6ffc6726f8ee08 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 8 Apr 2025 12:29:20 +0200 Subject: [PATCH 08/69] [ot] hw/opentitan: ot_earlgrey: ignore LC state status to execute code LC state support on EarlGrey is not yet fully implemented, bypass its execution control line. Signed-off-by: Emmanuel Blot (cherry picked from commit bd1b7acb45dda7203045669ef936c4c43f01cb56) --- hw/riscv/ot_earlgrey.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index ca0fc798621b0..254d00b5cc940 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -1145,6 +1145,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("edn-ep", 7u), IBEX_DEV_UINT_PROP("num-regions", OT_IBEX_WRAPPER_NUM_REGIONS), + /* remove the following line once LC state is implemented */ + IBEX_DEV_BOOL_PROP("lc-ignore", true) ), }, [OT_EG_SOC_DEV_RV_DM] = { From c89c715ad845b46af18b1579e2db0a6019006d4f Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 7 Apr 2025 14:41:40 +0200 Subject: [PATCH 09/69] [ot] hw/opentitan: ot_ibex_wrapper: remove Ibex Wrapper old implementations Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 8 - hw/opentitan/meson.build | 2 - hw/opentitan/ot_ibex_wrapper_dj.c | 1648 --------------------- hw/opentitan/ot_ibex_wrapper_eg.c | 1080 -------------- include/hw/opentitan/ot_ibex_wrapper_dj.h | 39 - include/hw/opentitan/ot_ibex_wrapper_eg.h | 40 - 6 files changed, 2817 deletions(-) delete mode 100644 hw/opentitan/ot_ibex_wrapper_dj.c delete mode 100644 hw/opentitan/ot_ibex_wrapper_eg.c delete mode 100644 include/hw/opentitan/ot_ibex_wrapper_dj.h delete mode 100644 include/hw/opentitan/ot_ibex_wrapper_eg.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 9133ba2a4d86d..ca57a0def2bd6 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -65,14 +65,6 @@ config OT_I2C_DJ config OT_IBEX_WRAPPER bool -config OT_IBEX_WRAPPER_DJ - select OT_IBEX_WRAPPER - bool - -config OT_IBEX_WRAPPER_EG - select OT_IBEX_WRAPPER - bool - config OT_KMAC bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 6cebf39e835f5..dcbfa4c42f1ef 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -24,8 +24,6 @@ system_ss.add(when: 'CONFIG_OT_GPIO_EG', if_true: files('ot_gpio_eg.c')) system_ss.add(when: 'CONFIG_OT_HMAC', if_true: [files('ot_hmac.c'), libtomcrypt_dep]) system_ss.add(when: 'CONFIG_OT_I2C_DJ', if_true: files('ot_i2c_dj.c')) system_ss.add(when: 'CONFIG_OT_IBEX_WRAPPER', if_true: files('ot_ibex_wrapper.c')) -system_ss.add(when: 'CONFIG_OT_IBEX_WRAPPER_DJ', if_true: files('ot_ibex_wrapper_dj.c')) -system_ss.add(when: 'CONFIG_OT_IBEX_WRAPPER_EG', if_true: files('ot_ibex_wrapper_eg.c')) system_ss.add(when: 'CONFIG_OT_KMAC', if_true: [files('ot_kmac.c'), libtomcrypt_dep]) system_ss.add(when: 'CONFIG_OT_LC_CTRL', if_true: files('ot_lc_ctrl.c')) system_ss.add(when: 'CONFIG_OT_MBX', if_true: files('ot_mbx.c')) diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c deleted file mode 100644 index 9324880b34d7b..0000000000000 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ /dev/null @@ -1,1648 +0,0 @@ -/* - * QEMU OpenTitan Darjeeling Ibex wrapper device - * - * Copyright (c) 2022-2024 Rivos, Inc. - * Copyright (c) 2025 lowRISC contributors. - * - * Author(s): - * Emmanuel Blot - * - * 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 "qemu/log.h" -#include "qemu/typedefs.h" -#include "qapi/error.h" -#include "chardev/char-fe.h" -#include "hw/opentitan/ot_alert.h" -#include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_edn.h" -#include "hw/opentitan/ot_ibex_wrapper_dj.h" -#include "hw/qdev-properties-system.h" -#include "hw/qdev-properties.h" -#include "hw/registerfields.h" -#include "hw/riscv/ibex_common.h" -#include "hw/riscv/ibex_irq.h" -#include "hw/sysbus.h" -#include "sysemu/runstate.h" -#include "trace.h" - - -/* DEBUG: define to print the full memory view on remap */ -#undef PRINT_MTREE - -#define PARAM_NUM_SW_ALERTS 2u -#define PARAM_NUM_REGIONS 32u -#define PARAM_NUM_SCRATCH_WORDS 8u -#define PARAM_NUM_ALERTS 4u - -/* clang-format off */ -REG32(ALERT_TEST, 0x0u) - FIELD(ALERT_TEST, FATAL_SW, 0u, 1u) - FIELD(ALERT_TEST, RECOV_SW, 1u, 1u) - FIELD(ALERT_TEST, FATAL_HW, 2u, 1u) - FIELD(ALERT_TEST, RECOV_HW, 3u, 1u) -REG32(SW_RECOV_ERR, 0x4u) - FIELD(SW_RECOV_ERR, VAL, 0u, 4u) -REG32(SW_FATAL_ERR, 0x8u) - FIELD(SW_FATAL_ERR, VAL, 0u, 4u) -REG32(IBUS_REGWEN_0, 0xcu) - SHARED_FIELD(REGWEN_EN, 0u, 1u) -REG32(IBUS_REGWEN_1, 0x10u) -REG32(IBUS_REGWEN_2, 0x14u) -REG32(IBUS_REGWEN_3, 0x18u) -REG32(IBUS_REGWEN_4, 0x1cu) -REG32(IBUS_REGWEN_5, 0x20u) -REG32(IBUS_REGWEN_6, 0x24u) -REG32(IBUS_REGWEN_7, 0x28u) -REG32(IBUS_REGWEN_8, 0x2cu) -REG32(IBUS_REGWEN_9, 0x30u) -REG32(IBUS_REGWEN_10, 0x34u) -REG32(IBUS_REGWEN_11, 0x38u) -REG32(IBUS_REGWEN_12, 0x3cu) -REG32(IBUS_REGWEN_13, 0x40u) -REG32(IBUS_REGWEN_14, 0x44u) -REG32(IBUS_REGWEN_15, 0x48u) -REG32(IBUS_REGWEN_16, 0x4cu) -REG32(IBUS_REGWEN_17, 0x50u) -REG32(IBUS_REGWEN_18, 0x54u) -REG32(IBUS_REGWEN_19, 0x58u) -REG32(IBUS_REGWEN_20, 0x5cu) -REG32(IBUS_REGWEN_21, 0x60u) -REG32(IBUS_REGWEN_22, 0x64u) -REG32(IBUS_REGWEN_23, 0x68u) -REG32(IBUS_REGWEN_24, 0x6cu) -REG32(IBUS_REGWEN_25, 0x70u) -REG32(IBUS_REGWEN_26, 0x74u) -REG32(IBUS_REGWEN_27, 0x78u) -REG32(IBUS_REGWEN_28, 0x7cu) -REG32(IBUS_REGWEN_29, 0x80u) -REG32(IBUS_REGWEN_30, 0x84u) -REG32(IBUS_REGWEN_31, 0x88u) -REG32(IBUS_ADDR_EN_0, 0x8cu) - SHARED_FIELD(ADDR_EN, 0u, 1u) -REG32(IBUS_ADDR_EN_1, 0x90u) -REG32(IBUS_ADDR_EN_2, 0x94u) -REG32(IBUS_ADDR_EN_3, 0x98u) -REG32(IBUS_ADDR_EN_4, 0x9cu) -REG32(IBUS_ADDR_EN_5, 0xa0u) -REG32(IBUS_ADDR_EN_6, 0xa4u) -REG32(IBUS_ADDR_EN_7, 0xa8u) -REG32(IBUS_ADDR_EN_8, 0xacu) -REG32(IBUS_ADDR_EN_9, 0xb0u) -REG32(IBUS_ADDR_EN_10, 0xb4u) -REG32(IBUS_ADDR_EN_11, 0xb8u) -REG32(IBUS_ADDR_EN_12, 0xbcu) -REG32(IBUS_ADDR_EN_13, 0xc0u) -REG32(IBUS_ADDR_EN_14, 0xc4u) -REG32(IBUS_ADDR_EN_15, 0xc8u) -REG32(IBUS_ADDR_EN_16, 0xccu) -REG32(IBUS_ADDR_EN_17, 0xd0u) -REG32(IBUS_ADDR_EN_18, 0xd4u) -REG32(IBUS_ADDR_EN_19, 0xd8u) -REG32(IBUS_ADDR_EN_20, 0xdcu) -REG32(IBUS_ADDR_EN_21, 0xe0u) -REG32(IBUS_ADDR_EN_22, 0xe4u) -REG32(IBUS_ADDR_EN_23, 0xe8u) -REG32(IBUS_ADDR_EN_24, 0xecu) -REG32(IBUS_ADDR_EN_25, 0xf0u) -REG32(IBUS_ADDR_EN_26, 0xf4u) -REG32(IBUS_ADDR_EN_27, 0xf8u) -REG32(IBUS_ADDR_EN_28, 0xfcu) -REG32(IBUS_ADDR_EN_29, 0x100u) -REG32(IBUS_ADDR_EN_30, 0x104u) -REG32(IBUS_ADDR_EN_31, 0x108u) -REG32(IBUS_ADDR_MATCHING_0, 0x10cu) -REG32(IBUS_ADDR_MATCHING_1, 0x110u) -REG32(IBUS_ADDR_MATCHING_2, 0x114u) -REG32(IBUS_ADDR_MATCHING_3, 0x118u) -REG32(IBUS_ADDR_MATCHING_4, 0x11cu) -REG32(IBUS_ADDR_MATCHING_5, 0x120u) -REG32(IBUS_ADDR_MATCHING_6, 0x124u) -REG32(IBUS_ADDR_MATCHING_7, 0x128u) -REG32(IBUS_ADDR_MATCHING_8, 0x12cu) -REG32(IBUS_ADDR_MATCHING_9, 0x130u) -REG32(IBUS_ADDR_MATCHING_10, 0x134u) -REG32(IBUS_ADDR_MATCHING_11, 0x138u) -REG32(IBUS_ADDR_MATCHING_12, 0x13cu) -REG32(IBUS_ADDR_MATCHING_13, 0x140u) -REG32(IBUS_ADDR_MATCHING_14, 0x144u) -REG32(IBUS_ADDR_MATCHING_15, 0x148u) -REG32(IBUS_ADDR_MATCHING_16, 0x14cu) -REG32(IBUS_ADDR_MATCHING_17, 0x150u) -REG32(IBUS_ADDR_MATCHING_18, 0x154u) -REG32(IBUS_ADDR_MATCHING_19, 0x158u) -REG32(IBUS_ADDR_MATCHING_20, 0x15cu) -REG32(IBUS_ADDR_MATCHING_21, 0x160u) -REG32(IBUS_ADDR_MATCHING_22, 0x164u) -REG32(IBUS_ADDR_MATCHING_23, 0x168u) -REG32(IBUS_ADDR_MATCHING_24, 0x16cu) -REG32(IBUS_ADDR_MATCHING_25, 0x170u) -REG32(IBUS_ADDR_MATCHING_26, 0x174u) -REG32(IBUS_ADDR_MATCHING_27, 0x178u) -REG32(IBUS_ADDR_MATCHING_28, 0x17cu) -REG32(IBUS_ADDR_MATCHING_29, 0x180u) -REG32(IBUS_ADDR_MATCHING_30, 0x184u) -REG32(IBUS_ADDR_MATCHING_31, 0x188u) -REG32(IBUS_REMAP_ADDR_0, 0x18cu) -REG32(IBUS_REMAP_ADDR_1, 0x190u) -REG32(IBUS_REMAP_ADDR_2, 0x194u) -REG32(IBUS_REMAP_ADDR_3, 0x198u) -REG32(IBUS_REMAP_ADDR_4, 0x19cu) -REG32(IBUS_REMAP_ADDR_5, 0x1a0u) -REG32(IBUS_REMAP_ADDR_6, 0x1a4u) -REG32(IBUS_REMAP_ADDR_7, 0x1a8u) -REG32(IBUS_REMAP_ADDR_8, 0x1acu) -REG32(IBUS_REMAP_ADDR_9, 0x1b0u) -REG32(IBUS_REMAP_ADDR_10, 0x1b4u) -REG32(IBUS_REMAP_ADDR_11, 0x1b8u) -REG32(IBUS_REMAP_ADDR_12, 0x1bcu) -REG32(IBUS_REMAP_ADDR_13, 0x1c0u) -REG32(IBUS_REMAP_ADDR_14, 0x1c4u) -REG32(IBUS_REMAP_ADDR_15, 0x1c8u) -REG32(IBUS_REMAP_ADDR_16, 0x1ccu) -REG32(IBUS_REMAP_ADDR_17, 0x1d0u) -REG32(IBUS_REMAP_ADDR_18, 0x1d4u) -REG32(IBUS_REMAP_ADDR_19, 0x1d8u) -REG32(IBUS_REMAP_ADDR_20, 0x1dcu) -REG32(IBUS_REMAP_ADDR_21, 0x1e0u) -REG32(IBUS_REMAP_ADDR_22, 0x1e4u) -REG32(IBUS_REMAP_ADDR_23, 0x1e8u) -REG32(IBUS_REMAP_ADDR_24, 0x1ecu) -REG32(IBUS_REMAP_ADDR_25, 0x1f0u) -REG32(IBUS_REMAP_ADDR_26, 0x1f4u) -REG32(IBUS_REMAP_ADDR_27, 0x1f8u) -REG32(IBUS_REMAP_ADDR_28, 0x1fcu) -REG32(IBUS_REMAP_ADDR_29, 0x200u) -REG32(IBUS_REMAP_ADDR_30, 0x204u) -REG32(IBUS_REMAP_ADDR_31, 0x208u) -REG32(DBUS_REGWEN_0, 0x20cu) -REG32(DBUS_REGWEN_1, 0x210u) -REG32(DBUS_REGWEN_2, 0x214u) -REG32(DBUS_REGWEN_3, 0x218u) -REG32(DBUS_REGWEN_4, 0x21cu) -REG32(DBUS_REGWEN_5, 0x220u) -REG32(DBUS_REGWEN_6, 0x224u) -REG32(DBUS_REGWEN_7, 0x228u) -REG32(DBUS_REGWEN_8, 0x22cu) -REG32(DBUS_REGWEN_9, 0x230u) -REG32(DBUS_REGWEN_10, 0x234u) -REG32(DBUS_REGWEN_11, 0x238u) -REG32(DBUS_REGWEN_12, 0x23cu) -REG32(DBUS_REGWEN_13, 0x240u) -REG32(DBUS_REGWEN_14, 0x244u) -REG32(DBUS_REGWEN_15, 0x248u) -REG32(DBUS_REGWEN_16, 0x24cu) -REG32(DBUS_REGWEN_17, 0x250u) -REG32(DBUS_REGWEN_18, 0x254u) -REG32(DBUS_REGWEN_19, 0x258u) -REG32(DBUS_REGWEN_20, 0x25cu) -REG32(DBUS_REGWEN_21, 0x260u) -REG32(DBUS_REGWEN_22, 0x264u) -REG32(DBUS_REGWEN_23, 0x268u) -REG32(DBUS_REGWEN_24, 0x26cu) -REG32(DBUS_REGWEN_25, 0x270u) -REG32(DBUS_REGWEN_26, 0x274u) -REG32(DBUS_REGWEN_27, 0x278u) -REG32(DBUS_REGWEN_28, 0x27cu) -REG32(DBUS_REGWEN_29, 0x280u) -REG32(DBUS_REGWEN_30, 0x284u) -REG32(DBUS_REGWEN_31, 0x288u) -REG32(DBUS_ADDR_EN_0, 0x28cu) -REG32(DBUS_ADDR_EN_1, 0x290u) -REG32(DBUS_ADDR_EN_2, 0x294u) -REG32(DBUS_ADDR_EN_3, 0x298u) -REG32(DBUS_ADDR_EN_4, 0x29cu) -REG32(DBUS_ADDR_EN_5, 0x2a0u) -REG32(DBUS_ADDR_EN_6, 0x2a4u) -REG32(DBUS_ADDR_EN_7, 0x2a8u) -REG32(DBUS_ADDR_EN_8, 0x2acu) -REG32(DBUS_ADDR_EN_9, 0x2b0u) -REG32(DBUS_ADDR_EN_10, 0x2b4u) -REG32(DBUS_ADDR_EN_11, 0x2b8u) -REG32(DBUS_ADDR_EN_12, 0x2bcu) -REG32(DBUS_ADDR_EN_13, 0x2c0u) -REG32(DBUS_ADDR_EN_14, 0x2c4u) -REG32(DBUS_ADDR_EN_15, 0x2c8u) -REG32(DBUS_ADDR_EN_16, 0x2ccu) -REG32(DBUS_ADDR_EN_17, 0x2d0u) -REG32(DBUS_ADDR_EN_18, 0x2d4u) -REG32(DBUS_ADDR_EN_19, 0x2d8u) -REG32(DBUS_ADDR_EN_20, 0x2dcu) -REG32(DBUS_ADDR_EN_21, 0x2e0u) -REG32(DBUS_ADDR_EN_22, 0x2e4u) -REG32(DBUS_ADDR_EN_23, 0x2e8u) -REG32(DBUS_ADDR_EN_24, 0x2ecu) -REG32(DBUS_ADDR_EN_25, 0x2f0u) -REG32(DBUS_ADDR_EN_26, 0x2f4u) -REG32(DBUS_ADDR_EN_27, 0x2f8u) -REG32(DBUS_ADDR_EN_28, 0x2fcu) -REG32(DBUS_ADDR_EN_29, 0x300u) -REG32(DBUS_ADDR_EN_30, 0x304u) -REG32(DBUS_ADDR_EN_31, 0x308u) -REG32(DBUS_ADDR_MATCHING_0, 0x30cu) -REG32(DBUS_ADDR_MATCHING_1, 0x310u) -REG32(DBUS_ADDR_MATCHING_2, 0x314u) -REG32(DBUS_ADDR_MATCHING_3, 0x318u) -REG32(DBUS_ADDR_MATCHING_4, 0x31cu) -REG32(DBUS_ADDR_MATCHING_5, 0x320u) -REG32(DBUS_ADDR_MATCHING_6, 0x324u) -REG32(DBUS_ADDR_MATCHING_7, 0x328u) -REG32(DBUS_ADDR_MATCHING_8, 0x32cu) -REG32(DBUS_ADDR_MATCHING_9, 0x330u) -REG32(DBUS_ADDR_MATCHING_10, 0x334u) -REG32(DBUS_ADDR_MATCHING_11, 0x338u) -REG32(DBUS_ADDR_MATCHING_12, 0x33cu) -REG32(DBUS_ADDR_MATCHING_13, 0x340u) -REG32(DBUS_ADDR_MATCHING_14, 0x344u) -REG32(DBUS_ADDR_MATCHING_15, 0x348u) -REG32(DBUS_ADDR_MATCHING_16, 0x34cu) -REG32(DBUS_ADDR_MATCHING_17, 0x350u) -REG32(DBUS_ADDR_MATCHING_18, 0x354u) -REG32(DBUS_ADDR_MATCHING_19, 0x358u) -REG32(DBUS_ADDR_MATCHING_20, 0x35cu) -REG32(DBUS_ADDR_MATCHING_21, 0x360u) -REG32(DBUS_ADDR_MATCHING_22, 0x364u) -REG32(DBUS_ADDR_MATCHING_23, 0x368u) -REG32(DBUS_ADDR_MATCHING_24, 0x36cu) -REG32(DBUS_ADDR_MATCHING_25, 0x370u) -REG32(DBUS_ADDR_MATCHING_26, 0x374u) -REG32(DBUS_ADDR_MATCHING_27, 0x378u) -REG32(DBUS_ADDR_MATCHING_28, 0x37cu) -REG32(DBUS_ADDR_MATCHING_29, 0x380u) -REG32(DBUS_ADDR_MATCHING_30, 0x384u) -REG32(DBUS_ADDR_MATCHING_31, 0x388u) -REG32(DBUS_REMAP_ADDR_0, 0x38cu) -REG32(DBUS_REMAP_ADDR_1, 0x390u) -REG32(DBUS_REMAP_ADDR_2, 0x394u) -REG32(DBUS_REMAP_ADDR_3, 0x398u) -REG32(DBUS_REMAP_ADDR_4, 0x39cu) -REG32(DBUS_REMAP_ADDR_5, 0x3a0u) -REG32(DBUS_REMAP_ADDR_6, 0x3a4u) -REG32(DBUS_REMAP_ADDR_7, 0x3a8u) -REG32(DBUS_REMAP_ADDR_8, 0x3acu) -REG32(DBUS_REMAP_ADDR_9, 0x3b0u) -REG32(DBUS_REMAP_ADDR_10, 0x3b4u) -REG32(DBUS_REMAP_ADDR_11, 0x3b8u) -REG32(DBUS_REMAP_ADDR_12, 0x3bcu) -REG32(DBUS_REMAP_ADDR_13, 0x3c0u) -REG32(DBUS_REMAP_ADDR_14, 0x3c4u) -REG32(DBUS_REMAP_ADDR_15, 0x3c8u) -REG32(DBUS_REMAP_ADDR_16, 0x3ccu) -REG32(DBUS_REMAP_ADDR_17, 0x3d0u) -REG32(DBUS_REMAP_ADDR_18, 0x3d4u) -REG32(DBUS_REMAP_ADDR_19, 0x3d8u) -REG32(DBUS_REMAP_ADDR_20, 0x3dcu) -REG32(DBUS_REMAP_ADDR_21, 0x3e0u) -REG32(DBUS_REMAP_ADDR_22, 0x3e4u) -REG32(DBUS_REMAP_ADDR_23, 0x3e8u) -REG32(DBUS_REMAP_ADDR_24, 0x3ecu) -REG32(DBUS_REMAP_ADDR_25, 0x3f0u) -REG32(DBUS_REMAP_ADDR_26, 0x3f4u) -REG32(DBUS_REMAP_ADDR_27, 0x3f8u) -REG32(DBUS_REMAP_ADDR_28, 0x3fcu) -REG32(DBUS_REMAP_ADDR_29, 0x400u) -REG32(DBUS_REMAP_ADDR_30, 0x404u) -REG32(DBUS_REMAP_ADDR_31, 0x408u) -REG32(NMI_ENABLE, 0x40cu) - SHARED_FIELD(NMI_ALERT_EN_BIT, 0u, 1u) - SHARED_FIELD(NMI_WDOG_EN_BIT, 1u, 1u) -REG32(NMI_STATE, 0x410u) -REG32(ERR_STATUS, 0x414u) - FIELD(ERR_STATUS, REG_INTG, 0u, 1u) - FIELD(ERR_STATUS, FATAL_INTG, 8u, 1u) - FIELD(ERR_STATUS, FATAL_CORE, 9u, 1u) - FIELD(ERR_STATUS, RECOV_CORE, 10u, 1u) -REG32(RND_DATA, 0x418u) -REG32(RND_STATUS, 0x41cu) - FIELD(RND_STATUS, RND_DATA_VALID, 0u, 1u) - FIELD(RND_STATUS, RND_DATA_FIPS, 1u, 1u) -REG32(FPGA_INFO, 0x420u) -REG32(DV_SIM_STATUS, 0x440u) - FIELD(DV_SIM_STATUS, CODE, 0u, 16u) - FIELD(DV_SIM_STATUS, INFO, 16u, 16u) -REG32(DV_SIM_LOG, 0x444u) -REG32(DV_SIM_WIN2, 0x448u) -REG32(DV_SIM_WIN3, 0x44cu) -REG32(DV_SIM_WIN4, 0x450u) -REG32(DV_SIM_WIN5, 0x454u) -REG32(DV_SIM_WIN6, 0x458u) -REG32(DV_SIM_WIN7, 0x45cu) -/* clang-format on */ - -#define ALERT_TEST_MASK \ - (R_ALERT_TEST_FATAL_SW_MASK | R_ALERT_TEST_RECOV_SW_MASK | \ - R_ALERT_TEST_FATAL_HW_MASK | R_ALERT_TEST_RECOV_HW_MASK) - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) - -#define R_LAST_REG (R_DV_SIM_WIN7) -#define REGS_COUNT (R_LAST_REG + 1u) -#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) -#define REG_NAME(_reg_) \ - ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") - -#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) -static const char *REG_NAMES[REGS_COUNT] = { - REG_NAME_ENTRY(ALERT_TEST), - REG_NAME_ENTRY(SW_RECOV_ERR), - REG_NAME_ENTRY(SW_FATAL_ERR), - REG_NAME_ENTRY(IBUS_REGWEN_0), - REG_NAME_ENTRY(IBUS_REGWEN_1), - REG_NAME_ENTRY(IBUS_REGWEN_2), - REG_NAME_ENTRY(IBUS_REGWEN_3), - REG_NAME_ENTRY(IBUS_REGWEN_4), - REG_NAME_ENTRY(IBUS_REGWEN_5), - REG_NAME_ENTRY(IBUS_REGWEN_6), - REG_NAME_ENTRY(IBUS_REGWEN_7), - REG_NAME_ENTRY(IBUS_REGWEN_8), - REG_NAME_ENTRY(IBUS_REGWEN_9), - REG_NAME_ENTRY(IBUS_REGWEN_10), - REG_NAME_ENTRY(IBUS_REGWEN_11), - REG_NAME_ENTRY(IBUS_REGWEN_12), - REG_NAME_ENTRY(IBUS_REGWEN_13), - REG_NAME_ENTRY(IBUS_REGWEN_14), - REG_NAME_ENTRY(IBUS_REGWEN_15), - REG_NAME_ENTRY(IBUS_REGWEN_16), - REG_NAME_ENTRY(IBUS_REGWEN_17), - REG_NAME_ENTRY(IBUS_REGWEN_18), - REG_NAME_ENTRY(IBUS_REGWEN_19), - REG_NAME_ENTRY(IBUS_REGWEN_20), - REG_NAME_ENTRY(IBUS_REGWEN_21), - REG_NAME_ENTRY(IBUS_REGWEN_22), - REG_NAME_ENTRY(IBUS_REGWEN_23), - REG_NAME_ENTRY(IBUS_REGWEN_24), - REG_NAME_ENTRY(IBUS_REGWEN_25), - REG_NAME_ENTRY(IBUS_REGWEN_26), - REG_NAME_ENTRY(IBUS_REGWEN_27), - REG_NAME_ENTRY(IBUS_REGWEN_28), - REG_NAME_ENTRY(IBUS_REGWEN_29), - REG_NAME_ENTRY(IBUS_REGWEN_30), - REG_NAME_ENTRY(IBUS_REGWEN_31), - REG_NAME_ENTRY(IBUS_ADDR_EN_0), - REG_NAME_ENTRY(IBUS_ADDR_EN_1), - REG_NAME_ENTRY(IBUS_ADDR_EN_2), - REG_NAME_ENTRY(IBUS_ADDR_EN_3), - REG_NAME_ENTRY(IBUS_ADDR_EN_4), - REG_NAME_ENTRY(IBUS_ADDR_EN_5), - REG_NAME_ENTRY(IBUS_ADDR_EN_6), - REG_NAME_ENTRY(IBUS_ADDR_EN_7), - REG_NAME_ENTRY(IBUS_ADDR_EN_8), - REG_NAME_ENTRY(IBUS_ADDR_EN_9), - REG_NAME_ENTRY(IBUS_ADDR_EN_10), - REG_NAME_ENTRY(IBUS_ADDR_EN_11), - REG_NAME_ENTRY(IBUS_ADDR_EN_12), - REG_NAME_ENTRY(IBUS_ADDR_EN_13), - REG_NAME_ENTRY(IBUS_ADDR_EN_14), - REG_NAME_ENTRY(IBUS_ADDR_EN_15), - REG_NAME_ENTRY(IBUS_ADDR_EN_16), - REG_NAME_ENTRY(IBUS_ADDR_EN_17), - REG_NAME_ENTRY(IBUS_ADDR_EN_18), - REG_NAME_ENTRY(IBUS_ADDR_EN_19), - REG_NAME_ENTRY(IBUS_ADDR_EN_20), - REG_NAME_ENTRY(IBUS_ADDR_EN_21), - REG_NAME_ENTRY(IBUS_ADDR_EN_22), - REG_NAME_ENTRY(IBUS_ADDR_EN_23), - REG_NAME_ENTRY(IBUS_ADDR_EN_24), - REG_NAME_ENTRY(IBUS_ADDR_EN_25), - REG_NAME_ENTRY(IBUS_ADDR_EN_26), - REG_NAME_ENTRY(IBUS_ADDR_EN_27), - REG_NAME_ENTRY(IBUS_ADDR_EN_28), - REG_NAME_ENTRY(IBUS_ADDR_EN_29), - REG_NAME_ENTRY(IBUS_ADDR_EN_30), - REG_NAME_ENTRY(IBUS_ADDR_EN_31), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_0), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_1), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_2), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_3), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_4), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_5), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_6), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_7), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_8), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_9), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_10), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_11), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_12), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_13), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_14), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_15), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_16), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_17), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_18), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_19), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_20), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_21), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_22), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_23), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_24), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_25), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_26), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_27), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_28), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_29), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_30), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_31), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_0), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_1), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_2), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_3), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_4), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_5), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_6), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_7), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_8), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_9), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_10), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_11), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_12), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_13), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_14), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_15), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_16), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_17), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_18), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_19), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_20), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_21), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_22), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_23), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_24), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_25), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_26), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_27), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_28), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_29), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_30), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_31), - REG_NAME_ENTRY(DBUS_REGWEN_0), - REG_NAME_ENTRY(DBUS_REGWEN_1), - REG_NAME_ENTRY(DBUS_REGWEN_2), - REG_NAME_ENTRY(DBUS_REGWEN_3), - REG_NAME_ENTRY(DBUS_REGWEN_4), - REG_NAME_ENTRY(DBUS_REGWEN_5), - REG_NAME_ENTRY(DBUS_REGWEN_6), - REG_NAME_ENTRY(DBUS_REGWEN_7), - REG_NAME_ENTRY(DBUS_REGWEN_8), - REG_NAME_ENTRY(DBUS_REGWEN_9), - REG_NAME_ENTRY(DBUS_REGWEN_10), - REG_NAME_ENTRY(DBUS_REGWEN_11), - REG_NAME_ENTRY(DBUS_REGWEN_12), - REG_NAME_ENTRY(DBUS_REGWEN_13), - REG_NAME_ENTRY(DBUS_REGWEN_14), - REG_NAME_ENTRY(DBUS_REGWEN_15), - REG_NAME_ENTRY(DBUS_REGWEN_16), - REG_NAME_ENTRY(DBUS_REGWEN_17), - REG_NAME_ENTRY(DBUS_REGWEN_18), - REG_NAME_ENTRY(DBUS_REGWEN_19), - REG_NAME_ENTRY(DBUS_REGWEN_20), - REG_NAME_ENTRY(DBUS_REGWEN_21), - REG_NAME_ENTRY(DBUS_REGWEN_22), - REG_NAME_ENTRY(DBUS_REGWEN_23), - REG_NAME_ENTRY(DBUS_REGWEN_24), - REG_NAME_ENTRY(DBUS_REGWEN_25), - REG_NAME_ENTRY(DBUS_REGWEN_26), - REG_NAME_ENTRY(DBUS_REGWEN_27), - REG_NAME_ENTRY(DBUS_REGWEN_28), - REG_NAME_ENTRY(DBUS_REGWEN_29), - REG_NAME_ENTRY(DBUS_REGWEN_30), - REG_NAME_ENTRY(DBUS_REGWEN_31), - REG_NAME_ENTRY(DBUS_ADDR_EN_0), - REG_NAME_ENTRY(DBUS_ADDR_EN_1), - REG_NAME_ENTRY(DBUS_ADDR_EN_2), - REG_NAME_ENTRY(DBUS_ADDR_EN_3), - REG_NAME_ENTRY(DBUS_ADDR_EN_4), - REG_NAME_ENTRY(DBUS_ADDR_EN_5), - REG_NAME_ENTRY(DBUS_ADDR_EN_6), - REG_NAME_ENTRY(DBUS_ADDR_EN_7), - REG_NAME_ENTRY(DBUS_ADDR_EN_8), - REG_NAME_ENTRY(DBUS_ADDR_EN_9), - REG_NAME_ENTRY(DBUS_ADDR_EN_10), - REG_NAME_ENTRY(DBUS_ADDR_EN_11), - REG_NAME_ENTRY(DBUS_ADDR_EN_12), - REG_NAME_ENTRY(DBUS_ADDR_EN_13), - REG_NAME_ENTRY(DBUS_ADDR_EN_14), - REG_NAME_ENTRY(DBUS_ADDR_EN_15), - REG_NAME_ENTRY(DBUS_ADDR_EN_16), - REG_NAME_ENTRY(DBUS_ADDR_EN_17), - REG_NAME_ENTRY(DBUS_ADDR_EN_18), - REG_NAME_ENTRY(DBUS_ADDR_EN_19), - REG_NAME_ENTRY(DBUS_ADDR_EN_20), - REG_NAME_ENTRY(DBUS_ADDR_EN_21), - REG_NAME_ENTRY(DBUS_ADDR_EN_22), - REG_NAME_ENTRY(DBUS_ADDR_EN_23), - REG_NAME_ENTRY(DBUS_ADDR_EN_24), - REG_NAME_ENTRY(DBUS_ADDR_EN_25), - REG_NAME_ENTRY(DBUS_ADDR_EN_26), - REG_NAME_ENTRY(DBUS_ADDR_EN_27), - REG_NAME_ENTRY(DBUS_ADDR_EN_28), - REG_NAME_ENTRY(DBUS_ADDR_EN_29), - REG_NAME_ENTRY(DBUS_ADDR_EN_30), - REG_NAME_ENTRY(DBUS_ADDR_EN_31), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_0), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_1), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_2), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_3), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_4), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_5), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_6), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_7), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_8), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_9), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_10), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_11), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_12), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_13), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_14), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_15), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_16), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_17), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_18), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_19), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_20), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_21), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_22), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_23), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_24), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_25), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_26), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_27), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_28), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_29), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_30), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_31), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_0), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_1), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_2), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_3), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_4), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_5), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_6), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_7), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_8), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_9), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_10), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_11), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_12), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_13), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_14), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_15), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_16), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_17), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_18), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_19), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_20), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_21), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_22), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_23), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_24), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_25), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_26), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_27), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_28), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_29), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_30), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_31), - REG_NAME_ENTRY(NMI_ENABLE), - REG_NAME_ENTRY(NMI_STATE), - REG_NAME_ENTRY(ERR_STATUS), - REG_NAME_ENTRY(RND_DATA), - REG_NAME_ENTRY(RND_STATUS), - REG_NAME_ENTRY(FPGA_INFO), - REG_NAME_ENTRY(DV_SIM_STATUS), - REG_NAME_ENTRY(DV_SIM_LOG), - REG_NAME_ENTRY(DV_SIM_WIN2), - REG_NAME_ENTRY(DV_SIM_WIN3), - REG_NAME_ENTRY(DV_SIM_WIN4), - REG_NAME_ENTRY(DV_SIM_WIN5), - REG_NAME_ENTRY(DV_SIM_WIN6), - REG_NAME_ENTRY(DV_SIM_WIN7), -}; - -#define OT_IBEX_CPU_EN_MASK (((1u << OT_IBEX_CPU_EN_COUNT)) - 1u) - -static const char MISSING_LOG_STRING[] = "(?)"; - -#define CASE_RANGE(_reg_, _cnt_) (_reg_)...((_reg_) + (_cnt_) - (1u)) - -#define xtrace_ot_ibex_wrapper_info(_s_, _msg_) \ - trace_ot_ibex_wrapper_info((_s_)->ot_id, __func__, __LINE__, _msg_) -#define xtrace_ot_ibex_wrapper_error(_s_, _msg_) \ - trace_ot_ibex_wrapper_error((_s_)->ot_id, __func__, __LINE__, _msg_) - -/* - * These enumerated values are not HW values, however the two last values are - * documented by DV SW as:"This is a terminal state. Any code appearing after - * this value is set is unreachable." - * - * There are therefore handled as special HW-SW case that triggers explicit - * QEMU termination with a special exit code. - */ -typedef enum { - TEST_STATUS_IN_BOOT_ROM = 0xb090, /* 'bogo', BOotrom GO */ - TEST_STATUS_IN_BOOT_ROM_HALT = 0xb057, /* 'bost', BOotrom STop */ - TEST_STATUS_IN_TEST = 0x4354, /* 'test' */ - TEST_STATUS_IN_WFI = 0x1d1e, /* 'idle' */ - TEST_STATUS_PASSED = 0x900d, /* 'good' */ - TEST_STATUS_FAILED = 0xbaad /* 'baad' */ -} OtIbexTestStatus; - -/* OpenTitan SW log severities. */ -typedef enum { - TEST_LOG_SEVERITY_INFO, - TEST_LOG_SEVERITY_WARN, - TEST_LOG_SEVERITY_ERROR, - TEST_LOG_SEVERITY_FATAL, -} OtIbexTestLogLevel; - -/* OpenTitan SW log metadata used to format a log line. */ -typedef struct { - OtIbexTestLogLevel severity; - uint32_t file_name_ptr; /* const char * in RV32 */ - uint32_t line; - uint32_t nargs; - uint32_t format_ptr; /* const char * in RV32 */ -} OtIbexTestLogFields; - -typedef enum { - TEST_LOG_STATE_IDLE, - TEST_LOG_STATE_ARG, - TEST_LOG_STATE_ERROR, -} OtIbexTestLogState; - -typedef struct { - OtIbexTestLogState state; - AddressSpace *as; - OtIbexTestLogFields fields; - unsigned arg_count; - uintptr_t *args; /* arguments */ - bool *strargs; /* whether slot should be freed or a not */ - const char *fmtptr; /* current pointer in format string */ - char *filename; - char *format; -} OtIbexTestLogEngine; - -struct OtIbexWrapperDjState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - MemoryRegion remappers[PARAM_NUM_REGIONS]; - MemoryRegion *sys_mem; - IbexIRQ alerts[PARAM_NUM_ALERTS]; - - uint32_t *regs; - OtIbexTestLogEngine *log_engine; - CPUState *cpu; - uint8_t cpu_en_bm; - bool esc_rx; - bool entropy_requested; - bool edn_connected; - - /* Optional properties */ - char *ot_id; - char *lc_ignore_ids; - OtEDNState *edn; - uint8_t edn_ep; - uint8_t qemu_version; - bool lc_ignore; - CharBackend chr; -}; - -/* should match OpenTitan definition */ -static_assert(sizeof(OtIbexTestLogFields) == 20u, - "Invalid OtIbexTestLogFields structure"); - -static void ot_ibex_wrapper_dj_update_alerts(OtIbexWrapperDjState *s) -{ - uint32_t level = s->regs[R_ALERT_TEST]; - - if (s->regs[R_SW_FATAL_ERR] != OT_MULTIBITBOOL4_FALSE) { - level |= R_SW_FATAL_ERR_VAL_MASK; - } - - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); - } -} - -static void -ot_ibex_wrapper_dj_remapper_destroy(OtIbexWrapperDjState *s, unsigned slot) -{ - g_assert(slot < PARAM_NUM_REGIONS); - MemoryRegion *mr = &s->remappers[slot]; - if (memory_region_is_mapped(mr)) { - trace_ot_ibex_wrapper_unmap(s->ot_id, slot); - memory_region_transaction_begin(); - memory_region_set_enabled(mr, false); - /* QEMU memory model enables unparenting alias regions */ - memory_region_del_subregion(s->sys_mem, mr); - memory_region_transaction_commit(); - } -} - -/* NOLINTNEXTLINE */ -static bool ot_ibex_wrapper_dj_mr_map_offset( - hwaddr *offset, const MemoryRegion *root, hwaddr dst, size_t size, - const MemoryRegion *tmr) -{ - if (root == tmr) { - return true; - } - - const MemoryRegion *mr; - - QTAILQ_FOREACH(mr, &root->subregions, subregions_link) { - if (dst < mr->addr || - (dst + size) > (mr->addr + int128_getlo(mr->size))) { - continue; - } - - bool ret; - - if (mr->alias) { - hwaddr alias_offset = mr->addr - mr->alias_offset; - dst -= alias_offset; - - ret = ot_ibex_wrapper_dj_mr_map_offset(offset, mr->alias, dst, size, - tmr); - if (ret) { - /* - * the selected MR tree leads to the target region, so update - * the alias offset with the local offset - */ - *offset += alias_offset; - } - } else { - ret = ot_ibex_wrapper_dj_mr_map_offset(offset, mr, dst, size, tmr); - if (ret) { - *offset += mr->addr; - } - } - - return ret; - } - - return false; -} - -static void ot_ibex_wrapper_dj_remapper_create( - OtIbexWrapperDjState *s, unsigned slot, hwaddr dst, hwaddr src, size_t size) -{ - g_assert(slot < PARAM_NUM_REGIONS); - MemoryRegion *mr = &s->remappers[slot]; - g_assert(!memory_region_is_mapped(mr)); - - int priority = (int)(PARAM_NUM_REGIONS - slot); - - MemoryRegion *mr_dst; - - char *name = g_strdup_printf(TYPE_OT_IBEX_WRAPPER_DJ "-remap[%u]", slot); - - memory_region_transaction_begin(); - /* - * try to map onto the actual device if there's a single one, otherwise - * map on the whole address space. - */ - MemoryRegionSection mrs; - mrs = memory_region_find(s->sys_mem, dst, (uint64_t)size); - size_t mrs_lsize = int128_getlo(mrs.size); - mr_dst = (mrs.mr && mrs_lsize >= size) ? mrs.mr : s->sys_mem; - - /* - * adjust the offset if the memory region target for the mapping - * is itself mapped through memory region(s) - */ - hwaddr offset = 0; - if (ot_ibex_wrapper_dj_mr_map_offset(&offset, s->sys_mem, dst, size, - mr_dst)) { - offset = dst - offset; - } - - trace_ot_ibex_wrapper_map(s->ot_id, slot, src, dst, size, mr_dst->name, - (uint32_t)offset); - memory_region_init_alias(mr, OBJECT(s), name, mr_dst, offset, - (uint64_t)size); - memory_region_add_subregion_overlap(s->sys_mem, src, mr, priority); - memory_region_set_enabled(mr, true); - memory_region_transaction_commit(); - g_free(name); - -#ifdef PRINT_MTREE - mtree_info(false, false, false, true); -#endif -} - -static void -ot_ibex_wrapper_dj_fill_entropy(void *opaque, uint32_t bits, bool fips) -{ - OtIbexWrapperDjState *s = opaque; - - trace_ot_ibex_wrapper_fill_entropy(s->ot_id, bits, fips); - - s->regs[R_RND_DATA] = bits; - s->regs[R_RND_STATUS] = R_RND_STATUS_RND_DATA_VALID_MASK; - if (fips) { - s->regs[R_RND_STATUS] |= R_RND_STATUS_RND_DATA_FIPS_MASK; - } - - s->entropy_requested = false; -} - -static bool ot_ibex_wrapper_dj_has_edn(OtIbexWrapperDjState *s) -{ - return (s->edn != NULL) && (s->edn_ep != UINT8_MAX); -} - -static void ot_ibex_wrapper_dj_request_entropy(OtIbexWrapperDjState *s) -{ - if (!s->entropy_requested && ot_ibex_wrapper_dj_has_edn(s)) { - if (unlikely(!s->edn_connected)) { - ot_edn_connect_endpoint(s->edn, s->edn_ep, - &ot_ibex_wrapper_dj_fill_entropy, s); - s->edn_connected = true; - } - s->entropy_requested = true; - trace_ot_ibex_wrapper_request_entropy(s->ot_id, s->entropy_requested); - if (ot_edn_request_entropy(s->edn, s->edn_ep)) { - s->entropy_requested = false; - xtrace_ot_ibex_wrapper_error(s, "failed to request entropy"); - } - } -} - -static void ot_ibex_wrapper_dj_update_remap(OtIbexWrapperDjState *s, bool doi, - unsigned slot) -{ - (void)doi; - g_assert(slot < PARAM_NUM_REGIONS); - /* - * Warning: - * for now, QEMU is unable to distinguish instruction or data access. - * in this implementation, we chose to enable remap whenever either D or I - * remapping is selected, and both D & I configuration match; we disable - * translation when both D & I are remapping are disabled - */ - - bool en_remap_i = s->regs[R_IBUS_ADDR_EN_0 + slot]; - bool en_remap_d = s->regs[R_DBUS_ADDR_EN_0 + slot]; - if (!en_remap_i && !en_remap_d) { - /* disable */ - ot_ibex_wrapper_dj_remapper_destroy(s, slot); - } else { - uint32_t src_match_i = s->regs[R_IBUS_ADDR_MATCHING_0 + slot]; - uint32_t src_match_d = s->regs[R_DBUS_ADDR_MATCHING_0 + slot]; - if (src_match_i != src_match_d) { - /* I and D do not match, do nothing */ - xtrace_ot_ibex_wrapper_info(s, "src remapping do not match"); - return; - } - uint32_t remap_addr_i = s->regs[R_IBUS_REMAP_ADDR_0 + slot]; - uint32_t remap_addr_d = s->regs[R_DBUS_REMAP_ADDR_0 + slot]; - if (remap_addr_i != remap_addr_d) { - /* I and D do not match, do nothing */ - xtrace_ot_ibex_wrapper_info(s, "dst remapping do not match"); - return; - } - /* enable */ - uint32_t map_size = (-src_match_i & (src_match_i + 1u)) << 1u; - uint32_t map_mask = ~(map_size - 1u); - uint32_t src_base = src_match_i & map_mask; - uint32_t dst_base = remap_addr_i & map_mask; - - ot_ibex_wrapper_dj_remapper_destroy(s, slot); - ot_ibex_wrapper_dj_remapper_create(s, slot, (hwaddr)dst_base, - (hwaddr)src_base, (size_t)map_size); - } -} - -static bool ot_ibex_wrapper_dj_log_load_string(OtIbexWrapperDjState *s, - hwaddr addr, char **str) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - /* - * Logging needs to access strings that are stored in guest memory. - * This function adopts a "best effort" strategy: it may fails to retrieve - * a log string argument. - */ - bool res = false; - MemoryRegionSection mrs; - - /* - * Find the region where the string may reside, using a small size as the - * length of the string is not known, and memory_region_find would fail if - * look up is performed behing the end of the containing memory region - */ - mrs = memory_region_find(eng->as->root, addr, 4u); - MemoryRegion *mr = mrs.mr; - if (!mr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); - goto end; - } - - if (!memory_region_is_ram(mr)) { - xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); - goto end; - } - - uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); - if (!src) { - xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); - goto end; - } - src += mrs.offset_within_region; - - size_t size = int128_getlo(mrs.size) - mrs.offset_within_region; - size = MIN(size, 4096u); - - const void *end = memchr((const void *)src, '\0', size); - if (!end) { - xtrace_ot_ibex_wrapper_error(s, "cannot compute strlen"); - goto end; - } - size_t slen = (uintptr_t)end - (uintptr_t)src; - - char *tstr = g_malloc(slen + 1); - memcpy(tstr, (const void *)src, slen); - tstr[slen] = '\0'; - - *str = tstr; - res = true; - -end: - if (mr) { - memory_region_unref(mr); - } - return res; -} - -static bool -ot_ibex_wrapper_dj_log_load_fields(OtIbexWrapperDjState *s, hwaddr addr) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - MemoryRegionSection mrs; - mrs = memory_region_find(eng->as->root, addr, sizeof(eng->fields)); - - MemoryRegion *mr = mrs.mr; - bool res = false; - - if (!mr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); - goto end; - } - - if (!memory_region_is_ram(mr)) { - xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); - goto end; - } - - uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); - if (!src) { - xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); - goto end; - } - src += mrs.offset_within_region; - - memcpy(&eng->fields, (const void *)src, sizeof(eng->fields)); - - if (eng->fields.file_name_ptr) { - if (!ot_ibex_wrapper_dj_log_load_string(s, - (uintptr_t) - eng->fields.file_name_ptr, - &eng->filename)) { - xtrace_ot_ibex_wrapper_error(s, "cannot get filename"); - goto end; - } - } - - if (eng->fields.format_ptr) { - if (!ot_ibex_wrapper_dj_log_load_string(s, - (uintptr_t) - eng->fields.format_ptr, - &eng->format)) { - xtrace_ot_ibex_wrapper_error(s, "cannot get format string"); - goto end; - } - } - - eng->arg_count = 0; - eng->fmtptr = eng->format; - if (eng->fields.nargs) { - eng->args = g_new0(uintptr_t, eng->fields.nargs); - eng->strargs = g_new0(bool, eng->fields.nargs); - } else { - eng->args = NULL; - eng->strargs = NULL; - } - - res = true; - -end: - if (mr) { - memory_region_unref(mr); - } - return res; -} - -static bool -ot_ibex_wrapper_dj_log_load_arg(OtIbexWrapperDjState *s, uint32_t value) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - if (!eng->fmtptr) { - xtrace_ot_ibex_wrapper_error(s, "invalid fmtptr"); - return false; - } - - bool cont; - do { - cont = false; - eng->fmtptr = strchr(eng->fmtptr, '%'); - if (!eng->fmtptr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); - return false; - } - eng->fmtptr++; - switch (*eng->fmtptr) { - case '%': - eng->fmtptr++; - cont = true; - continue; - case '\0': - xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); - return false; - case 's': - if (!ot_ibex_wrapper_dj_log_load_string( - s, (hwaddr)value, (char **)&eng->args[eng->arg_count])) { - xtrace_ot_ibex_wrapper_error(s, "cannot load string arg"); - /* use a default string, best effort strategy */ - eng->args[eng->arg_count] = (uintptr_t)&MISSING_LOG_STRING[0]; - } else { - /* string has been dynamically allocated, and should be freed */ - eng->strargs[eng->arg_count] = true; - } - break; - default: - eng->args[eng->arg_count] = (uintptr_t)value; - break; - } - } while (cont); - - eng->arg_count++; - - return true; -} - -static void ot_ibex_wrapper_dj_log_cleanup(OtIbexWrapperDjState *s) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - if (eng->strargs && eng->args) { - for (unsigned ix = 0; ix < eng->fields.nargs; ix++) { - if (eng->strargs[ix]) { - if (eng->args[ix]) { - g_free((void *)eng->args[ix]); - } - } - } - } - g_free(eng->format); - g_free(eng->filename); - g_free(eng->strargs); - g_free(eng->args); - eng->format = NULL; - eng->filename = NULL; - eng->fmtptr = NULL; - eng->strargs = NULL; - eng->args = NULL; -} - -static void ot_ibex_wrapper_dj_log_emit(OtIbexWrapperDjState *s) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - const char *level; - switch (eng->fields.severity) { - case TEST_LOG_SEVERITY_INFO: - level = "INFO"; - break; - case TEST_LOG_SEVERITY_WARN: - level = "WARN "; - break; - case TEST_LOG_SEVERITY_ERROR: - level = "ERROR "; - break; - case TEST_LOG_SEVERITY_FATAL: - level = "FATAL "; - break; - default: - level = "DEBUG "; - break; - } - - /* discard the path of the stored file to reduce log message length */ - const char *basename = eng->filename ? strrchr(eng->filename, '/') : NULL; - basename = basename ? basename + 1u : eng->filename; - - char *logfmt = g_strdup_printf("%s %s:%d %s\n", level, basename, - eng->fields.line, eng->format); - -/* hack ahead: use the uintptr_t array as a va_list */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" - char *logmsg = g_strdup_vprintf(logfmt, (char *)eng->args); -#pragma GCC diagnostic pop - - if (!qemu_chr_fe_backend_connected(&s->chr)) { - qemu_log_mask(LOG_STRACE, "%s", logmsg); - } else { - qemu_chr_fe_write(&s->chr, (const uint8_t *)logmsg, - (int)strlen(logmsg)); - } - - g_free(logmsg); - g_free(logfmt); - - ot_ibex_wrapper_dj_log_cleanup(s); -} - -static void -ot_ibex_wrapper_dj_status_report(OtIbexWrapperDjState *s, uint32_t value) -{ - const char *msg; - switch (value) { - case TEST_STATUS_IN_BOOT_ROM: - msg = "IN_BOOT_ROM"; - break; - case TEST_STATUS_IN_BOOT_ROM_HALT: - msg = "IN_BOOT_ROM_HALT"; - break; - case TEST_STATUS_IN_TEST: - msg = "IN_TEST"; - break; - case TEST_STATUS_IN_WFI: - msg = "IN_BOOT_WFI"; - break; - case TEST_STATUS_PASSED: - msg = "PASSED"; - break; - case TEST_STATUS_FAILED: - msg = "FAILED"; - break; - default: - msg = "UNKNOWN"; - break; - } - - if (!qemu_chr_fe_backend_connected(&s->chr)) { - qemu_log_mask(LOG_STRACE, "%s\n", msg); - } else { - qemu_chr_fe_write(&s->chr, (const uint8_t *)msg, (int)strlen(msg)); - uint8_t eol[] = { '\n' }; - qemu_chr_fe_write(&s->chr, eol, (int)sizeof(eol)); - } -} - -static void -ot_ibex_wrapper_dj_log_handle(OtIbexWrapperDjState *s, uint32_t value) -{ - /* - * Note about logging: - * - * For OT DV logging to work, the "fields" should not be placed in the - * default linker-discarded sections such as ".logs.fields" - * i.e. __attribute__((section(".logs.fields"))) should be removed from - * the "LOG()"" macro. - */ - OtIbexTestLogEngine *eng = s->log_engine; - - switch (eng->state) { - case TEST_LOG_STATE_IDLE: - if (!ot_ibex_wrapper_dj_log_load_fields(s, (hwaddr)value)) { - eng->state = TEST_LOG_STATE_ERROR; - ot_ibex_wrapper_dj_log_cleanup(s); - break; - } - if (eng->fields.nargs) { - eng->state = TEST_LOG_STATE_ARG; - } else { - ot_ibex_wrapper_dj_log_emit(s); - eng->state = TEST_LOG_STATE_IDLE; - } - break; - case TEST_LOG_STATE_ARG: - if (!ot_ibex_wrapper_dj_log_load_arg(s, value)) { - ot_ibex_wrapper_dj_log_cleanup(s); - eng->state = TEST_LOG_STATE_ERROR; - } - if (eng->arg_count == eng->fields.nargs) { - ot_ibex_wrapper_dj_log_emit(s); - eng->state = TEST_LOG_STATE_IDLE; - } - break; - case TEST_LOG_STATE_ERROR: - default: - qemu_log_mask(LOG_GUEST_ERROR, "Can no longer handle DV log, in error"); - break; - } -} - -static void ot_ibex_wrapper_dj_update_exec(OtIbexWrapperDjState *s) -{ - /* - * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and - * power manager CPU enable are all enabled." - */ - bool enable = - ((s->cpu_en_bm & OT_IBEX_CPU_EN_MASK) == OT_IBEX_CPU_EN_MASK) && - !s->esc_rx; - trace_ot_ibex_wrapper_update_exec(s->ot_id ?: "", s->cpu_en_bm, s->esc_rx, - enable); - - if (enable) { - s->cpu->halted = 0; - if (s->cpu->held_in_reset) { - resettable_release_reset(OBJECT(s->cpu), RESET_TYPE_COLD); - } - cpu_resume(s->cpu); - } else { - if (!s->cpu->halted) { - s->cpu->halted = 1; - cpu_exit(s->cpu); - } - } -} - -static void ot_ibex_wrapper_dj_cpu_enable_recv(void *opaque, int n, int level) -{ - OtIbexWrapperDjState *s = opaque; - - g_assert((unsigned)n < OT_IBEX_CPU_EN_COUNT); - - if (level) { - s->cpu_en_bm |= 1u << (unsigned)n; - } else { - s->cpu_en_bm &= ~(1u << (unsigned)n); - } - - /* - * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and - * power manager CPU enable are all enabled." - */ - trace_ot_ibex_wrapper_cpu_enable(s->ot_id ?: "", n ? "PWR" : "LC", - (bool)level); - - ot_ibex_wrapper_dj_update_exec(s); -} - -static void ot_ibex_wrapper_dj_escalate_rx(void *opaque, int n, int level) -{ - OtIbexWrapperDjState *s = opaque; - - g_assert(n == 0); - - trace_ot_ibex_wrapper_escalate_rx(s->ot_id ?: "", (bool)level); - - s->esc_rx = (bool)level; - - ot_ibex_wrapper_dj_update_exec(s); -} - -static uint64_t -ot_ibex_wrapper_dj_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - OtIbexWrapperDjState *s = opaque; - (void)size; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - - switch (reg) { - case R_RND_DATA: - if (!ot_ibex_wrapper_dj_has_edn(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: No EDN connection\n", __func__); - val32 = 0; - break; - } - val32 = s->regs[reg]; - if (!(s->regs[R_RND_STATUS] & R_RND_STATUS_RND_DATA_VALID_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Read invalid entropy data 0x%08x\n", __func__, - val32); - } - s->regs[reg] = 0; - s->regs[R_RND_STATUS] = 0; - ot_ibex_wrapper_dj_request_entropy(s); - break; - case R_RND_STATUS: - if (!ot_ibex_wrapper_dj_has_edn(s)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: No EDN connection\n", __func__); - val32 = 0; - break; - } - val32 = s->regs[reg]; - if (!(val32 & R_RND_STATUS_RND_DATA_VALID_MASK)) { - ot_ibex_wrapper_dj_request_entropy(s); - } - break; - case R_DV_SIM_LOG: - val32 = 0; - break; - default: - val32 = s->regs[reg]; - break; - } - - uint32_t pc = ibex_get_current_pc(); - trace_ot_ibex_wrapper_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), - val32, pc); - - return (uint64_t)val32; -}; - -static void ot_ibex_wrapper_dj_regs_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned size) -{ - OtIbexWrapperDjState *s = opaque; - (void)size; - uint32_t val32 = (uint32_t)val64; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - trace_ot_ibex_wrapper_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), - val32, pc); - - switch (reg) { - case R_ALERT_TEST: - val32 &= ALERT_TEST_MASK; - s->regs[reg] = val32; - ot_ibex_wrapper_dj_update_alerts(s); - break; - case R_SW_FATAL_ERR: - if ((val32 >> 16u) == 0xC0DEu) { - /* guest should now use DV_SIM_STATUS register */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: QEMU exit on SW_FATAL_ERR is deprecated", - __func__); - /* discard MSB magic */ - val32 &= UINT16_MAX; - /* discard multibool4false mark */ - val32 >>= 4u; - /* std exit code should be in [0..127] range */ - if (val32 > 127u) { - val32 = 127u; - } - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, (int)val32); - } - val32 &= R_SW_FATAL_ERR_VAL_MASK; - s->regs[reg] = ot_multibitbool_w1s_write(s->regs[reg], val32, 4u); - ot_ibex_wrapper_dj_update_alerts(s); - break; - case CASE_RANGE(R_IBUS_REGWEN_0, PARAM_NUM_REGIONS): - case CASE_RANGE(R_DBUS_REGWEN_0, PARAM_NUM_REGIONS): - val32 &= REGWEN_EN_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case CASE_RANGE(R_IBUS_ADDR_EN_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_IBUS_ADDR_EN_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_dj_update_remap(s, false, reg - R_IBUS_ADDR_EN_0); - break; - case CASE_RANGE(R_IBUS_ADDR_MATCHING_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_IBUS_ADDR_MATCHING_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - break; - case CASE_RANGE(R_IBUS_REMAP_ADDR_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_IBUS_REMAP_ADDR_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_dj_update_remap(s, false, reg - R_IBUS_REMAP_ADDR_0); - break; - case CASE_RANGE(R_DBUS_ADDR_EN_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_DBUS_ADDR_EN_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_dj_update_remap(s, true, reg - R_DBUS_ADDR_EN_0); - break; - case CASE_RANGE(R_DBUS_ADDR_MATCHING_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_DBUS_ADDR_MATCHING_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - break; - case CASE_RANGE(R_DBUS_REMAP_ADDR_0, PARAM_NUM_REGIONS): - if (s->regs[reg - R_DBUS_REMAP_ADDR_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_dj_update_remap(s, true, reg - R_DBUS_REMAP_ADDR_0); - break; - case R_DV_SIM_STATUS: - ot_ibex_wrapper_dj_status_report(s, val32); - switch (val32 & R_DV_SIM_STATUS_CODE_MASK) { - case TEST_STATUS_PASSED: - trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM success, exiting", 0); - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, 0); - break; - case TEST_STATUS_FAILED: { - uint32_t info = FIELD_EX32(val32, DV_SIM_STATUS, INFO); - int ret; - if (info == 0) { - /* no extra info */ - ret = 1; - } else { - ret = (int)(info & 0x7fu); - } - trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM failure, exiting", - ret); - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, ret); - break; - } - default: - s->regs[reg] = val32; - break; - } - break; - case R_DV_SIM_LOG: - ot_ibex_wrapper_dj_log_handle(s, val32); - break; - default: - s->regs[reg] = val32; - break; - } -}; - -/* all properties are optional */ -static Property ot_ibex_wrapper_dj_properties[] = { - DEFINE_PROP_STRING("ot_id", OtIbexWrapperDjState, ot_id), - DEFINE_PROP_LINK("edn", OtIbexWrapperDjState, edn, TYPE_OT_EDN, - OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtIbexWrapperDjState, edn_ep, UINT8_MAX), - DEFINE_PROP_BOOL("lc-ignore", OtIbexWrapperDjState, lc_ignore, false), - DEFINE_PROP_UINT8("qemu_version", OtIbexWrapperDjState, qemu_version, 0), - DEFINE_PROP_STRING("lc-ignore-ids", OtIbexWrapperDjState, lc_ignore_ids), - DEFINE_PROP_CHR("logdev", OtIbexWrapperDjState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static const MemoryRegionOps ot_ibex_wrapper_dj_regs_ops = { - .read = &ot_ibex_wrapper_dj_regs_read, - .write = &ot_ibex_wrapper_dj_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static void ot_ibex_wrapper_dj_reset_enter(Object *obj, ResetType type) -{ - OtIbexWrapperClass *c = OT_IBEX_WRAPPER_DJ_GET_CLASS(obj); - OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(obj); - - trace_ot_ibex_wrapper_reset(s->ot_id, "enter"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - g_assert(s->ot_id); - g_assert(s->sys_mem); - - if (s->lc_ignore_ids) { - char *ign = g_strdup(s->lc_ignore_ids); - char *token = strtok(ign, ","); - while (token) { - if (!strcmp(token, s->ot_id)) { - s->lc_ignore = true; - } - token = strtok(NULL, ","); - } - g_free(ign); - } - - if (!s->cpu) { - CPUState *cpu = ot_common_get_local_cpu(DEVICE(s)); - if (!cpu) { - error_setg(&error_fatal, "Could not find the associated vCPU"); - g_assert_not_reached(); - } - s->cpu = cpu; - } - - for (unsigned slot = 0; slot < PARAM_NUM_REGIONS; slot++) { - ot_ibex_wrapper_dj_remapper_destroy(s, slot); - } - - memset(s->regs, 0, REGS_SIZE); - s->regs[R_SW_RECOV_ERR] = 0x9u; - s->regs[R_SW_FATAL_ERR] = 0x9u; - for (unsigned ix = 0; ix < PARAM_NUM_REGIONS; ix++) { - s->regs[R_IBUS_REGWEN_0 + ix] = 0x1u; - s->regs[R_DBUS_REGWEN_0 + ix] = 0x1u; - } - /* 'QMU_' in LE, _ is the QEMU version stored in the MSB */ - s->regs[R_FPGA_INFO] = 0x00554d51u + (((uint32_t)s->qemu_version) << 24u); - s->entropy_requested = false; - s->cpu_en_bm = s->lc_ignore ? (1u << OT_IBEX_LC_CTRL_CPU_EN) : 0; - - memset(s->log_engine, 0, sizeof(*s->log_engine)); -} - -static void ot_ibex_wrapper_dj_reset_exit(Object *obj, ResetType type) -{ - OtIbexWrapperClass *c = OT_IBEX_WRAPPER_DJ_GET_CLASS(obj); - OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(obj); - - trace_ot_ibex_wrapper_reset(s->ot_id, "exit"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); - - /* "Upon reset the data will be invalid with a new EDN request pending." */ - ot_ibex_wrapper_dj_request_entropy(s); -} - -static void ot_ibex_wrapper_dj_realize(DeviceState *dev, Error **errp) -{ - OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(dev); - (void)errp; - - s->sys_mem = ot_common_get_local_address_space(dev)->root; -} - -static void ot_ibex_wrapper_dj_init(Object *obj) -{ - OtIbexWrapperDjState *s = OT_IBEX_WRAPPER_DJ(obj); - - memory_region_init_io(&s->mmio, obj, &ot_ibex_wrapper_dj_regs_ops, s, - TYPE_OT_IBEX_WRAPPER_DJ, REGS_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); - - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); - } - - qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_dj_cpu_enable_recv, - OT_IBEX_WRAPPER_CPU_EN, OT_IBEX_CPU_EN_COUNT); - qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_dj_escalate_rx, - OT_ALERT_ESCALATE, 1); - - s->regs = g_new0(uint32_t, REGS_COUNT); - s->log_engine = g_new0(OtIbexTestLogEngine, 1u); -} - -static void ot_ibex_wrapper_dj_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - (void)data; - - dc->realize = &ot_ibex_wrapper_dj_realize; - device_class_set_props(dc, ot_ibex_wrapper_dj_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - ResettableClass *rc = RESETTABLE_CLASS(klass); - OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_dj_reset_enter, - NULL, &ot_ibex_wrapper_dj_reset_exit, - &ic->parent_phases); -} - -static const TypeInfo ot_ibex_wrapper_dj_info = { - .name = TYPE_OT_IBEX_WRAPPER_DJ, - .parent = TYPE_OT_IBEX_WRAPPER, - .instance_size = sizeof(OtIbexWrapperDjState), - .instance_init = &ot_ibex_wrapper_dj_init, - .class_init = &ot_ibex_wrapper_dj_class_init, - .class_size = sizeof(OtIbexWrapperClass), -}; - -static void ot_ibex_wrapper_dj_register_types(void) -{ - type_register_static(&ot_ibex_wrapper_dj_info); -} - -type_init(ot_ibex_wrapper_dj_register_types); diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c deleted file mode 100644 index 0501197d10e58..0000000000000 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * QEMU OpenTitan EarlGrey Ibex wrapper device - * - * Copyright (c) 2022-2024 Rivos, Inc. - * Copyright (c) 2025 lowRISC contributors. - * - * Author(s): - * Emmanuel Blot - * Loïc Lefort - * - * 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 "qemu/log.h" -#include "qemu/typedefs.h" -#include "qapi/error.h" -#include "chardev/char-fe.h" -#include "exec/address-spaces.h" -#include "hw/opentitan/ot_alert.h" -#include "hw/opentitan/ot_common.h" -#include "hw/opentitan/ot_edn.h" -#include "hw/opentitan/ot_ibex_wrapper_eg.h" -#include "hw/qdev-properties-system.h" -#include "hw/qdev-properties.h" -#include "hw/registerfields.h" -#include "hw/riscv/ibex_common.h" -#include "hw/riscv/ibex_irq.h" -#include "hw/sysbus.h" -#include "sysemu/runstate.h" -#include "trace.h" - - -/* DEBUG: define to print the full memory view on remap */ -#undef PRINT_MTREE - -#define PARAM_NUM_SW_ALERTS 2u -#define PARAM_NUM_REGIONS 2u -#define PARAM_NUM_SCRATCH_WORDS 8u -#define PARAM_NUM_ALERTS 4u - -/* clang-format off */ -REG32(ALERT_TEST, 0x0u) - FIELD(ALERT_TEST, FATAL_SW, 0u, 1u) - FIELD(ALERT_TEST, RECOV_SW, 1u, 1u) - FIELD(ALERT_TEST, FATAL_HW, 2u, 1u) - FIELD(ALERT_TEST, RECOV_HW, 3u, 1u) -REG32(SW_RECOV_ERR, 0x4u) - FIELD(SW_RECOV_ERR, VAL, 0u, 4u) -REG32(SW_FATAL_ERR, 0x8u) - FIELD(SW_FATAL_ERR, VAL, 0u, 4u) -REG32(IBUS_REGWEN_0, 0xcu) - SHARED_FIELD(REGWEN_EN, 0u, 1u) -REG32(IBUS_REGWEN_1, 0x10u) -REG32(IBUS_ADDR_EN_0, 0x14u) - SHARED_FIELD(ADDR_EN, 0u, 1u) -REG32(IBUS_ADDR_EN_1, 0x18u) -REG32(IBUS_ADDR_MATCHING_0, 0x1cu) -REG32(IBUS_ADDR_MATCHING_1, 0x20u) -REG32(IBUS_REMAP_ADDR_0, 0x24u) -REG32(IBUS_REMAP_ADDR_1, 0x28u) -REG32(DBUS_REGWEN_0, 0x2cu) -REG32(DBUS_REGWEN_1, 0x30u) -REG32(DBUS_ADDR_EN_0, 0x34u) -REG32(DBUS_ADDR_EN_1, 0x38u) -REG32(DBUS_ADDR_MATCHING_0, 0x3cu) -REG32(DBUS_ADDR_MATCHING_1, 0x40u) -REG32(DBUS_REMAP_ADDR_0, 0x44u) -REG32(DBUS_REMAP_ADDR_1, 0x48u) -REG32(NMI_ENABLE, 0x4cu) - SHARED_FIELD(NMI_ALERT_EN_BIT, 0u, 1u) - SHARED_FIELD(NMI_WDOG_EN_BIT, 1u, 1u) -REG32(NMI_STATE, 0x50u) -REG32(ERR_STATUS, 0x54u) - FIELD(ERR_STATUS, REG_INTG, 0u, 1u) - FIELD(ERR_STATUS, FATAL_INTG, 8u, 1u) - FIELD(ERR_STATUS, FATAL_CORE, 9u, 1u) - FIELD(ERR_STATUS, RECOV_CORE, 10u, 1u) -REG32(RND_DATA, 0x58u) -REG32(RND_STATUS, 0x5cu) - FIELD(RND_STATUS, RND_DATA_VALID, 0u, 1u) - FIELD(RND_STATUS, RND_DATA_FIPS, 1u, 1u) -REG32(FPGA_INFO, 0x60u) -REG32(DV_SIM_STATUS, 0x80u) - FIELD(DV_SIM_STATUS, CODE, 0u, 16u) - FIELD(DV_SIM_STATUS, INFO, 16u, 16u) -REG32(DV_SIM_LOG, 0x84u) -REG32(DV_SIM_WIN2, 0x88u) -REG32(DV_SIM_WIN3, 0x8cu) -REG32(DV_SIM_WIN4, 0x90u) -REG32(DV_SIM_WIN5, 0x94u) -REG32(DV_SIM_WIN6, 0x98u) -REG32(DV_SIM_WIN7, 0x9cu) - -/* clang-format on */ - -#define ALERT_TEST_MASK \ - (R_ALERT_TEST_FATAL_SW_MASK | R_ALERT_TEST_RECOV_SW_MASK | \ - R_ALERT_TEST_FATAL_HW_MASK | R_ALERT_TEST_RECOV_HW_MASK) - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) - -#define R_LAST_REG (R_DV_SIM_WIN7) -#define REGS_COUNT (R_LAST_REG + 1u) -#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) -#define REG_NAME(_reg_) \ - ((((_reg_) <= REGS_COUNT) && REG_NAMES[_reg_]) ? REG_NAMES[_reg_] : "?") - -#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) -static const char *REG_NAMES[REGS_COUNT] = { - REG_NAME_ENTRY(ALERT_TEST), - REG_NAME_ENTRY(SW_RECOV_ERR), - REG_NAME_ENTRY(SW_FATAL_ERR), - REG_NAME_ENTRY(IBUS_REGWEN_0), - REG_NAME_ENTRY(IBUS_REGWEN_1), - REG_NAME_ENTRY(IBUS_ADDR_EN_0), - REG_NAME_ENTRY(IBUS_ADDR_EN_1), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_0), - REG_NAME_ENTRY(IBUS_ADDR_MATCHING_1), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_0), - REG_NAME_ENTRY(IBUS_REMAP_ADDR_1), - REG_NAME_ENTRY(DBUS_REGWEN_0), - REG_NAME_ENTRY(DBUS_REGWEN_1), - REG_NAME_ENTRY(DBUS_ADDR_EN_0), - REG_NAME_ENTRY(DBUS_ADDR_EN_1), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_0), - REG_NAME_ENTRY(DBUS_ADDR_MATCHING_1), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_0), - REG_NAME_ENTRY(DBUS_REMAP_ADDR_1), - REG_NAME_ENTRY(NMI_ENABLE), - REG_NAME_ENTRY(NMI_STATE), - REG_NAME_ENTRY(ERR_STATUS), - REG_NAME_ENTRY(RND_DATA), - REG_NAME_ENTRY(RND_STATUS), - REG_NAME_ENTRY(FPGA_INFO), - REG_NAME_ENTRY(DV_SIM_STATUS), - REG_NAME_ENTRY(DV_SIM_LOG), - REG_NAME_ENTRY(DV_SIM_WIN2), - REG_NAME_ENTRY(DV_SIM_WIN3), - REG_NAME_ENTRY(DV_SIM_WIN4), - REG_NAME_ENTRY(DV_SIM_WIN5), - REG_NAME_ENTRY(DV_SIM_WIN6), - REG_NAME_ENTRY(DV_SIM_WIN7), -}; - -#define OT_IBEX_CPU_EN_MASK (((1u << OT_IBEX_CPU_EN_COUNT)) - 1u) - -static const char MISSING_LOG_STRING[] = "(?)"; - -#define xtrace_ot_ibex_wrapper_info(_s_, _msg_) \ - trace_ot_ibex_wrapper_info((_s_)->ot_id, __func__, __LINE__, _msg_) -#define xtrace_ot_ibex_wrapper_error(_s_, _msg_) \ - trace_ot_ibex_wrapper_error((_s_)->ot_id, __func__, __LINE__, _msg_) - -/* - * These enumerated values are not HW values, however the two last values are - * documented by DV SW as:"This is a terminal state. Any code appearing after - * this value is set is unreachable." - * - * There are therefore handled as special HW-SW case that triggers explicit - * QEMU termination with a special exit code. - */ -typedef enum { - TEST_STATUS_IN_BOOT_ROM = 0xb090, /* 'bogo', BOotrom GO */ - TEST_STATUS_IN_BOOT_ROM_HALT = 0xb057, /* 'bost', BOotrom STop */ - TEST_STATUS_IN_TEST = 0x4354, /* 'test' */ - TEST_STATUS_IN_WFI = 0x1d1e, /* 'idle' */ - TEST_STATUS_PASSED = 0x900d, /* 'good' */ - TEST_STATUS_FAILED = 0xbaad /* 'baad' */ -} OtIbexTestStatus; - -/* OpenTitan SW log severities. */ -typedef enum { - TEST_LOG_SEVERITY_INFO, - TEST_LOG_SEVERITY_WARN, - TEST_LOG_SEVERITY_ERROR, - TEST_LOG_SEVERITY_FATAL, -} OtIbexTestLogLevel; - -/* OpenTitan SW log metadata used to format a log line. */ -typedef struct { - OtIbexTestLogLevel severity; - uint32_t file_name_ptr; /* const char * in RV32 */ - uint32_t line; - uint32_t nargs; - uint32_t format_ptr; /* const char * in RV32 */ -} OtIbexTestLogFields; - -typedef enum { - TEST_LOG_STATE_IDLE, - TEST_LOG_STATE_ARG, - TEST_LOG_STATE_ERROR, -} OtIbexTestLogState; - -typedef struct { - OtIbexTestLogState state; - AddressSpace *as; - OtIbexTestLogFields fields; - unsigned arg_count; - uintptr_t *args; /* arguments */ - bool *strargs; /* whether slot should be freed or a not */ - const char *fmtptr; /* current pointer in format string */ - char *filename; - char *format; -} OtIbexTestLogEngine; - -struct OtIbexWrapperEgState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - MemoryRegion remappers[PARAM_NUM_REGIONS]; - IbexIRQ alerts[PARAM_NUM_ALERTS]; - - uint32_t *regs; - OtIbexTestLogEngine *log_engine; - CPUState *cpu; - uint8_t cpu_en_bm; - bool entropy_requested; - bool edn_connected; - bool esc_rx; - - char *ot_id; - OtEDNState *edn; - uint8_t edn_ep; - uint8_t qemu_version; - CharBackend chr; -}; - -/* should match OpenTitan definition */ -static_assert(sizeof(OtIbexTestLogFields) == 20u, - "Invalid OtIbexTestLogFields structure"); - -static void ot_ibex_wrapper_eg_update_alerts(OtIbexWrapperEgState *s) -{ - uint32_t level = s->regs[R_ALERT_TEST]; - - if (s->regs[R_SW_FATAL_ERR] != OT_MULTIBITBOOL4_FALSE) { - level |= R_SW_FATAL_ERR_VAL_MASK; - } - - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); - } -} - -static void -ot_ibex_wrapper_eg_remapper_destroy(OtIbexWrapperEgState *s, unsigned slot) -{ - g_assert(slot < PARAM_NUM_REGIONS); - MemoryRegion *mr = &s->remappers[slot]; - if (memory_region_is_mapped(mr)) { - trace_ot_ibex_wrapper_unmap(s->ot_id, slot); - memory_region_transaction_begin(); - memory_region_set_enabled(mr, false); - /* QEMU memory model enables unparenting alias regions */ - MemoryRegion *sys_mem = get_system_memory(); - memory_region_del_subregion(sys_mem, mr); - memory_region_transaction_commit(); - } -} - -static void ot_ibex_wrapper_eg_remapper_create( - OtIbexWrapperEgState *s, unsigned slot, hwaddr dst, hwaddr src, size_t size) -{ - g_assert(slot < PARAM_NUM_REGIONS); - MemoryRegion *mr = &s->remappers[slot]; - g_assert(!memory_region_is_mapped(mr)); - - int priority = (int)(PARAM_NUM_REGIONS - slot); - - MemoryRegion *sys_mem = get_system_memory(); - MemoryRegion *mr_dst; - - char *name = g_strdup_printf(TYPE_OT_IBEX_WRAPPER_EG "-remap[%u]", slot); - - memory_region_transaction_begin(); - /* - * try to map onto the actual device if there's a single one, otherwise - * map on the whole address space. - */ - MemoryRegionSection mrs; - mrs = memory_region_find(sys_mem, dst, (uint64_t)size); - size_t mrs_lsize = int128_getlo(mrs.size); - mr_dst = (mrs.mr && mrs_lsize >= size) ? mrs.mr : sys_mem; - hwaddr offset = dst - mr_dst->addr; - trace_ot_ibex_wrapper_map(s->ot_id, slot, src, dst, size, mr_dst->name, - (uint32_t)offset); - memory_region_init_alias(mr, OBJECT(s), name, mr_dst, offset, - (uint64_t)size); - memory_region_add_subregion_overlap(sys_mem, src, mr, priority); - memory_region_set_enabled(mr, true); - memory_region_transaction_commit(); - g_free(name); - -#ifdef PRINT_MTREE - mtree_info(false, false, false, true); -#endif -} - -static void -ot_ibex_wrapper_eg_fill_entropy(void *opaque, uint32_t bits, bool fips) -{ - OtIbexWrapperEgState *s = opaque; - - trace_ot_ibex_wrapper_fill_entropy(s->ot_id, bits, fips); - - s->regs[R_RND_DATA] = bits; - s->regs[R_RND_STATUS] = R_RND_STATUS_RND_DATA_VALID_MASK; - if (fips) { - s->regs[R_RND_STATUS] |= R_RND_STATUS_RND_DATA_FIPS_MASK; - } - - s->entropy_requested = false; -} - -static void ot_ibex_wrapper_eg_request_entropy(OtIbexWrapperEgState *s) -{ - if (!s->entropy_requested) { - if (unlikely(!s->edn_connected)) { - ot_edn_connect_endpoint(s->edn, s->edn_ep, - &ot_ibex_wrapper_eg_fill_entropy, s); - s->edn_connected = true; - } - s->entropy_requested = true; - trace_ot_ibex_wrapper_request_entropy(s->ot_id, s->entropy_requested); - if (ot_edn_request_entropy(s->edn, s->edn_ep)) { - s->entropy_requested = false; - xtrace_ot_ibex_wrapper_error(s, "failed to request entropy"); - } - } -} - -static void ot_ibex_wrapper_eg_update_remap(OtIbexWrapperEgState *s, bool doi, - unsigned slot) -{ - (void)doi; - g_assert(slot < PARAM_NUM_REGIONS); - /* - * Warning: - * for now, QEMU is unable to distinguish instruction or data access. - * in this implementation, we chose to enable remap whenever either D or I - * remapping is selected, and both D & I configuration match; we disable - * translation when both D & I are remapping are disabled - */ - - bool en_remap_i = s->regs[R_IBUS_ADDR_EN_0 + slot]; - bool en_remap_d = s->regs[R_DBUS_ADDR_EN_0 + slot]; - if (!en_remap_i && !en_remap_d) { - /* disable */ - ot_ibex_wrapper_eg_remapper_destroy(s, slot); - } else { - uint32_t src_match_i = s->regs[R_IBUS_ADDR_MATCHING_0 + slot]; - uint32_t src_match_d = s->regs[R_DBUS_ADDR_MATCHING_0 + slot]; - if (src_match_i != src_match_d) { - /* I and D do not match, do nothing */ - xtrace_ot_ibex_wrapper_info(s, "src remapping do not match"); - return; - } - uint32_t remap_addr_i = s->regs[R_IBUS_REMAP_ADDR_0 + slot]; - uint32_t remap_addr_d = s->regs[R_DBUS_REMAP_ADDR_0 + slot]; - if (remap_addr_i != remap_addr_d) { - /* I and D do not match, do nothing */ - xtrace_ot_ibex_wrapper_info(s, "dst remapping do not match"); - return; - } - /* enable */ - uint32_t map_size = (-src_match_i & (src_match_i + 1u)) << 1u; - uint32_t map_mask = ~(map_size - 1u); - uint32_t src_base = src_match_i & map_mask; - uint32_t dst_base = remap_addr_i & map_mask; - - ot_ibex_wrapper_eg_remapper_destroy(s, slot); - ot_ibex_wrapper_eg_remapper_create(s, slot, (hwaddr)dst_base, - (hwaddr)src_base, (size_t)map_size); - } -} - -static bool ot_ibex_wrapper_eg_log_load_string(OtIbexWrapperEgState *s, - hwaddr addr, char **str) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - /* - * Logging needs to access strings that are stored in guest memory. - * This function adopts a "best effort" strategy: it may fails to retrieve - * a log string argument. - */ - bool res = false; - MemoryRegionSection mrs; - - /* - * Find the region where the string may reside, using a small size as the - * length of the string is not known, and memory_region_find would fail if - * look up is performed behing the end of the containing memory region - */ - mrs = memory_region_find(eng->as->root, addr, 4u); - MemoryRegion *mr = mrs.mr; - if (!mr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); - goto end; - } - - if (!memory_region_is_ram(mr)) { - xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); - goto end; - } - - uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); - if (!src) { - xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); - goto end; - } - src += mrs.offset_within_region; - - size_t size = int128_getlo(mrs.size) - mrs.offset_within_region; - size = MIN(size, 4096u); - - const void *end = memchr((const void *)src, '\0', size); - if (!end) { - xtrace_ot_ibex_wrapper_error(s, "cannot compute strlen"); - goto end; - } - size_t slen = (uintptr_t)end - (uintptr_t)src; - - char *tstr = g_malloc(slen + 1); - memcpy(tstr, (const void *)src, slen); - tstr[slen] = '\0'; - - *str = tstr; - res = true; - -end: - if (mr) { - memory_region_unref(mr); - } - return res; -} - -static bool -ot_ibex_wrapper_eg_log_load_fields(OtIbexWrapperEgState *s, hwaddr addr) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - MemoryRegionSection mrs; - mrs = memory_region_find(eng->as->root, addr, sizeof(eng->fields)); - - MemoryRegion *mr = mrs.mr; - bool res = false; - - if (!mr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find mr section"); - goto end; - } - - if (!memory_region_is_ram(mr)) { - xtrace_ot_ibex_wrapper_error(s, "invalid mr section"); - goto end; - } - - uintptr_t src = (uintptr_t)memory_region_get_ram_ptr(mr); - if (!src) { - xtrace_ot_ibex_wrapper_error(s, "cannot get host mem"); - goto end; - } - src += mrs.offset_within_region; - - memcpy(&eng->fields, (const void *)src, sizeof(eng->fields)); - - if (eng->fields.file_name_ptr) { - if (!ot_ibex_wrapper_eg_log_load_string(s, - (uintptr_t) - eng->fields.file_name_ptr, - &eng->filename)) { - xtrace_ot_ibex_wrapper_error(s, "cannot get filename"); - goto end; - } - } - - if (eng->fields.format_ptr) { - if (!ot_ibex_wrapper_eg_log_load_string(s, - (uintptr_t) - eng->fields.format_ptr, - &eng->format)) { - xtrace_ot_ibex_wrapper_error(s, "cannot get format string"); - goto end; - } - } - - eng->arg_count = 0; - eng->fmtptr = eng->format; - if (eng->fields.nargs) { - eng->args = g_new0(uintptr_t, eng->fields.nargs); - eng->strargs = g_new0(bool, eng->fields.nargs); - } else { - eng->args = NULL; - eng->strargs = NULL; - } - - res = true; - -end: - if (mr) { - memory_region_unref(mr); - } - return res; -} - -static bool -ot_ibex_wrapper_eg_log_load_arg(OtIbexWrapperEgState *s, uint32_t value) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - if (!eng->fmtptr) { - xtrace_ot_ibex_wrapper_error(s, "invalid fmtptr"); - return false; - } - - bool cont; - do { - cont = false; - eng->fmtptr = strchr(eng->fmtptr, '%'); - if (!eng->fmtptr) { - xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); - return false; - } - eng->fmtptr++; - switch (*eng->fmtptr) { - case '%': - eng->fmtptr++; - cont = true; - continue; - case '\0': - xtrace_ot_ibex_wrapper_error(s, "cannot find formatter"); - return false; - case 's': - if (!ot_ibex_wrapper_eg_log_load_string( - s, (hwaddr)value, (char **)&eng->args[eng->arg_count])) { - xtrace_ot_ibex_wrapper_error(s, "cannot load string arg"); - /* use a default string, best effort strategy */ - eng->args[eng->arg_count] = (uintptr_t)&MISSING_LOG_STRING[0]; - } else { - /* string has been dynamically allocated, and should be freed */ - eng->strargs[eng->arg_count] = true; - } - break; - default: - eng->args[eng->arg_count] = (uintptr_t)value; - break; - } - } while (cont); - - eng->arg_count++; - - return true; -} - -static void ot_ibex_wrapper_eg_log_cleanup(OtIbexWrapperEgState *s) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - if (eng->strargs && eng->args) { - for (unsigned ix = 0; ix < eng->fields.nargs; ix++) { - if (eng->strargs[ix]) { - if (eng->args[ix]) { - g_free((void *)eng->args[ix]); - } - } - } - } - g_free(eng->format); - g_free(eng->filename); - g_free(eng->strargs); - g_free(eng->args); - eng->format = NULL; - eng->filename = NULL; - eng->fmtptr = NULL; - eng->strargs = NULL; - eng->args = NULL; -} - -static void ot_ibex_wrapper_eg_log_emit(OtIbexWrapperEgState *s) -{ - OtIbexTestLogEngine *eng = s->log_engine; - - const char *level; - switch (eng->fields.severity) { - case TEST_LOG_SEVERITY_INFO: - level = "INFO"; - break; - case TEST_LOG_SEVERITY_WARN: - level = "WARN "; - break; - case TEST_LOG_SEVERITY_ERROR: - level = "ERROR "; - break; - case TEST_LOG_SEVERITY_FATAL: - level = "FATAL "; - break; - default: - level = "DEBUG "; - break; - } - - /* discard the path of the stored file to reduce log message length */ - const char *basename = eng->filename ? strrchr(eng->filename, '/') : NULL; - basename = basename ? basename + 1u : eng->filename; - - char *logfmt = g_strdup_printf("%s %s:%d %s\n", level, basename, - eng->fields.line, eng->format); - -/* hack ahead: use the uintptr_t array as a va_list */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" - char *logmsg = g_strdup_vprintf(logfmt, (char *)eng->args); -#pragma GCC diagnostic pop - - if (!qemu_chr_fe_backend_connected(&s->chr)) { - qemu_log_mask(LOG_STRACE, "%s", logmsg); - } else { - qemu_chr_fe_write(&s->chr, (const uint8_t *)logmsg, - (int)strlen(logmsg)); - } - - g_free(logmsg); - g_free(logfmt); - - ot_ibex_wrapper_eg_log_cleanup(s); -} - -static void -ot_ibex_wrapper_eg_status_report(OtIbexWrapperEgState *s, uint32_t value) -{ - const char *msg; - switch (value) { - case TEST_STATUS_IN_BOOT_ROM: - msg = "IN_BOOT_ROM"; - break; - case TEST_STATUS_IN_BOOT_ROM_HALT: - msg = "IN_BOOT_ROM_HALT"; - break; - case TEST_STATUS_IN_TEST: - msg = "IN_TEST"; - break; - case TEST_STATUS_IN_WFI: - msg = "IN_BOOT_WFI"; - break; - case TEST_STATUS_PASSED: - msg = "PASSED"; - break; - case TEST_STATUS_FAILED: - msg = "FAILED"; - break; - default: - msg = "UNKNOWN"; - break; - } - - if (!qemu_chr_fe_backend_connected(&s->chr)) { - qemu_log_mask(LOG_STRACE, "%s\n", msg); - } else { - qemu_chr_fe_write(&s->chr, (const uint8_t *)msg, (int)strlen(msg)); - uint8_t eol[] = { '\n' }; - qemu_chr_fe_write(&s->chr, eol, (int)sizeof(eol)); - } -} - -static void -ot_ibex_wrapper_eg_log_handle(OtIbexWrapperEgState *s, uint32_t value) -{ - /* - * Note about logging: - * - * For OT DV logging to work, the "fields" should not be placed in the - * default linker-discarded sections such as ".logs.fields" - * i.e. __attribute__((section(".logs.fields"))) should be removed from - * the "LOG()"" macro. - */ - OtIbexTestLogEngine *eng = s->log_engine; - - switch (eng->state) { - case TEST_LOG_STATE_IDLE: - if (!ot_ibex_wrapper_eg_log_load_fields(s, (hwaddr)value)) { - eng->state = TEST_LOG_STATE_ERROR; - ot_ibex_wrapper_eg_log_cleanup(s); - break; - } - if (eng->fields.nargs) { - eng->state = TEST_LOG_STATE_ARG; - } else { - ot_ibex_wrapper_eg_log_emit(s); - eng->state = TEST_LOG_STATE_IDLE; - } - break; - case TEST_LOG_STATE_ARG: - if (!ot_ibex_wrapper_eg_log_load_arg(s, value)) { - ot_ibex_wrapper_eg_log_cleanup(s); - eng->state = TEST_LOG_STATE_ERROR; - } - if (eng->arg_count == eng->fields.nargs) { - ot_ibex_wrapper_eg_log_emit(s); - eng->state = TEST_LOG_STATE_IDLE; - } - break; - case TEST_LOG_STATE_ERROR: - default: - qemu_log_mask(LOG_GUEST_ERROR, "Can no longer handle DV log, in error"); - break; - } -} - -static void ot_ibex_wrapper_eg_update_exec(OtIbexWrapperEgState *s) -{ - /* - * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and - * power manager CPU enable are all enabled." - */ - bool enable = - ((s->cpu_en_bm & OT_IBEX_CPU_EN_MASK) == OT_IBEX_CPU_EN_MASK) && - !s->esc_rx; - trace_ot_ibex_wrapper_update_exec(s->ot_id ?: "", s->cpu_en_bm, s->esc_rx, - enable); - - if (enable) { - s->cpu->halted = 0; - if (s->cpu->held_in_reset) { - resettable_release_reset(OBJECT(s->cpu), RESET_TYPE_COLD); - } - cpu_resume(s->cpu); - } else { - if (!s->cpu->halted) { - s->cpu->halted = 1; - cpu_exit(s->cpu); - } - } -} - -static void ot_ibex_wrapper_eg_cpu_enable_recv(void *opaque, int n, int level) -{ - OtIbexWrapperEgState *s = opaque; - - g_assert((unsigned)n < OT_IBEX_CPU_EN_COUNT); - - if (level) { - s->cpu_en_bm |= 1u << (unsigned)n; - } else { - s->cpu_en_bm &= ~(1u << (unsigned)n); - } - - /* - * "Fetch is only enabled when local fetch enable, lifecycle CPU enable and - * power manager CPU enable are all enabled." - */ - trace_ot_ibex_wrapper_cpu_enable(s->ot_id ?: "", n ? "PWR" : "LC", - (bool)level); - - ot_ibex_wrapper_eg_update_exec(s); -} - -static void ot_ibex_wrapper_eg_escalate_rx(void *opaque, int n, int level) -{ - OtIbexWrapperEgState *s = opaque; - - g_assert(n == 0); - - trace_ot_ibex_wrapper_escalate_rx(s->ot_id ?: "", (bool)level); - - s->esc_rx = (bool)level; - - ot_ibex_wrapper_eg_update_exec(s); -} - -static uint64_t -ot_ibex_wrapper_eg_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - OtIbexWrapperEgState *s = opaque; - (void)size; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - - switch (reg) { - case R_RND_DATA: - val32 = s->regs[reg]; - if (!(s->regs[R_RND_STATUS] & R_RND_STATUS_RND_DATA_VALID_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Read invalid entropy data 0x%08x\n", __func__, - val32); - } - s->regs[reg] = 0; - s->regs[R_RND_STATUS] = 0; - ot_ibex_wrapper_eg_request_entropy(s); - break; - case R_RND_STATUS: - val32 = s->regs[reg]; - if (!(val32 & R_RND_STATUS_RND_DATA_VALID_MASK)) { - ot_ibex_wrapper_eg_request_entropy(s); - } - break; - case R_DV_SIM_LOG: - val32 = 0; - break; - default: - val32 = s->regs[reg]; - break; - } - - uint32_t pc = ibex_get_current_pc(); - trace_ot_ibex_wrapper_io_read_out(s->ot_id, addr, REG_NAME(reg), val32, pc); - - return (uint64_t)val32; -}; - -static void ot_ibex_wrapper_eg_regs_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned size) -{ - OtIbexWrapperEgState *s = opaque; - (void)size; - uint32_t val32 = (uint32_t)val64; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - trace_ot_ibex_wrapper_io_write(s->ot_id, addr, REG_NAME(reg), val32, pc); - - switch (reg) { - case R_ALERT_TEST: - val32 &= ALERT_TEST_MASK; - s->regs[reg] = val32; - ot_ibex_wrapper_eg_update_alerts(s); - break; - case R_SW_FATAL_ERR: - if ((val32 >> 16u) == 0xC0DEu) { - /* guest should now use DV_SIM_STATUS register */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: QEMU exit on SW_FATAL_ERR is deprecated", - __func__); - /* discard MSB magic */ - val32 &= UINT16_MAX; - /* discard multibool4false mark */ - val32 >>= 4u; - /* std exit code should be in [0..127] range */ - if (val32 > 127u) { - val32 = 127u; - } - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, (int)val32); - } - val32 &= R_SW_FATAL_ERR_VAL_MASK; - s->regs[reg] = ot_multibitbool_w1s_write(s->regs[reg], val32, 4u); - ot_ibex_wrapper_eg_update_alerts(s); - break; - case R_IBUS_REGWEN_0: - case R_IBUS_REGWEN_1: - case R_DBUS_REGWEN_0: - case R_DBUS_REGWEN_1: - val32 &= REGWEN_EN_MASK; - s->regs[reg] &= val32; /* RW0C */ - break; - case R_IBUS_ADDR_EN_0: - case R_IBUS_ADDR_EN_1: - if (s->regs[reg - R_IBUS_ADDR_EN_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_eg_update_remap(s, false, reg - R_IBUS_ADDR_EN_0); - break; - case R_IBUS_ADDR_MATCHING_0: - case R_IBUS_ADDR_MATCHING_1: - if (s->regs[reg - R_IBUS_ADDR_MATCHING_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - break; - case R_IBUS_REMAP_ADDR_0: - case R_IBUS_REMAP_ADDR_1: - if (s->regs[reg - R_IBUS_REMAP_ADDR_0 + R_IBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_eg_update_remap(s, false, reg - R_IBUS_REMAP_ADDR_0); - break; - case R_DBUS_ADDR_EN_0: - case R_DBUS_ADDR_EN_1: - if (s->regs[reg - R_DBUS_ADDR_EN_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_eg_update_remap(s, true, reg - R_DBUS_ADDR_EN_0); - break; - case R_DBUS_ADDR_MATCHING_0: - case R_DBUS_ADDR_MATCHING_1: - if (s->regs[reg - R_DBUS_ADDR_MATCHING_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - break; - case R_DBUS_REMAP_ADDR_0: - case R_DBUS_REMAP_ADDR_1: - if (s->regs[reg - R_DBUS_REMAP_ADDR_0 + R_DBUS_REGWEN_0]) { - s->regs[reg] = val32; - } - ot_ibex_wrapper_eg_update_remap(s, true, reg - R_DBUS_REMAP_ADDR_0); - break; - case R_DV_SIM_STATUS: - ot_ibex_wrapper_eg_status_report(s, val32); - switch (val32) { - case TEST_STATUS_PASSED: - trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM success, exiting", 0); - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, 0); - break; - case TEST_STATUS_FAILED: { - uint32_t info = FIELD_EX32(val32, DV_SIM_STATUS, INFO); - int ret; - if (info == 0) { - /* no extra info */ - ret = 1; - } else { - ret = (int)(info & 0x7fu); - } - trace_ot_ibex_wrapper_exit(s->ot_id, "DV SIM failure, exiting", - ret); - qemu_system_shutdown_request_with_code( - SHUTDOWN_CAUSE_GUEST_SHUTDOWN, ret); - break; - } - default: - s->regs[reg] = val32; - break; - } - break; - case R_DV_SIM_LOG: - ot_ibex_wrapper_eg_log_handle(s, val32); - break; - default: - s->regs[reg] = val32; - break; - } -}; - -static Property ot_ibex_wrapper_eg_properties[] = { - DEFINE_PROP_STRING("ot_id", OtIbexWrapperEgState, ot_id), - DEFINE_PROP_LINK("edn", OtIbexWrapperEgState, edn, TYPE_OT_EDN, - OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtIbexWrapperEgState, edn_ep, UINT8_MAX), - DEFINE_PROP_UINT8("qemu_version", OtIbexWrapperEgState, qemu_version, 0), - DEFINE_PROP_CHR("logdev", OtIbexWrapperEgState, chr), /* optional */ - DEFINE_PROP_END_OF_LIST(), -}; - -static const MemoryRegionOps ot_ibex_wrapper_eg_regs_ops = { - .read = &ot_ibex_wrapper_eg_regs_read, - .write = &ot_ibex_wrapper_eg_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static void ot_ibex_wrapper_eg_reset_enter(Object *obj, ResetType type) -{ - OtIbexWrapperClass *c = OT_IBEX_WRAPPER_EG_GET_CLASS(obj); - OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); - - trace_ot_ibex_wrapper_reset(s->ot_id, "enter"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - g_assert(s->ot_id); - g_assert(s->edn); - g_assert(s->edn_ep != UINT8_MAX); - - if (!s->cpu) { - CPUState *cpu = ot_common_get_local_cpu(DEVICE(s)); - if (!cpu) { - error_setg(&error_fatal, "Could not find the associated vCPU"); - g_assert_not_reached(); - } - s->cpu = cpu; - } - - for (unsigned slot = 0; slot < PARAM_NUM_REGIONS; slot++) { - ot_ibex_wrapper_eg_remapper_destroy(s, slot); - } - - memset(s->regs, 0, REGS_SIZE); - s->regs[R_SW_RECOV_ERR] = 0x9u; - s->regs[R_SW_FATAL_ERR] = 0x9u; - s->regs[R_IBUS_REGWEN_0] = 0x1u; - s->regs[R_IBUS_REGWEN_1] = 0x1u; - s->regs[R_DBUS_REGWEN_0] = 0x1u; - s->regs[R_DBUS_REGWEN_1] = 0x1u; - /* 'QMU_' in LE, _ is the QEMU version stored in the MSB */ - s->regs[R_FPGA_INFO] = 0x00554d51u + (((uint32_t)s->qemu_version) << 24u); - s->entropy_requested = false; - /* LC cycle triggerring is not supported on Earlgrey emulation for now */ - s->cpu_en_bm = 1u << OT_IBEX_LC_CTRL_CPU_EN; - - memset(s->log_engine, 0, sizeof(*s->log_engine)); -} - -static void ot_ibex_wrapper_eg_reset_exit(Object *obj, ResetType type) -{ - OtIbexWrapperClass *c = OT_IBEX_WRAPPER_EG_GET_CLASS(obj); - OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); - - trace_ot_ibex_wrapper_reset(s->ot_id, "exit"); - - if (c->parent_phases.enter) { - c->parent_phases.enter(obj, type); - } - - s->log_engine->as = ot_common_get_local_address_space(DEVICE(s)); - - /* "Upon reset the data will be invalid with a new EDN request pending." */ - ot_ibex_wrapper_eg_request_entropy(s); -} - -static void ot_ibex_wrapper_eg_init(Object *obj) -{ - OtIbexWrapperEgState *s = OT_IBEX_WRAPPER_EG(obj); - - memory_region_init_io(&s->mmio, obj, &ot_ibex_wrapper_eg_regs_ops, s, - TYPE_OT_IBEX_WRAPPER_EG, REGS_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); - } - - qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_eg_cpu_enable_recv, - OT_IBEX_WRAPPER_CPU_EN, OT_IBEX_CPU_EN_COUNT); - qdev_init_gpio_in_named(DEVICE(obj), &ot_ibex_wrapper_eg_escalate_rx, - OT_ALERT_ESCALATE, 1); - - s->regs = g_new0(uint32_t, REGS_COUNT); - s->log_engine = g_new0(OtIbexTestLogEngine, 1u); -} - -static void ot_ibex_wrapper_eg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - (void)data; - - device_class_set_props(dc, ot_ibex_wrapper_eg_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - ResettableClass *rc = RESETTABLE_CLASS(klass); - OtIbexWrapperClass *ic = OT_IBEX_WRAPPER_CLASS(klass); - resettable_class_set_parent_phases(rc, &ot_ibex_wrapper_eg_reset_enter, - NULL, &ot_ibex_wrapper_eg_reset_exit, - &ic->parent_phases); -} - -static const TypeInfo ot_ibex_wrapper_eg_info = { - .name = TYPE_OT_IBEX_WRAPPER_EG, - .parent = TYPE_OT_IBEX_WRAPPER, - .instance_size = sizeof(OtIbexWrapperEgState), - .instance_init = &ot_ibex_wrapper_eg_init, - .class_init = &ot_ibex_wrapper_eg_class_init, - .class_size = sizeof(OtIbexWrapperClass), -}; - -static void ot_ibex_wrapper_eg_register_types(void) -{ - type_register_static(&ot_ibex_wrapper_eg_info); -} - -type_init(ot_ibex_wrapper_eg_register_types); diff --git a/include/hw/opentitan/ot_ibex_wrapper_dj.h b/include/hw/opentitan/ot_ibex_wrapper_dj.h deleted file mode 100644 index 8544e52eb6cb3..0000000000000 --- a/include/hw/opentitan/ot_ibex_wrapper_dj.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * QEMU OpenTitan Darjeeling Ibex Wrapper device - * - * Copyright (c) 2022-2024 Rivos, Inc. - * Copyright (c) 2025 lowRISC contributors. - * - * Author(s): - * Emmanuel Blot - * - * 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. - */ - -#ifndef HW_OPENTITAN_OT_IBEX_WRAPPER_DJ_H -#define HW_OPENTITAN_OT_IBEX_WRAPPER_DJ_H - -#include "qom/object.h" -#include "hw/opentitan/ot_ibex_wrapper.h" - -#define TYPE_OT_IBEX_WRAPPER_DJ "ot-ibex_wrapper-dj" -OBJECT_DECLARE_TYPE(OtIbexWrapperDjState, OtIbexWrapperClass, - OT_IBEX_WRAPPER_DJ) - -#endif /* HW_OPENTITAN_OT_IBEX_WRAPPER_DJ_H */ diff --git a/include/hw/opentitan/ot_ibex_wrapper_eg.h b/include/hw/opentitan/ot_ibex_wrapper_eg.h deleted file mode 100644 index b65179d2badae..0000000000000 --- a/include/hw/opentitan/ot_ibex_wrapper_eg.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * QEMU OpenTitan EarlGrey Ibex Wrapper device - * - * Copyright (c) 2022-2024 Rivos, Inc. - * Copyright (c) 2025 lowRISC contributors. - * - * Author(s): - * Emmanuel Blot - * Loïc Lefort - * - * 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. - */ - -#ifndef HW_OPENTITAN_OT_IBEX_WRAPPER_EG_H -#define HW_OPENTITAN_OT_IBEX_WRAPPER_EG_H - -#include "qom/object.h" -#include "hw/opentitan/ot_ibex_wrapper.h" - -#define TYPE_OT_IBEX_WRAPPER_EG "ot-ibex_wrapper-eg" -OBJECT_DECLARE_TYPE(OtIbexWrapperEgState, OtIbexWrapperClass, - OT_IBEX_WRAPPER_EG) - -#endif /* HW_OPENTITAN_OT_IBEX_WRAPPER_EG_H */ From 727653327064a0f1825dfbe429c0e42bf850fd0d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 31 Mar 2025 17:32:19 +0200 Subject: [PATCH 10/69] [ot] target/riscv: cpu: make get_physical_address a virtual function. This changes enables providing custom address translation engine. OpenTitan does not have an MMU, but support a custom virtual remapper. Signed-off-by: Emmanuel Blot --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 14 ++++++++++ target/riscv/cpu_helper.c | 55 ++++++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1da47db4d0e75..888e515c0fe19 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2906,6 +2906,7 @@ static void riscv_cpu_common_class_init(ObjectClass *c, void *data) #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &riscv_sysemu_ops; cc->get_arch_id = riscv_get_arch_id; + mcc->riscv_get_physical_address = riscv_get_physical_address; #endif cc->gdb_arch_name = riscv_gdb_arch_name; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4ddca4cc668da..95be96e677373 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -542,6 +542,14 @@ struct RISCVCPUClass { DeviceRealize parent_realize; ResettablePhases parent_phases; + + int (*riscv_get_physical_address)(CPURISCVState *env, hwaddr *physical, + int *ret_prot, vaddr addr, + target_ulong *fault_pte_addr, + int access_type, int mmu_idx, + bool first_stage, bool two_stage, + bool is_debug, bool is_probe); + uint32_t misa_mxl_max; /* max mxl for this cpu */ }; @@ -586,6 +594,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, char *riscv_isa_string(RISCVCPU *cpu); int riscv_cpu_max_xlen(RISCVCPUClass *mcc); bool riscv_cpu_option_set(const char *optname); +int riscv_get_physical_address(CPURISCVState *env, hwaddr *physical, + int *ret_prot, vaddr addr, + target_ulong *fault_pte_addr, + int access_type, int mmu_idx, + bool first_stage, bool two_stage, + bool is_debug, bool is_probe); #ifndef CONFIG_USER_ONLY void riscv_cpu_do_interrupt(CPUState *cpu); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 89696467db3c4..f95acd83b32f6 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -864,7 +864,8 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, } /* - * get_physical_address - get the physical address for this virtual address + * riscv_get_physical_address - get the physical address for this virtual + * address * * Do a page table walk to obtain the physical address corresponding to a * virtual address. Returns 0 if the translation was successful @@ -885,12 +886,12 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot, hwaddr addr, * @two_stage: Are we going to perform two stage translation * @is_debug: Is this access from a debugger or the monitor? */ -static int get_physical_address(CPURISCVState *env, hwaddr *physical, - int *ret_prot, vaddr addr, - target_ulong *fault_pte_addr, - int access_type, int mmu_idx, - bool first_stage, bool two_stage, - bool is_debug, bool is_probe) +int riscv_get_physical_address(CPURISCVState *env, hwaddr *physical, + int *ret_prot, vaddr addr, + target_ulong *fault_pte_addr, + int access_type, int mmu_idx, + bool first_stage, bool two_stage, + bool is_debug, bool is_probe) { /* * NOTE: the env->pc value visible here will not be @@ -1035,10 +1036,10 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, hwaddr vbase; /* Do the second stage translation on the base PTE address. */ - int vbase_ret = get_physical_address(env, &vbase, &vbase_prot, - base, NULL, MMU_DATA_LOAD, - MMUIdx_U, false, true, - is_debug, false); + int vbase_ret = riscv_get_physical_address(env, &vbase, &vbase_prot, + base, NULL, MMU_DATA_LOAD, + MMUIdx_U, false, true, + is_debug, false); if (vbase_ret != TRANSLATE_SUCCESS) { if (fault_pte_addr) { @@ -1425,19 +1426,22 @@ void riscv_cpu_store_debug_cause(CPUState *cs, unsigned cause) hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *cc = RISCV_CPU_CLASS(cs->cc); CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; int mmu_idx = riscv_env_mmu_index(&cpu->env, false); - if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx, - true, env->virt_enabled, true, false)) { + if (cc->riscv_get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, + mmu_idx, true, env->virt_enabled, true, + false)) { return -1; } if (env->virt_enabled) { - if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL, - 0, MMUIdx_U, false, true, true, false)) { + if (cc->riscv_get_physical_address(env, &phys_addr, &prot, phys_addr, + NULL, 0, MMUIdx_U, false, true, true, + false)) { return -1; } } @@ -1528,6 +1532,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, bool probe, uintptr_t retaddr) { RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *cc = RISCV_CPU_CLASS(cs->cc); CPURISCVState *env = &cpu->env; vaddr im_address; hwaddr pa = 0; @@ -1549,9 +1554,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, pmu_tlb_fill_incr_ctr(cpu, access_type); if (two_stage_lookup) { /* Two stage lookup */ - ret = get_physical_address(env, &pa, &prot, address, - &env->guest_phys_fault_addr, access_type, - mmu_idx, true, true, false, probe); + ret = cc->riscv_get_physical_address(env, &pa, &prot, address, + &env->guest_phys_fault_addr, + access_type, mmu_idx, true, true, + false, probe); /* * A G-stage exception may be triggered during two state lookup. @@ -1572,9 +1578,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* Second stage lookup */ im_address = pa; - ret = get_physical_address(env, &pa, &prot2, im_address, NULL, - access_type, MMUIdx_U, false, true, - false, probe); + ret = cc->riscv_get_physical_address(env, &pa, &prot2, im_address, + NULL, access_type, MMUIdx_U, + false, true, + false, probe); qemu_log_mask(CPU_LOG_MMU, "%s 2nd-stage address=%" VADDR_PRIx @@ -1610,9 +1617,9 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } else { /* Single stage lookup */ - ret = get_physical_address(env, &pa, &prot, address, NULL, - access_type, mmu_idx, true, false, false, - probe); + ret = cc->riscv_get_physical_address(env, &pa, &prot, address, NULL, + access_type, mmu_idx, true, false, + false, probe); qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " From 24097e760d63992e67771b9ff5a0f40913cb5a0f Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 3 Apr 2025 19:06:30 +0200 Subject: [PATCH 11/69] [ot] target/riscv: cpu: add an option to use virtual address with PMP On OpenTitan devices, PMP is validating translated addresses, not physical ones. Signed-off-by: Emmanuel Blot --- target/riscv/cpu.h | 4 ++++ target/riscv/cpu_helper.c | 16 ++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 95be96e677373..d9aac33569616 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -377,6 +377,10 @@ struct CPUArchState { * translation active. */ bool two_stage_lookup; + + /* Whether to use virtual address for PMP (default: physical address) */ + bool vaddr_pmp; + /* * Signals whether the current exception occurred while doing two-stage * address translation for the VS-stage page table walk. diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f95acd83b32f6..48e43917a57b1 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1592,14 +1592,16 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, prot &= prot2; if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, pa, + hwaddr pmp_addr = env->vaddr_pmp ? address : pa; + + ret = get_physical_address_pmp(env, &prot_pmp, pmp_addr, size, access_type, mode); - tlb_size = pmp_get_tlb_size(env, pa); + tlb_size = pmp_get_tlb_size(env, pmp_addr); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" " %d tlb_size %" HWADDR_PRIu "\n", - __func__, pa, ret, prot_pmp, tlb_size); + __func__, pmp_addr, ret, prot_pmp, tlb_size); prot &= prot_pmp; } else { @@ -1627,14 +1629,16 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, __func__, address, ret, pa, prot); if (ret == TRANSLATE_SUCCESS) { - ret = get_physical_address_pmp(env, &prot_pmp, pa, + hwaddr pmp_addr = env->vaddr_pmp ? address : pa; + + ret = get_physical_address_pmp(env, &prot_pmp, pmp_addr, size, access_type, mode); - tlb_size = pmp_get_tlb_size(env, pa); + tlb_size = pmp_get_tlb_size(env, pmp_addr); qemu_log_mask(CPU_LOG_MMU, "%s PMP address=" HWADDR_FMT_plx " ret %d prot" " %d tlb_size %" HWADDR_PRIu "\n", - __func__, pa, ret, prot_pmp, tlb_size); + __func__, pmp_addr, ret, prot_pmp, tlb_size); prot &= prot_pmp; } From 0cdfa12a2b1144b2be27700512810362963a3314 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 9 Apr 2025 18:37:21 +0200 Subject: [PATCH 12/69] [ot] hw/opentitan: ot_vmapper: create new device to handle address translation Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 4 + hw/opentitan/meson.build | 2 + hw/opentitan/ot_vmapper.c | 841 ++++++++++++++++++++++++++++++ hw/opentitan/trace-events | 7 + include/hw/opentitan/ot_vmapper.h | 60 +++ 5 files changed, 914 insertions(+) create mode 100644 hw/opentitan/ot_vmapper.c create mode 100644 include/hw/opentitan/ot_vmapper.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index ca57a0def2bd6..25002fc91b86e 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -63,6 +63,7 @@ config OT_I2C_DJ bool config OT_IBEX_WRAPPER + select OT_VMAPPER bool config OT_KMAC @@ -159,4 +160,7 @@ config OT_UART config OT_UNIMP bool +config OT_VMAPPER + bool + source otbn/Kconfig diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index dcbfa4c42f1ef..6ce0036b74ba3 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -53,4 +53,6 @@ system_ss.add(when: 'CONFIG_OT_TIMER', if_true: files('ot_timer.c')) system_ss.add(when: 'CONFIG_OT_UART', if_true: files('ot_uart.c')) system_ss.add(when: 'CONFIG_OT_UNIMP', if_true: files('ot_unimp.c')) +riscv_ss.add(when: 'CONFIG_OT_VMAPPER', if_true: files('ot_vmapper.c')) + subdir('otbn') diff --git a/hw/opentitan/ot_vmapper.c b/hw/opentitan/ot_vmapper.c new file mode 100644 index 0000000000000..ff89bdd611ba0 --- /dev/null +++ b/hw/opentitan/ot_vmapper.c @@ -0,0 +1,841 @@ +/* + * QEMU OpenTitan virtual mapper device + * + * Copyright (c) 2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * + * 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 "qemu/log.h" +#include "qemu/typedefs.h" +#include "exec/exec-all.h" +#include "exec/page-protection.h" +#include "hw/boards.h" +#include "hw/opentitan/ot_vmapper.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/ibex_common.h" +#include "trace.h" + +/* define to log range definitions */ +#undef SHOW_RANGE_LIST +#undef SHOW_RANGE_TREE +/* define to log address mapping (highly verbose) */ +#undef SHOW_ADDRESS_MAPPING + +/* Translatable region */ +typedef struct { + uint32_t start; /* first address of the region to be translated */ + uint32_t end; /* last address of the region to be translated */ + uint32_t dest; /* first address of the destination */ + uint8_t prio; /* priority of the slot, 0 = highest priority */ + bool active; /* whether this range is active or should be ignored */ + bool execute; /* whether this range is executable (for insn range)*/ +} OtRegionRange; + +struct OtVMapperState { + DeviceState parent_obj; + + OtRegionRange *dranges; /* Configured data ranges */ + OtRegionRange *iranges; /* Configured instruction ranges */ + + OtRegionRange *lranges[2u]; /* Last matched data and instruction ranges */ + + CPUState *cpu; + GTree *dtree; /* Active data ranges */ + GTree *itree; /* Active instruction ranges */ + bool insert_mode; /* Whether trees are used in insertion or matching mode */ + bool show; /* debug flag to enable list and tree dump */ + bool silent_align; /* whether to silence alignment warnings */ + + char *ot_id; + uint8_t cpu_idx; /* cpu index, i.e. which vCPU is translated */ + uint8_t trans_count; /* count of translatable regions */ +}; + +#define VMAP_RANGE(_glist_) ((OtRegionRange *)((_glist_)->data)) +#define VMAP_PRIOR(_ra_, _rb_) \ + ((VMAP_RANGE(_ra_)->pos < VMAP_RANGE(_rb_)->pos) ? VMAP_RANGE(_ra_) : \ + VMAP_RANGE(_rb_)) +#define VMAP_RANGE_TO_TREE_KEY(_s_, _e_) \ + ((gpointer)(((uintptr_t)(_s_)) | ((uintptr_t)(_e_) << 32u))) +#define VMAP_REGION_TO_TREE_KEY(_r_) \ + VMAP_RANGE_TO_TREE_KEY((_r_)->start, (_r_)->end) +#define VMAP_TREE_KEY_TO_RANGE_START(_g_) \ + ((uint32_t)(((uintptr_t)(_g_)) & UINT32_MAX)) +#define VMAP_TREE_KEY_TO_RANGE_END(_g_) ((uint32_t)(((uintptr_t)(_g_)) >> 32u)) + +/* 'g_tree_remove_all' is deprecated: Not available before 2.70 */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +/* + * order ranges for g_list_sort + * 1. by start address first, + * 2. then by end address, + * 3. then by priority (from higest to lowest) + */ +static gint ot_vmapper_compare(gconstpointer a, gconstpointer b) +{ + const OtRegionRange *ra = (const OtRegionRange *)a; + const OtRegionRange *rb = (const OtRegionRange *)b; + + return (gint)((ra->start == rb->start) ? + ((ra->end == rb->end) ? (ra->prio - rb->prio) : + (ra->end - rb->end)) : + (ra->start - rb->start)); +} + +/* + * order range by increasing addresses, for g_tree + * - in insert mode, return a strict range comparison + * - in match mode, return equality for any A address in B range + */ +static gint ot_vmapper_compare_address(gconstpointer a, gconstpointer b, + gpointer user_data) +{ + gint ret; + bool insert_mode = *(const bool *)user_data; + + uint32_t as = VMAP_TREE_KEY_TO_RANGE_START(a); + uint32_t bs = VMAP_TREE_KEY_TO_RANGE_START(b); + + if (!insert_mode) { + uint32_t ae = VMAP_TREE_KEY_TO_RANGE_END(a); + uint32_t be = VMAP_TREE_KEY_TO_RANGE_END(b); + /* in match mode, any A start address within B range matches */ + g_assert(ae == 0); + if (as < bs) { + ret = -1; + } else if (as > be) { + ret = 1; + } else { + ret = 0; + } + } else { + /* in insertion mode, compare the start address */ + ret = as == bs ? 0 : ((as < bs) ? -1 : 1); + } + + return ret; +} + +#ifdef SHOW_RANGE_LIST +#define VMAP_SHOW_RANGE_LIST(_s_, _l_, _m_) \ + ot_vmapper_show_range_list(_s_, _l_, _m_) + +static void ot_vmapper_show_range_list(const OtVMapperState *s, + const GList *rglist, const char *msg) +{ + if (!s->show) { + return; + } + qemu_log("%s: %s %s\n", __func__, s->ot_id, msg); + const GList *current = rglist; + unsigned pos = 0; + while (current) { + const OtRegionRange *rg = VMAP_RANGE(current); + qemu_log(" * %2u: [%2u] 0x%08x..0x%08x -> 0x%08x X:%u\n", pos, rg->prio, + rg->start, rg->end, rg->dest, rg->execute); + current = current->next; + pos++; + } + qemu_log("%s: %s %u items\n\n", __func__, s->ot_id, pos); +} +#else +#define VMAP_SHOW_RANGE_LIST(_s_, _l_, _m_) +#endif + +static GTree *ot_vmapper_create_tree(OtVMapperState *s) +{ + return g_tree_new_full(&ot_vmapper_compare_address, &s->insert_mode, NULL, + &g_free); +} + +/* + * there should be no need for such workarounds, however some prehistoric + * OSes (CentOS 7, ...) rely on outdated Glib versions that lack useful + * functions. Some other old OSes (Ubuntu 20.x) also lack new functions... + */ +#if (GLIB_MAJOR_VERSION == 2) + +/* 'g_tree_remove_all' is deprecated: Not available before 2.70 */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#if (GLIB_MINOR_VERSION >= 68) && (GLIB_MINOR_VERSION < 70) + +/* + * g_tree_remove_all is only available from 2.70 + * re-implement it + */ +static void g_tree_remove_all(GTree *tree) +{ + GTreeNode *node; + GTreeNode *next; + g_return_if_fail(tree != NULL); + node = g_tree_node_first(tree); + while (node) { + next = g_tree_node_next(node); + if (tree->key_destroy_func) { + tree->key_destroy_func(node->key); + } + if (tree->value_destroy_func) { + tree->value_destroy_func(node->value); + } + g_slice_free(GTreeNode, node); + node = next; + } + tree->root = NULL; +} +#endif /* >= 2.68 < 2.70 */ + +static void ot_vmapper_flush_tree(OtVMapperState *s, GTree *tree) +{ +#if (GLIB_MINOR_VERSION < 68) +#ifdef SHOW_RANGE_TREE +/* non essential feature, please upgrade Glib */ +#error SHOW_RANGE_TREE not supported with current Glib version +#endif /* SHOW_RANGE_TREE */ + /* + * GTreeNode is only available from 2.68 + * destroy the whole tree and build a new one + * this is ulgy, but should not be used except on outdated hosts + */ + if (tree == s->itree) { + g_tree_destroy(s->itree); + s->itree = ot_vmapper_create_tree(s); + } else if (tree == s->dtree) { + g_tree_destroy(s->dtree); + s->dtree = ot_vmapper_create_tree(s); + } else { + g_assert_not_reached(); + } +#else /* >= 2.68 */ + (void)s; + g_tree_remove_all(tree); +#endif /* >= 2.68 */ +} + +/* "-Wdeprecated-declarations" */ +#pragma GCC diagnostic pop + +#endif /* 2.x */ + +#ifdef SHOW_RANGE_TREE +#define VMAP_SHOW_RANGE_TREE(_s_, _i_) ot_vmapper_show_range_tree(_s_, _i_) + +static gboolean ot_vmapper_show_node(GTreeNode *node, gpointer data) +{ + const OtRegionRange *rg = (const OtRegionRange *)g_tree_node_value(node); + unsigned *count = (unsigned *)data; + qemu_log(" * %2u: 0x%08x..0x%08x -> 0x%08x X:%u\n", *count, rg->start, + rg->end, rg->dest, rg->execute); + *count += 1; + return FALSE; +} + +static void ot_vmapper_show_range_tree(const OtVMapperState *s, bool insn) +{ + if (!s->show) { + return; + } + qemu_log("%s: %s %s\n", __func__, s->ot_id, insn ? "insn" : "data"); + unsigned count = 0; + g_tree_foreach_node(insn ? s->itree : s->dtree, &ot_vmapper_show_node, + &count); + qemu_log("%s: %s %u items\n\n", __func__, s->ot_id, count); +} +#else +#define VMAP_SHOW_RANGE_TREE(_s_, _i_) +#endif + +/* class singleton */ +static OtVMapperClass *ot_vmapper_class; + +static int ot_vmapper_get_phy_addr(OtVMapperState *s, hwaddr *physical, + int *ret_prot, vaddr addr, int access_type) +{ + bool insn = access_type == MMU_INST_FETCH; + + OtRegionRange *range = s->lranges[insn]; + +#ifdef SHOW_ADDRESS_MAPPING + bool trace; +#endif + if (!range || (((uint32_t)addr) < range->start) || + (((uint32_t)addr) > range->end)) { + GTree *tree = insn ? s->itree : s->dtree; + range = g_tree_lookup(tree, VMAP_RANGE_TO_TREE_KEY(addr, 0)); + s->lranges[insn] = range; +#ifdef SHOW_ADDRESS_MAPPING + trace = true; +#endif + } else { +#ifdef SHOW_ADDRESS_MAPPING + trace = false; +#endif + } + + if G_UNLIKELY (!range || (addr > range->end)) { + return TRANSLATE_FAIL; + } + + hwaddr offset = addr - (hwaddr)range->start; + *physical = range->dest + offset; + *ret_prot = PAGE_READ | PAGE_WRITE | (range->execute ? PAGE_EXEC : 0); + +#ifdef SHOW_ADDRESS_MAPPING + if (trace) { + trace_ot_vmapper_new_phy_addr(s->ot_id, insn, (uint32_t)addr, + *physical); + } +#endif + + return TRANSLATE_SUCCESS; +} + +/* NOLINTBEGIN(readability-non-const-parameter) */ +static int ot_riscv_get_physical_address( + CPURISCVState *env, hwaddr *physical, int *ret_prot, vaddr addr, + target_ulong *fault_pte_addr, int access_type, int mmu_idx, + bool first_stage, bool two_stage, bool is_debug, bool is_probe) +/* NOLINTEND(readability-non-const-parameter) */ +{ + (void)fault_pte_addr; + (void)mmu_idx; + (void)first_stage; + (void)two_stage; + (void)is_debug; + (void)is_probe; + + CPUState *cpu = env_cpu(env); + g_assert(cpu->cpu_index < ot_vmapper_class->num_instances); + + OtVMapperState *s = ot_vmapper_class->instances[cpu->cpu_index]; + + return ot_vmapper_get_phy_addr(s, physical, ret_prot, addr, access_type); +} + +static GList *ot_vmapper_range_discretize(OtVMapperState *s, GList *rglist) +{ + (void)s; + + /* + * replace overlapping ranges with additional, non-overlapping ones, + * the input list should be sorted by address, and only contain active + * regions. + */ + GList *current = rglist; + while (current) { + GList *next = current->next; + + if (!next) { + break; + } + + /* + * Note about region priority: + * "If a transaction matches multiple regions, the lowest indexed region + * has priority." + * Here, region with the lower position has higher priority. + */ + + /* are two consecutive ranges overlapping? */ + if (VMAP_RANGE(next)->start < VMAP_RANGE(current)->end) { + /* + * next item starts before current ends: do split, two cases: + * +-----------------+ +---------------+ + * | current | | current | + * +-----+-------+---+ +-----+---------+----+ + * | next | (a) | next | (b) + * +-------+ +--------------+ + */ + if (VMAP_RANGE(next)->end <= VMAP_RANGE(current)->end) { + /* + * case (a): next is fully overlapped by current, two cases: + * + * +-----------------+ +-----------------+ + * | current | | cur | ... | rh| + * +-----+-------+---+ +-----+-------+---+ + * | ... | (a1) | next | (a2) + * +-------+ +-------+ + */ + if (VMAP_RANGE(current)->prio < VMAP_RANGE(next)->prio) { + /* + * case (a1): next is fully masked by current, as next as + * a less priority than current, skip next entirely + */ + current = g_list_remove_link(current, next); + g_list_free_1(next); + } else { + /* + * case (a2): central part of current is masked by next + * 1. create a new region for the right part of current + * 2. insert the new region after next + * 3. update current's end position + */ + OtRegionRange *right = g_new0(OtRegionRange, 1); + right->start = VMAP_RANGE(next)->end + 1u; + right->end = VMAP_RANGE(current)->end; + right->dest = + right->dest + right->start - VMAP_RANGE(current)->start; + right->prio = VMAP_RANGE(current)->prio; + right->execute = VMAP_RANGE(current)->execute; + right->active = true; + /* assignation is useless, but GCC won't let it go */ + next = g_list_insert(next, right, 1u); + /* trim current on next */ + VMAP_RANGE(current)->end = VMAP_RANGE(next)->start - 1u; + } + } else { + /* case (b): next extends after current, two cases: + * + * +-------------+ +-------------+ + * | current | | cur | ... | + * +-----+-------+----+ +-----+-------+---+ + * | ... |next| (b1) | next | (b2) + * +-------+----+ +-----------+ + */ + if (VMAP_RANGE(current)->prio < VMAP_RANGE(next)->prio) { + /* case (b1): next is partially masked by current, trim it*/ + VMAP_RANGE(next)->dest += + VMAP_RANGE(current)->end - VMAP_RANGE(next)->start; + VMAP_RANGE(next)->start = VMAP_RANGE(current)->end + 1u; + } else { + /* case (b2): current is partially masked by next, trim it*/ + VMAP_RANGE(current)->end = VMAP_RANGE(next)->start - 1u; + } + } + } + + /* + * be sure to take the immediate next element of current, which may be + * the left item if one has been inserted + */ + current = current->next; + } + + return rglist; +} + +static GList * +ot_vmapper_fill_empty_gaps(OtVMapperState *s, GList *rglist, bool insn) +{ + /* + * The full address range needs to be defined. Fill all gaps between defined + * ranges with no access items. The input list should be sorted by address, + * and only contain active regions. + */ + GList *current = rglist; + uint32_t addr = 0; + + while (current) { + if (addr < VMAP_RANGE(current)->start) { + OtRegionRange *gap = g_new0(OtRegionRange, 1); + gap->start = addr; + gap->end = VMAP_RANGE(current)->start - 1u; + gap->dest = gap->start; + /* lowest priority */ + gap->prio = s->trans_count; + gap->execute = insn; + gap->active = true; + rglist = g_list_insert_before(rglist, current, gap); + } + addr = VMAP_RANGE(current)->end + 1u; + current = current->next; + } + + /* + * if the list is empty or if the last item of the list does not end on the + * last valid address, append an all access item. + */ + uint32_t end; + if (rglist) { + current = g_list_last(rglist); + end = VMAP_RANGE(current)->end; + } else { + current = NULL; + end = 0; + } + + if (end != UINT32_MAX) { + OtRegionRange *last = g_new0(OtRegionRange, 1); + last->start = end ? end + 1u : 0; + last->end = UINT32_MAX; + last->dest = last->start; /* 1:1 mapping */ + last->prio = s->trans_count; + last->execute = insn; + last->active = true; + if (current) { + /* assignation is useless, but GCC won't let it go ... */ + current = g_list_insert(current, last, 1u); + /* ... while clang-tidy found this stupid */ + (void)current; + } else { + rglist = g_list_insert(NULL, last, 0u); + } + } + + /* from here, its is guaranteed that rglist contains at least one item */ + + return rglist; +} + +static GList *ot_vmapper_fuse(OtVMapperState *s, GList *rglist) +{ + /* + * simplify the list whenever possible; if contiguous items share the same + * access permissions, fuse them to reduce the number of memory regions to + * create; the input list should be sorted by address, and only contain + * active regions. + */ + GList *current = rglist; + (void)s; + + while (current && current->next) { + GList *next = current->next; + /* should always be valid, use this opportunity to validate the list */ + g_assert(VMAP_RANGE(current)->end + 1u == VMAP_RANGE(next)->start); + if ((VMAP_RANGE(current)->execute == VMAP_RANGE(next)->execute) && + ((VMAP_RANGE(next)->start - VMAP_RANGE(next)->dest) == + (VMAP_RANGE(current)->start - VMAP_RANGE(current)->dest))) { + VMAP_RANGE(current)->end = VMAP_RANGE(next)->end; + + rglist = g_list_remove_link(rglist, next); + g_free(next->data); // OtRegionRange + g_list_free(next); + + /* + * do not change the current item if a fusion occured, since the + * current new adjacent 'next' may also be a fusion candidate. + */ + } else { + current = current->next; + } + } + + return rglist; +} + +static void ot_vmapper_rebuild_tree(OtVMapperState *s, GTree *tree, + GList *rglist) +{ + const GList *current = rglist; + + /* empty the tree AND free any contained OtRegionRange items */ + ot_vmapper_flush_tree(s, tree); + + /* configure the tree comparison for insertion */ + s->insert_mode = true; + + while (current) { + OtRegionRange *range = VMAP_RANGE(current); + g_assert(range->active); + + /* transfer ownership of the OtRegionRange item from list to the tree */ + g_tree_insert(tree, VMAP_REGION_TO_TREE_KEY(range), range); + + current = current->next; + } + + /* configure the tree comparison for matching range */ + s->insert_mode = false; + + /* + * free the list but not its OtRegionRange items which are now owned by the + * tree + */ + g_list_free(rglist); +} + +static void ot_vmapper_update(OtVMapperState *s, bool insn) +{ + GList *rglist = NULL; + GTree *rgtree = insn ? s->itree : s->dtree; + OtRegionRange *ranges = insn ? s->iranges : s->dranges; + + /* create sortable range items and add them to a new list */ + for (unsigned ix = 0; ix < s->trans_count; ix++) { + const OtRegionRange *crg = &ranges[ix]; + + /* ignore disabled range entries */ + if (!crg->active) { + continue; + } + + /* duplicate range since it may be modified */ + OtRegionRange *range = g_new0(OtRegionRange, 1); + memcpy(range, crg, sizeof(OtRegionRange)); + + rglist = g_list_prepend(rglist, range); + } + + if (rglist) { + VMAP_SHOW_RANGE_LIST(s, rglist, "initial"); + + /* sort the list, in start address order (end address if start are + * equal) */ + rglist = g_list_sort(rglist, &ot_vmapper_compare); + + VMAP_SHOW_RANGE_LIST(s, rglist, "sorted"); + + rglist = ot_vmapper_range_discretize(s, rglist); + + VMAP_SHOW_RANGE_LIST(s, rglist, "discretized"); + + /* now rglist contains a list of unique range permissions */ + + /* fill all empty gaps with denied ranges */ + rglist = ot_vmapper_fill_empty_gaps(s, rglist, insn); + + VMAP_SHOW_RANGE_LIST(s, rglist, "extended"); + + /* combine adjacent items sharing the same properties */ + rglist = ot_vmapper_fuse(s, rglist); + + VMAP_SHOW_RANGE_LIST(s, rglist, "fused"); + } else { + /* create a one item list with no access for the whole address range */ + rglist = ot_vmapper_fill_empty_gaps(s, rglist, insn); + + VMAP_SHOW_RANGE_LIST(s, rglist, "default"); + } + + /* rglist is freed on return */ + ot_vmapper_rebuild_tree(s, rgtree, rglist); + + s->lranges[insn] = NULL; + + VMAP_SHOW_RANGE_TREE(s, true); + VMAP_SHOW_RANGE_TREE(s, false); +} + +static void ot_vmapper_translate(OtVMapperState *s, bool insn, unsigned slot, + hwaddr src, hwaddr dst, size_t size) +{ + g_assert(slot < s->trans_count); + + /* + * QEMU virtual address implementation is built around the size of a small + * MMU page, usually 4KiB. Any attempt to use non-aligned address or size + * makes QEMU fall back on a 1-byte page size, which leads to terrible + * performance issues (this usually translates into up to 3 lookups per + * instruction fetch or data access). This mode is not even supported here + * as it makes QEMU unusable. + * + * Virtual remapper should not be used when non-aligned remapping are + * expected. See ot_ibex_wrapper for more details. + */ + struct { + const char *name; + uint32_t value; + } checks[] = { + { "source", (uint32_t)src }, + { "dest", (uint32_t)dst }, + { "size", (uint32_t)size }, + }; + for (unsigned cix = 0; cix < ARRAY_SIZE(checks); cix++) { + if (!s->silent_align && (checks[cix].value & (TARGET_PAGE_SIZE - 1u))) { + error_report("%s: %s %s %s (0x%08x) is not aligned on 4KiB, " + "translation may fail", + __func__, s->ot_id, insn ? "insn" : "data", + checks[cix].name, checks[cix].value); + s->silent_align = true; + } + } + + OtRegionRange *ranges = insn ? s->iranges : s->dranges; + OtRegionRange *range = &ranges[slot]; + + bool activated = range->active; + range->start = src; + range->end = size ? src + size - 1u : src; + range->dest = dst; + range->execute = insn; + range->active = size != 0; + + if (activated != range->active) { + if (size) { + trace_ot_vmapper_translate_enable(s->ot_id, insn, slot, src, dst, + size); + } else { + trace_ot_vmapper_translate_disable(s->ot_id, insn, slot); + } + s->show = true; + } + + ot_vmapper_update(s, insn); + s->show = false; + + tlb_flush_all_cpus_synced(s->cpu); +} + +static CPUState *ot_vmapper_retrieve_cpu(OtVMapperState *s) +{ + DeviceState *cs = + ibex_get_child_device(DEVICE(OBJECT(s)->parent), TYPE_RISCV_CPU, 0); + return cs ? CPU(cs) : NULL; +} + +static void ot_vmapper_override_vcpu_config(OtVMapperState *s) +{ + g_assert(s->cpu); + + RISCVCPUClass *cc = RISCV_CPU_GET_CLASS(s->cpu); + RISCVCPU *cpu = RISCV_CPU(s->cpu); + + trace_ot_vmapper_override_vcpu_config(s->ot_id); + + /* reroute riscv_get_physical_address to custom implementation */ + cc->riscv_get_physical_address = &ot_riscv_get_physical_address; + /* force PMP to work with remapped address */ + cpu->env.vaddr_pmp = true; +} + +static Property ot_vmapper_properties[] = { + DEFINE_PROP_STRING("ot_id", OtVMapperState, ot_id), + DEFINE_PROP_UINT8("cpu_index", OtVMapperState, cpu_idx, UINT8_MAX), + DEFINE_PROP_UINT8("trans_count", OtVMapperState, trans_count, UINT8_MAX), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ot_vmapper_reset_enter(Object *obj, ResetType type) +{ + OtVMapperClass *c = OT_VMAPPER_GET_CLASS(obj); + OtVMapperState *s = OT_VMAPPER(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + memset(s->dranges, 0, sizeof(OtRegionRange) * s->trans_count); + memset(s->iranges, 0, sizeof(OtRegionRange) * s->trans_count); + memset(s->lranges, 0, sizeof(s->lranges)); + + for (unsigned ix = 0; ix < s->trans_count; ix++) { + s->dranges[ix].prio = ix; + s->iranges[ix].prio = ix; + } + + s->insert_mode = false; + s->show = false; + s->silent_align = false; + + ot_vmapper_flush_tree(s, s->dtree); + ot_vmapper_flush_tree(s, s->itree); + + s->cpu = ot_vmapper_retrieve_cpu(s); +} + +static void ot_vmapper_reset_exit(Object *obj, ResetType type) +{ + OtVMapperClass *c = OT_VMAPPER_GET_CLASS(obj); + OtVMapperState *s = OT_VMAPPER(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + ot_vmapper_override_vcpu_config(s); + + ot_vmapper_update(s, false); + ot_vmapper_update(s, true); +} + +static void ot_vmapper_realize(DeviceState *dev, Error **errp) +{ + OtVMapperState *s = OT_VMAPPER(dev); + OtVMapperClass *c = OT_VMAPPER_GET_CLASS(dev); + (void)errp; + + if (s->cpu_idx == UINT8_MAX) { + CPUState *cs = ot_vmapper_retrieve_cpu(s); + if (cs) { + s->cpu_idx = cs->cpu_index; + } + } + + /* + * if the CPU cannot be retrieved from our parent, it needs to be configured + * from a property + */ + g_assert(s->cpu_idx != UINT8_MAX); + + c->instances[s->cpu_idx] = s; + + s->dranges = g_new0(OtRegionRange, s->trans_count); + s->iranges = g_new0(OtRegionRange, s->trans_count); +} + +static void ot_vmapper_init(Object *obj) +{ + OtVMapperState *s = OT_VMAPPER(obj); + + if (!ot_vmapper_class->instances) { + OtVMapperClass *vc = OT_VMAPPER_GET_CLASS(obj); + vc->num_instances = MACHINE(qdev_get_machine())->smp.cpus; + vc->instances = g_new0(OtVMapperState *, vc->num_instances); + } + + /* + * Binary trees where key is a translation region start address and value + * is an active translation region (OtRegionRange). + */ + s->dtree = ot_vmapper_create_tree(s); + s->itree = ot_vmapper_create_tree(s); +} + +static void ot_vmapper_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &ot_vmapper_realize; + device_class_set_props(dc, ot_vmapper_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtVMapperClass *vc = OT_VMAPPER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_vmapper_reset_enter, NULL, + &ot_vmapper_reset_exit, + &vc->parent_phases); + + vc->translate = &ot_vmapper_translate; + + ot_vmapper_class = vc; +} + +static const TypeInfo ot_vmapper_info = { + .name = TYPE_OT_VMAPPER, + .parent = TYPE_DEVICE, + .instance_size = sizeof(OtVMapperState), + .instance_init = &ot_vmapper_init, + .class_size = sizeof(OtVMapperClass), + .class_init = &ot_vmapper_class_init, +}; + +static void ot_vmapper_register_types(void) +{ + type_register_static(&ot_vmapper_info); +} + +type_init(ot_vmapper_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 029923a15528d..938bbad573b4c 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -542,3 +542,10 @@ ot_uart_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: ot_unimp_irq(const char *id, unsigned ix, bool lvl) "%s: #%u = %u" ot_unimp_alert(const char *id, unsigned ix) "%s: #%u" + +# ot_vmapper.c + +ot_vmapper_new_phy_addr(char *id, bool insn, uint32_t vaddr, uint32_t paddr) "%s: i:%u v:0x%08x p:0x%08x" +ot_vmapper_override_vcpu_config(char *id) "%s: redirect address resolution and force PMP to use virtual addresses" +ot_vmapper_translate_enable(char *id, bool insn, unsigned slot, uint32_t src, uint32_t dst, unsigned size) "%s: i:%u s:%u src:0x%08x dst:0x%08x size:0x%x" +ot_vmapper_translate_disable(char *id, bool insn, unsigned slot) "%s: i:%u s:%u" diff --git a/include/hw/opentitan/ot_vmapper.h b/include/hw/opentitan/ot_vmapper.h new file mode 100644 index 0000000000000..d9bb2347e74a7 --- /dev/null +++ b/include/hw/opentitan/ot_vmapper.h @@ -0,0 +1,60 @@ +/* + * QEMU OpenTitan virtual mapper device + * + * Copyright (c) 2025 Rivos, Inc. + * + * Author(s): + * Emmanuel Blot + * + * 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. + */ + +#ifndef HW_OPENTITAN_OT_VMAPPER_H +#define HW_OPENTITAN_OT_VMAPPER_H + +#include "qom/object.h" +#include "exec/hwaddr.h" + +#define TYPE_OT_VMAPPER "ot-vmapper" +OBJECT_DECLARE_TYPE(OtVMapperState, OtVMapperClass, OT_VMAPPER) + +/* + * Configure the address translation for an address range. + * + * @insn whether the translation is for an instruction. + * @slot the execution slot to use for the translation. + * @src the first address of the range to match. + * @dst the first physical address of the range to translate to. + * @size the size (extentof the range to match. If 0, disable the translation + * for the selected slot. + */ +typedef void (*OtVMapperTranslate)(OtVMapperState *s, bool insn, unsigned slot, + hwaddr src, hwaddr dst, size_t size); + +struct OtVMapperClass { + DeviceClass parent_class; + ResettablePhases parent_phases; + + OtVMapperTranslate translate; + + OtVMapperState **instances; + unsigned num_instances; +}; + +#endif /* HW_OPENTITAN_OT_VMAPPER_H */ From 1755ee56da88fd21f0ed62c0187d855a11b28ca6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 8 Apr 2025 16:13:12 +0200 Subject: [PATCH 13/69] [ot] hw/opentitan: ot_ibex_wrapper: add a link to the virtual remapper Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/opentitan/ot_ibex_wrapper.c b/hw/opentitan/ot_ibex_wrapper.c index 4dab792ef93db..4bccb07f66ad0 100644 --- a/hw/opentitan/ot_ibex_wrapper.c +++ b/hw/opentitan/ot_ibex_wrapper.c @@ -37,6 +37,7 @@ #include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_edn.h" #include "hw/opentitan/ot_ibex_wrapper.h" +#include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties-system.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" @@ -217,6 +218,7 @@ struct OtIbexWrapperState { char *ot_id; char *lc_ignore_ids; OtEDNState *edn; + OtVMapperState *vmapper; uint8_t num_regions; uint8_t edn_ep; uint8_t qemu_version; @@ -1323,6 +1325,8 @@ static void ot_ibex_wrapper_fill_tables(OtIbexWrapperState *s) static Property ot_ibex_wrapper_properties[] = { DEFINE_PROP_STRING("ot_id", OtIbexWrapperState, ot_id), DEFINE_PROP_LINK("edn", OtIbexWrapperState, edn, TYPE_OT_EDN, OtEDNState *), + DEFINE_PROP_LINK("vmapper", OtIbexWrapperState, vmapper, TYPE_OT_VMAPPER, + OtVMapperState *), DEFINE_PROP_UINT8("num-regions", OtIbexWrapperState, num_regions, 0), DEFINE_PROP_UINT8("edn-ep", OtIbexWrapperState, edn_ep, UINT8_MAX), DEFINE_PROP_BOOL("lc-ignore", OtIbexWrapperState, lc_ignore, false), @@ -1423,6 +1427,8 @@ static void ot_ibex_wrapper_realize(DeviceState *dev, Error **errp) g_assert(s->sys_mem); g_assert(s->ot_id); g_assert(s->num_regions); + /* if EDN mode is enabled, EDN endpoint must be set */ + g_assert(!s->edn || s->edn_ep != UINT8_MAX); s->remap_reg_count = s->num_regions * ACCESS_COUNT * sizeof(OtIbexRemap) / sizeof(uint32_t); From 4fe58b5f990445027d3894fd5aa672cf95fe32ce Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 1 Apr 2025 17:26:28 +0200 Subject: [PATCH 14/69] [ot] hw/opentitan: ot_darjeeling, ot_earlgrey: new virtual remapper Instantiate and configure the virtual remapper. Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 25 ++++++++++++++++--------- hw/riscv/ot_earlgrey.c | 24 ++++++++++++++++-------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 1fa3d992176af..b551e7d6fbd66 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -68,6 +68,7 @@ #include "hw/opentitan/ot_sram_ctrl.h" #include "hw/opentitan/ot_timer.h" #include "hw/opentitan/ot_uart.h" +#include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties.h" #include "hw/riscv/dm.h" #include "hw/riscv/dtm.h" @@ -159,6 +160,7 @@ enum OtDjSocDevice { OT_DJ_SOC_DEV_TAP_CTRL, OT_DJ_SOC_DEV_TIMER, OT_DJ_SOC_DEV_UART0, + OT_DJ_SOC_DEV_VMAPPER, /* IRQ splitters, i.e. 1-to-N signal dispatchers */ OT_DJ_SOC_SPLITTER_LC_HW_DEBUG, OT_DJ_SOC_SPLITTER_LC_ESCALATE, @@ -277,7 +279,7 @@ enum OtDjPinmuxMioOut { #define OT_DJ_PRIVATE_REGION_OFFSET 0x00000000u #define OT_DJ_PRIVATE_REGION_SIZE (1u << 30u) -#define OT_IBEX_WRAPPER_NUM_REGIONS 32u +#define OT_DJ_IBEX_WRAPPER_NUM_REGIONS 32u /* CTN address space */ #define OT_DJ_CTN_REGION_OFFSET 0x40000000u @@ -797,11 +799,12 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_ALERT(3, 98) ), .link = IBEXDEVICELINKDEFS( - OT_DJ_SOC_DEVLINK("edn", EDN0) + OT_DJ_SOC_DEVLINK("edn", EDN0), + OT_DJ_SOC_DEVLINK("vmapper", VMAPPER) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("edn-ep", 7u), - IBEX_DEV_UINT_PROP("num-regions", OT_IBEX_WRAPPER_NUM_REGIONS) + IBEX_DEV_UINT_PROP("num-regions", OT_DJ_IBEX_WRAPPER_NUM_REGIONS) ), }, [OT_DJ_SOC_DEV_RV_DM] = { @@ -1356,6 +1359,13 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_STRING_PROP("ot_id", "ret") ), }, + [OT_DJ_SOC_DEV_VMAPPER] = { + .type = TYPE_OT_VMAPPER, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_STRING_PROP("ot_id", "soc"), + IBEX_DEV_UINT_PROP("trans_count", OT_DJ_IBEX_WRAPPER_NUM_REGIONS) + ), + }, /* IRQ splitters */ [OT_DJ_SOC_SPLITTER_LC_HW_DEBUG] = { .type = TYPE_SPLIT_IRQ, @@ -1544,15 +1554,12 @@ static void ot_dj_soc_reset_hold(Object *obj, ResetType type) c->parent_phases.hold(obj, type); } - Object *dmi = OBJECT(s->devices[OT_DJ_SOC_DEV_DTM]); - resettable_reset(dmi, type); - - // TODO: not sure where Reset is plugged here... resettable_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_DM_LC_CTRL]), type); resettable_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_DM_MBX]), type); + resettable_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_DTM]), type); + resettable_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_DM]), type); + resettable_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_VMAPPER]), type); - Object *dm = OBJECT(s->devices[OT_DJ_SOC_DEV_DM]); - resettable_reset(dm, type); /* keep ROM_CTRLs in reset, we'll release them last */ resettable_assert_reset(OBJECT(s->devices[OT_DJ_SOC_DEV_ROM0]), type); diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 254d00b5cc940..6ce825decfc3b 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -67,6 +67,7 @@ #include "hw/opentitan/ot_timer.h" #include "hw/opentitan/ot_uart.h" #include "hw/opentitan/ot_unimp.h" +#include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties.h" #include "hw/riscv/dm.h" #include "hw/riscv/dtm.h" @@ -151,6 +152,7 @@ enum OtEGSocDevice { OT_EG_SOC_DEV_UART2, OT_EG_SOC_DEV_UART3, OT_EG_SOC_DEV_USBDEV, + OT_EG_SOC_DEV_VMAPPER, }; enum OtEgResetRequest { @@ -194,7 +196,7 @@ enum OtEGBoardDevice { /* Verilator AON clock is 125 kHz */ #define OT_EG_VERILATOR_AON_CLK_HZ OT_EG_VERILATOR_PERIPHERAL_CLK_HZ -#define OT_IBEX_WRAPPER_NUM_REGIONS 2u +#define OT_EG_IBEX_WRAPPER_NUM_REGIONS 2u static const uint8_t ot_eg_pmp_cfgs[] = { /* clang-format off */ @@ -1140,11 +1142,12 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(3, 64) ), .link = IBEXDEVICELINKDEFS( - OT_EG_SOC_DEVLINK("edn", EDN0) + OT_EG_SOC_DEVLINK("edn", EDN0), + OT_EG_SOC_DEVLINK("vmapper", VMAPPER) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("edn-ep", 7u), - IBEX_DEV_UINT_PROP("num-regions", OT_IBEX_WRAPPER_NUM_REGIONS), + IBEX_DEV_UINT_PROP("num-regions", OT_EG_IBEX_WRAPPER_NUM_REGIONS), /* remove the following line once LC state is implemented */ IBEX_DEV_BOOL_PROP("lc-ignore", true) ), @@ -1196,6 +1199,13 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 41) ), }, + [OT_EG_SOC_DEV_VMAPPER] = { + .type = TYPE_OT_VMAPPER, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_STRING_PROP("ot_id", "soc"), + IBEX_DEV_UINT_PROP("trans_count", OT_EG_IBEX_WRAPPER_NUM_REGIONS) + ), + } /* clang-format on */ }; @@ -1376,11 +1386,9 @@ static void ot_eg_soc_reset_hold(Object *obj, ResetType type) c->parent_phases.hold(obj, type); } - Object *dtm = OBJECT(s->devices[OT_EG_SOC_DEV_DTM]); - resettable_reset(dtm, type); - - Object *dm = OBJECT(s->devices[OT_EG_SOC_DEV_DM]); - resettable_reset(dm, type); + resettable_reset(OBJECT(s->devices[OT_EG_SOC_DEV_DTM]), type); + resettable_reset(OBJECT(s->devices[OT_EG_SOC_DEV_DM]), type); + resettable_reset(OBJECT(s->devices[OT_EG_SOC_DEV_VMAPPER]), type); /* keep ROM_CTRL in reset, we'll release it last */ resettable_assert_reset(OBJECT(s->devices[OT_EG_SOC_DEV_ROM_CTRL]), type); From 3d6a10de45cbd027430f60834c09b6d2d0b5ec32 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 14 Apr 2025 11:35:51 +0200 Subject: [PATCH 15/69] [ot] hw/opentitan: ot_ibex_wrapper: add new address translation implementation Replace MR alias based memory mapping with ot_vmapper engine by default. Use `alias-mode=true` to revert to the legacy implementation. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper.c | 48 +++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_ibex_wrapper.c b/hw/opentitan/ot_ibex_wrapper.c index 4bccb07f66ad0..db7220bc2a97a 100644 --- a/hw/opentitan/ot_ibex_wrapper.c +++ b/hw/opentitan/ot_ibex_wrapper.c @@ -25,6 +25,20 @@ * 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. + * + * Note: there are two modes to handle address remapping: + * - default mode: use an MMU-like implementation (via ot_vmapper) to remap + * addresses. This mode enables to remap instruction accesses and data + * accesses independently, as the real HW. However, due to QEMU limitations, + * addresses and mapped region sizes should be aligned and multiple of 4096 + * bytes, i.e. a standard MMU page size. This is the recommended mode. + * - legacy mode: This mode has no address nor size limitations, however it + * cannot distinguish instruction accesses from data accesses, which means + * that both kind of accesses must be defined for each active remapping slot + * for the remapping to be enabled. Moreover it relies on MemoryRegion + * aliasing and may not be as robust as the default mode. It is recommended + * to use the default mode whenever possible. To enable this legacy mode, + * set the "alias-mode" property to true. */ #include "qemu/osdep.h" @@ -223,6 +237,7 @@ struct OtIbexWrapperState { uint8_t edn_ep; uint8_t qemu_version; bool lc_ignore; + bool alias_mode; CharBackend chr; }; @@ -477,6 +492,29 @@ static void ot_ibex_wrapper_update_remap_mr( } } +static void ot_ibex_wrapper_update_remap_vmap( + OtIbexWrapperState *s, OtIbexRemapAccess access, unsigned slot) + +{ + g_assert(slot < s->num_regions); + g_assert(s->vmapper); + g_assert(access < ACCESS_COUNT); + + bool enable = s->regs.remap[access][slot].addr_en; + uint32_t match_addr = s->regs.remap[access][slot].addr_matching; + uint32_t remap_addr = s->regs.remap[access][slot].remap_addr; + + uint32_t map_size = (-match_addr & (match_addr + 1u)) << 1u; + uint32_t map_mask = ~(map_size - 1u); + uint32_t src_base = match_addr & map_mask; + uint32_t dst_base = remap_addr & map_mask; + + OtVMapperClass *vc = OT_VMAPPER_GET_CLASS(s->vmapper); + + vc->translate(s->vmapper, access == ACCESS_INSN, slot, src_base, dst_base, + enable ? map_size : 0); +} + /* * DV logging management */ @@ -1049,7 +1087,12 @@ ot_ibex_wrapper_write_remap(OtIbexWrapperState *s, unsigned reg, uint32_t value) return; } - ot_ibex_wrapper_update_remap_mr(s, (OtIbexRemapAccess)access, region); + if (s->alias_mode) { + ot_ibex_wrapper_update_remap_mr(s, (OtIbexRemapAccess)access, region); + + } else { + ot_ibex_wrapper_update_remap_vmap(s, (OtIbexRemapAccess)access, region); + } } static void ot_ibex_wrapper_write_nmi_enable(OtIbexWrapperState *s, @@ -1330,6 +1373,7 @@ static Property ot_ibex_wrapper_properties[] = { DEFINE_PROP_UINT8("num-regions", OtIbexWrapperState, num_regions, 0), DEFINE_PROP_UINT8("edn-ep", OtIbexWrapperState, edn_ep, UINT8_MAX), DEFINE_PROP_BOOL("lc-ignore", OtIbexWrapperState, lc_ignore, false), + DEFINE_PROP_BOOL("alias-mode", OtIbexWrapperState, alias_mode, false), DEFINE_PROP_UINT8("qemu_version", OtIbexWrapperState, qemu_version, 0), DEFINE_PROP_STRING("lc-ignore-ids", OtIbexWrapperState, lc_ignore_ids), DEFINE_PROP_CHR("logdev", OtIbexWrapperState, chr), @@ -1429,6 +1473,8 @@ static void ot_ibex_wrapper_realize(DeviceState *dev, Error **errp) g_assert(s->num_regions); /* if EDN mode is enabled, EDN endpoint must be set */ g_assert(!s->edn || s->edn_ep != UINT8_MAX); + /* if legacy alias_mode is disabled, vmapper must be set */ + g_assert(!s->alias_mode || s->vmapper); s->remap_reg_count = s->num_regions * ACCESS_COUNT * sizeof(OtIbexRemap) / sizeof(uint32_t); From e0d0af4e0a08a24bf6a9fd77ebf881b2bf597277 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 8 Apr 2025 17:46:04 +0200 Subject: [PATCH 16/69] [ot] docs/opentitan: ot_darjeeling, ot_earlgrey: document remapping feature Signed-off-by: Emmanuel Blot --- docs/opentitan/darjeeling.md | 14 ++++++++++++++ docs/opentitan/earlgrey.md | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/docs/opentitan/darjeeling.md b/docs/opentitan/darjeeling.md index f7087fe0773af..d47c12fb53d15 100644 --- a/docs/opentitan/darjeeling.md +++ b/docs/opentitan/darjeeling.md @@ -205,6 +205,20 @@ virtual machine. It contains three ASCII chars `QMU` followed with a configurabl the MSB, whose meaning is not defined. It can be any 8-byte value, and defaults to 0x0. To configure this version field, use the `qemu_version` property of the Ibex Wrapper device. +There are two modes to handle address remapping, with different limitations: + +- default mode: use an MMU-like implementation (via ot_vmapper) to remap addresses. This mode + enables to remap instruction accesses and data accesses independently, as the real HW. However, + due to QEMU limitations, addresses and mapped region sizes should be aligned and multiple of 4096 + bytes, i.e. a standard MMU page size. This is the recommended mode. + +- legacy mode: This mode has no address nor size limitations, however it cannot distinguish + instruction accesses from data accesses, which means that both kind of accesses must be defined + for each active remapping slot for the remapping to be enabled. Moreover it relies on MemoryRegion + aliasing and may not be as robust as the default mode. It is recommended to use the default mode + whenever possible. To enable this legacy mode, set the `alias-mode` property to true: + `-global ot-ibex_wrapper.alias-mode=true` + ### OTBN * `-global ot-otbn.logfile=` dumps executed instructions on OTBN core into the specified diff --git a/docs/opentitan/earlgrey.md b/docs/opentitan/earlgrey.md index 3dd1d3177d8a7..af36048c61449 100644 --- a/docs/opentitan/earlgrey.md +++ b/docs/opentitan/earlgrey.md @@ -156,6 +156,27 @@ See [`tools.md`](tools.md) Note: MTD bus 2 is assigned to the internal controller with the embedded flash storage. See also the SPI Host section. +### Ibex Wrapper + +The `FPGA_INFO` register of the Ibex Wrapper device is used to report that the HW platform is a QEMU +virtual machine. It contains three ASCII chars `QMU` followed with a configurable _version_ field in +the MSB, whose meaning is not defined. It can be any 8-byte value, and defaults to 0x0. To configure +this version field, use the `qemu_version` property of the Ibex Wrapper device. + +There are two modes to handle address remapping, with different limitations: + +- default mode: use an MMU-like implementation (via ot_vmapper) to remap addresses. This mode + enables to remap instruction accesses and data accesses independently, as the real HW. However, + due to QEMU limitations, addresses and mapped region sizes should be aligned and multiple of 4096 + bytes, i.e. a standard MMU page size. This is the recommended mode. + +- legacy mode: This mode has no address nor size limitations, however it cannot distinguish + instruction accesses from data accesses, which means that both kind of accesses must be defined + for each active remapping slot for the remapping to be enabled. Moreover it relies on MemoryRegion + aliasing and may not be as robust as the default mode. It is recommended to use the default mode + whenever possible. To enable this legacy mode, set the `alias-mode` property to true: + `-global ot-ibex_wrapper.alias-mode=true` + ### OTBN * `-global ot-otbn.logfile=` output OTBN execution message to the specified logfile. When From add4e83f27152f075514ecf12aded4e1aca54d97 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 10 Apr 2025 15:37:49 +0200 Subject: [PATCH 17/69] [ot] hw/opentitan: ot_dma: fix memory range checks, trigger a BUS error on invalid range Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 31 +++++++++++++++++++++---------- hw/opentitan/trace-events | 2 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 991e2ed5fe2f7..6d19b75b56ce4 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan DMA device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -599,22 +599,20 @@ ot_dma_check_device(OtDMAState *s, bool d_or_s, OtDMAAddrSpace *asix, "addr: 0x%" HWADDR_PRIx " size: 0x%" HWADDR_PRIx "\n", __func__, s->ot_id, d_or_s ? "dest" : "src", AS_NAME(aix), start, size); - ot_dma_set_xerror(s, d_or_s ? ERR_DEST_ADDR : ERR_SRC_ADDR); - return NULL; + /* the HW cannot detect this case; inform the guest and resume */ } if (mrs.offset_within_region + int128_getlo(mrs.size) < size) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Invalid size\n", __func__, s->ot_id); - ot_dma_set_xerror(s, ERR_SIZE); - memory_region_unref(mrs.mr); - return NULL; + /* the HW cannot detect this case; inform the guest and resume */ } trace_ot_dma_check_device(s->ot_id, d_or_s ? "Dest" : "Src", AS_NAME(aix), - start, size, mrs.mr->name, mrs.mr->ram); + start, size, mrs.mr ? mrs.mr->name : "invalid", + mrs.mr ? mrs.mr->ram : false); *asix = aix; - *is_dev = !mrs.mr->ram; + *is_dev = mrs.mr ? !mrs.mr->ram : false; /* neither RAM nor device... */ *offset = mrs.offset_within_region; /* caller should invoke memory_region_unref(mrs.mr) once done with mr */ @@ -661,8 +659,7 @@ static bool ot_dma_go(OtDMAState *s) case TRANSACTION_WIDTH_WORD: break; default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: %s: Invalid transaction width for hashing\n", + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Invalid transaction width\n", __func__, s->ot_id); ot_dma_set_xerror(s, ERR_SIZE); } @@ -873,6 +870,20 @@ static bool ot_dma_go(OtDMAState *s) } } + + if (!smr || !dmr) { + /* + * Real HW would start a DMA transaction and receive a bus error. + * Here it is impossible to start a transaction as memory buffer would + * not be valid, so trigger the error ealier. + * When device-to-device DMA is implemented, it might be possible to + * use a dummy, invalid region similar with no ops (see the default + * QEMU unassigned_mem_ops) to start a DMA operation and handle the + * error once it fails. + */ + ot_dma_set_xerror(s, ERR_BUS); + } + if (s->state == SM_ERROR) { if (smr) { memory_region_unref(smr); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 938bbad573b4c..69e3a54d0ac57 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -122,7 +122,7 @@ ot_dm_tl_update(const char *devname, uint32_t addr, uint32_t value, const char * ot_dma_abort(const char *id) "%s" ot_dma_change_state(const char *id, int line, const char *state, int stateval) "%s: @%d: %s [%d]" -ot_dma_check_device(const char *id, const char *dir, const char *asname, uint64_t addr, uint64_t size, const char *rootname, unsigned ram) "%s: %s as=%s addr=0x%" PRIx64 " size:0x%" PRIx64 " %s ram:%u" +ot_dma_check_device(const char *id, const char *dir, const char *asname, uint64_t addr, uint64_t size, const char *rootname, bool ram) "%s: %s as=%s addr=0x%" PRIx64 " size:0x%" PRIx64 " %s ram:%u" ot_dma_complete(const char *id, int res) "%s: %d" ot_dma_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_dma_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" From 281ecb229e9a2dd5a9c8443a655a47311952a9c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 10 Apr 2025 17:18:57 +0200 Subject: [PATCH 18/69] [ot] hw/opentitan: ot_dma: fix SHA message digest byte order. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 6d19b75b56ce4..2c441b7130fae 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -1020,9 +1020,7 @@ static void ot_dma_complete(OtDMAState *s) unsigned md_count = sha->desc->hashsize / sizeof(uint32_t); for (unsigned ix = 0; ix < md_count; ix++) { - // it is likely some shuffling (little endian, etc) is required - // here, but for now the bit order of the HW is not known. - s->regs[R_SHA2_DIGEST_0 + ix] = md[ix]; + stl_be_p(&s->regs[R_SHA2_DIGEST_0 + ix], md[ix]); } s->regs[R_STATUS] |= R_STATUS_SHA2_DIGEST_VALID_MASK; From 2c88cb31e1a7856cc9cfc0d407768f6724d88f46 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 11 Apr 2025 12:19:33 +0200 Subject: [PATCH 19/69] [ot] hw/opentitan: ot_rstmgr: reset all but one registers on reset Only RESET_INFO needs to be preserved on reset. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_rstmgr.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 1bb26727ea2ff..ebf1ab1a100e0 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Reset Manager device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -520,23 +520,24 @@ static void ot_rstmgr_reset(DeviceState *dev) s->cpu = cpu; } - s->regs[R_RESET_REQ] = OT_MULTIBITBOOL4_FALSE; + uint32_t reset_info = s->regs[R_RESET_INFO]; + + memset(s->regs, 0, REGS_SIZE); + if (s->por) { - memset(s->regs, 0, REGS_SIZE); s->regs[R_RESET_INFO] = R_RESET_INFO_POR_MASK; s->por = false; } else { - /* TODO: need to check which registers are actually reset when !PoR */ - s->regs[R_ALERT_TEST] = 0u; + s->regs[R_RESET_INFO] = reset_info; } - - s->regs[R_ALERT_REGWEN] = 0x1u; - s->regs[R_CPU_REGWEN] = 0x1u; + s->regs[R_RESET_REQ] = OT_MULTIBITBOOL4_FALSE; + s->regs[R_ALERT_REGWEN] = R_ALERT_REGWEN_EN_MASK; + s->regs[R_CPU_REGWEN] = R_CPU_REGWEN_EN_MASK; for (unsigned ix = 0; ix < PARAM_NUM_SW_RESETS; ix++) { - s->regs[R_SW_RST_REGWEN_0 + ix] = 0x1u; + s->regs[R_SW_RST_REGWEN_0 + ix] = SW_RST_REGWEN_EN_MASK; } for (unsigned ix = 0; ix < PARAM_NUM_SW_RESETS; ix++) { - s->regs[R_SW_RST_CTRL_N_0 + ix] = 0x1u; + s->regs[R_SW_RST_CTRL_N_0 + ix] = SW_RST_CTRL_VAL_MASK; } ibex_irq_lower(&s->soc_reset); From 6db86d1602f53ce845015f700a0ecb5d50de5a77 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 14 Apr 2025 14:56:37 +0200 Subject: [PATCH 20/69] [ot] hw/riscv: dm: discard exception signal when hart is not defined. If the guest randomly write in the PULP debug module, this would made the QEMU machine to crash. Signed-off-by: Emmanuel Blot --- hw/riscv/dm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/riscv/dm.c b/hw/riscv/dm.c index 6f9f21144e346..334c72da1387f 100644 --- a/hw/riscv/dm.c +++ b/hw/riscv/dm.c @@ -1016,6 +1016,11 @@ static void riscv_dm_acknowledge(void *opaque, int irq, int level) case ACK_EXCEPTION: /* level value is meaningless */ hart = dm->hart; + if (!hart) { + /* likely a more serious issue */ + qemu_log("%s: no selected hart\n", __func__); + break; + } dm->cmd_err = CMD_ERR_EXCEPTION; riscv_dm_set_cs(dm, false); riscv_dm_set_busy(dm, false); From 468447f064184c3609b50bf2df128235bae402b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 14 Apr 2025 16:17:44 +0200 Subject: [PATCH 21/69] [ot] hw/opentitan: ot_lc_ctrl: fix an out-of-bound buffer access. Each state contains word_count uint16_t entries. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_lc_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index 910286ac93440..4bc9544c58d49 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -2033,7 +2033,7 @@ static void ot_lc_ctrl_configure_transitions( tix++, lcval += tdesc->word_count) { memcpy(&lcval[0], &last[0], tix * sizeof(uint16_t)); memcpy(&lcval[tix], &first[tix], - (tdesc->step_count - tix) * sizeof(uint16_t)); + (tdesc->word_count - tix) * sizeof(uint16_t)); } g_free(last); From 89a5d3893f716e182b723852d9da54ae64783c32 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 14 Apr 2025 16:51:37 +0200 Subject: [PATCH 22/69] [ot] hw/opentitan: ot_spi_host: fix an out-of-bound buffer access. Invalid CSID should not be used to index the banked configopts registers. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_host.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_spi_host.c b/hw/opentitan/ot_spi_host.c index cc01a8ffcf8e6..a456a039701c9 100644 --- a/hw/opentitan/ot_spi_host.c +++ b/hw/opentitan/ot_spi_host.c @@ -1145,25 +1145,28 @@ static void ot_spi_host_io_write(void *opaque, hwaddr addr, uint64_t val64, REG_UPDATE(s, ERROR_STATUS, CMDINVAL, 1u); } - if (!(s->regs[R_CSID] < s->num_cs)) { + unsigned csid = s->regs[R_CSID]; + + if (!(csid < s->num_cs)) { /* CSID exceeds max num_cs */ - qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid csid\n", __func__, - s->ot_id); + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: invalid csid: %u\n", + __func__, s->ot_id, csid); REG_UPDATE(s, ERROR_STATUS, CSIDINVAL, 1u); + csid = 0; } CmdFifoSlot slot = { .opts = s->regs[R_CONFIGOPTS], .command = val32, - .cs = s->regs[R_CSID], + .cs = csid, .id = s->last_command_id++, }; trace_ot_spi_host_new_command( s->ot_id, slot.id, F_COMMAND_DIRECTION[FIELD_EX32(slot.command, COMMAND, DIRECTION)], - F_COMMAND_SPEED[FIELD_EX32(slot.command, COMMAND, SPEED)], - s->regs[R_CSID], (bool)FIELD_EX32(slot.command, COMMAND, CSAAT), + F_COMMAND_SPEED[FIELD_EX32(slot.command, COMMAND, SPEED)], csid, + (bool)FIELD_EX32(slot.command, COMMAND, CSAAT), FIELD_EX32(slot.command, COMMAND, LEN) + 1u); cmdfifo_push(s->cmd_fifo, &slot); From 80fbc1c56c072660525e1144a7dfd40ef5f4e9a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 15 Apr 2025 11:10:46 +0200 Subject: [PATCH 23/69] [ot] hw/opentitan: ot_spi_device: fix JEDEC ID byte sequence generation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_device.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index d14ddc5dd1d0b..66ec896708d16 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -115,7 +115,7 @@ REG32(JEDEC_CC, 0x2cu) FIELD(JEDEC_CC, CC, 0u, 8u) FIELD(JEDEC_CC, NUM_CC, 8u, 8u) REG32(JEDEC_ID, 0x30u) - FIELD(JEDEC_ID, ID, 0u, 16u) + FIELD(JEDEC_ID, DEVICE, 0u, 16u) FIELD(JEDEC_ID, MF, 16u, 8u) REG32(READ_THRESHOLD, 0x34u) FIELD(READ_THRESHOLD, THRESHOLD, 0u, 10u) @@ -580,7 +580,7 @@ static const char *TPM_REG_NAMES[TPM_REGS_COUNT] = { R_FLASH_STATUS_DRV1_MASK | R_FLASH_STATUS_HOLD_NRST_MASK) #define FLASH_STATUS_MASK (R_FLASH_STATUS_BUSY_MASK | FLASH_STATUS_STATUS_MASK) #define JEDEC_CC_MASK (R_JEDEC_CC_CC_MASK | R_JEDEC_CC_NUM_CC_MASK) -#define JEDEC_ID_MASK (R_JEDEC_ID_ID_MASK | R_JEDEC_ID_MF_MASK) +#define JEDEC_ID_MASK (R_JEDEC_ID_DEVICE_MASK | R_JEDEC_ID_MF_MASK) #define COMMAND_OPCODE(_cmd_info_) \ ((uint8_t)((_cmd_info_) & CMD_INFO_OPCODE_MASK)) @@ -924,14 +924,19 @@ static void ot_spi_device_flash_decode_read_jedec(OtSPIDeviceState *s) { SpiDeviceFlash *f = &s->flash; - f->len = 3u; - uint32_t cc_count = FIELD_EX32(s->spi_regs[R_JEDEC_CC], JEDEC_CC, NUM_CC); - uint32_t cc_code = FIELD_EX32(s->spi_regs[R_JEDEC_CC], JEDEC_CC, CC); - uint32_t jedec = s->spi_regs[R_JEDEC_ID]; - /* use len field to count continuation code */ - memset(f->buffer, (int)cc_code, cc_count); - stl_le_p(&f->buffer[cc_count], bswap32(jedec << 8)); - f->len += cc_count; + uint8_t cc_count = + (uint8_t)FIELD_EX32(s->spi_regs[R_JEDEC_CC], JEDEC_CC, NUM_CC); + uint8_t cc_code = + (uint8_t)FIELD_EX32(s->spi_regs[R_JEDEC_CC], JEDEC_CC, CC); + uint8_t jedec_manuf = + (uint8_t)FIELD_EX32(s->spi_regs[R_JEDEC_ID], JEDEC_ID, MF); + uint16_t jedec_device = + (uint16_t)FIELD_EX32(s->spi_regs[R_JEDEC_ID], JEDEC_ID, DEVICE); + memset(f->buffer, (int)(uint8_t)cc_code, (size_t)cc_count); + f->len = cc_count; + f->buffer[f->len++] = jedec_manuf; + f->buffer[f->len++] = (uint8_t)(jedec_device >> 8u); + f->buffer[f->len++] = (uint8_t)(jedec_device >> 0u); memset(&f->buffer[f->len], (int)SPI_DEFAULT_TX_VALUE, SPI_FLASH_BUFFER_SIZE - f->len); f->src = f->buffer; From 1b9d41b6991edba6ef27726991e36287175b307c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 15 Apr 2025 17:23:43 +0200 Subject: [PATCH 24/69] [ot] hw/opentitan: ot_otp_dj: fix invalid ECC memory buffer reference. Also add an extra sanity check. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index d88496e57d14a..1a639d614d1f8 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling One Time Programmable (OTP) memory controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -1807,6 +1807,8 @@ static inline int ot_otp_dj_write_backend(OtOTPDjState *s, const void *buffer, * the blk_pwrite API is awful, isolate it so that linter exceptions are * are not repeated over and over */ + g_assert(offset + size <= s->otp->size); + // NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer, /* a bitfield of enum is not an enum item */ @@ -3365,7 +3367,7 @@ static void ot_otp_dj_lci_write_complete(OtOTPDjState *s, bool success) } if (ot_otp_dj_is_ecc_enabled(s)) { offset = (uintptr_t)s->otp->ecc - (uintptr_t)s->otp->storage; - if (ot_otp_dj_write_backend(s, &((uint16_t *)&s->otp->ecc)[lc_off], + if (ot_otp_dj_write_backend(s, &((uint16_t *)s->otp->ecc)[lc_off], (unsigned)(offset + (lcdesc->offset >> 1u)), lcdesc->size >> 1u)) { @@ -3774,6 +3776,7 @@ static void ot_otp_dj_load(OtOTPDjState *s) otp->data_size = data_size; otp->ecc_size = ecc_size; + otp->size = otp_size; } static Property ot_otp_dj_properties[] = { From 265c5c7d4dbd27973a25478727ad5e004eedbf9c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 15 Apr 2025 18:31:37 +0200 Subject: [PATCH 25/69] [ot] hw/opentitan: ot_otp: rename OtOTPStateClass as OtOTPClass Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 4 ++-- hw/opentitan/ot_entropy_src.c | 3 +-- hw/opentitan/ot_lc_ctrl.c | 9 +++------ hw/opentitan/ot_otp.c | 6 ++++-- hw/opentitan/ot_otp_dj.c | 14 +++++++------- hw/opentitan/ot_otp_eg.c | 12 ++++++------ hw/opentitan/ot_sram_ctrl.c | 6 ++---- include/hw/opentitan/ot_otp.h | 6 +++--- include/hw/opentitan/ot_otp_dj.h | 2 +- include/hw/opentitan/ot_otp_eg.h | 2 +- 10 files changed, 30 insertions(+), 34 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index 601d5d57e0961..c78e500354f41 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -1825,8 +1825,8 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, xtrace_ot_csrng_info("handling CTRL change", val32); ot_csrng_handle_enable(s); bool granted; - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = + OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); const OtOTPEntropyCfg *entropy_cfg = oc->get_entropy_cfg(s->otp_ctrl); if (entropy_cfg) { diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 34201bb562d41..4276cd7011169 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1600,8 +1600,7 @@ static void ot_entropy_src_reset(DeviceState *dev) ibex_irq_set(&s->alerts[ix], 0); } - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); const OtOTPEntropyCfg *entropy_cfg = oc->get_entropy_cfg(s->otp_ctrl); g_assert(entropy_cfg); diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index 4bc9544c58d49..c6516d4cb29c8 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -1115,8 +1115,7 @@ static void ot_lc_ctrl_kmac_handle_resp(void *opaque, const OtKMACAppRsp *rsp) static uint32_t ot_lc_ctrl_load_lc_info(OtLcCtrlState *s) { - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); OtLcCtrlStateValue lc_state; OtLcCtrlTransitionCountValue lc_tcount; uint8_t lc_valid; @@ -1195,8 +1194,7 @@ static uint32_t ot_lc_ctrl_load_lc_info(OtLcCtrlState *s) static void ot_lc_ctrl_load_otp_hw_cfg(OtLcCtrlState *s) { - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); const OtOTPHWCfg *hw_cfg = oc->get_hw_cfg(s->otp_ctrl); memcpy(&s->regs[R_DEVICE_ID_0], &hw_cfg->device_id[0], @@ -1262,8 +1260,7 @@ static void ot_lc_ctrl_handle_otp_ack(void *opaque, bool ack) static void ot_lc_ctrl_program_otp(OtLcCtrlState *s, unsigned lc_tcount, OtLcState lc_state) { - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); if (!oc->program_req) { qemu_log_mask(LOG_UNIMP, diff --git a/hw/opentitan/ot_otp.c b/hw/opentitan/ot_otp.c index 982bd89ff3018..d2132c7d7c6e9 100644 --- a/hw/opentitan/ot_otp.c +++ b/hw/opentitan/ot_otp.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan One Time Programmable (OTP) memory controller * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -26,9 +26,11 @@ */ #include "qemu/osdep.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_otp.h" -OBJECT_DEFINE_ABSTRACT_TYPE(OtOTPState, ot_otp, OT_OTP, SYS_BUS_DEVICE) +OT_OBJECT_DEFINE_ABSTRACT_TYPE(OtOTPState, OtOTPClass, ot_otp, OT_OTP, + SYS_BUS_DEVICE) static void ot_otp_class_init(ObjectClass *oc, void *data) {} diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 1a639d614d1f8..c87e9ff496aa6 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3980,13 +3980,13 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, ot_otp_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - OtOTPStateClass *odc = OT_OTP_CLASS(klass); + OtOTPClass *oc = OT_OTP_CLASS(klass); - odc->get_lc_info = &ot_otp_dj_get_lc_info; - odc->get_hw_cfg = &ot_otp_dj_get_hw_cfg; - odc->get_entropy_cfg = &ot_otp_dj_get_entropy_cfg; - odc->get_otp_key = &ot_otp_dj_get_otp_key; - odc->program_req = &ot_otp_dj_program_req; + oc->get_lc_info = &ot_otp_dj_get_lc_info; + oc->get_hw_cfg = &ot_otp_dj_get_hw_cfg; + oc->get_entropy_cfg = &ot_otp_dj_get_entropy_cfg; + oc->get_otp_key = &ot_otp_dj_get_otp_key; + oc->program_req = &ot_otp_dj_program_req; } static const TypeInfo ot_otp_dj_info = { @@ -3994,7 +3994,7 @@ static const TypeInfo ot_otp_dj_info = { .parent = TYPE_OT_OTP, .instance_size = sizeof(OtOTPDjState), .instance_init = &ot_otp_dj_init, - .class_size = sizeof(OtOTPStateClass), + .class_size = sizeof(OtOTPClass), .class_init = &ot_otp_dj_class_init, }; diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 09cc68eea019e..fdcef75504c8d 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey One Time Programmable (OTP) memory controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -1427,11 +1427,11 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, ot_otp_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - OtOTPStateClass *odc = OT_OTP_CLASS(klass); + OtOTPClass *oc = OT_OTP_CLASS(klass); - odc->get_lc_info = &ot_otp_eg_ctrl_get_lc_info; - odc->get_hw_cfg = &ot_otp_eg_ctrl_get_hw_cfg; - odc->get_entropy_cfg = &ot_otp_eg_ctrl_get_entropy_cfg; + oc->get_lc_info = &ot_otp_eg_ctrl_get_lc_info; + oc->get_hw_cfg = &ot_otp_eg_ctrl_get_hw_cfg; + oc->get_entropy_cfg = &ot_otp_eg_ctrl_get_entropy_cfg; } static const TypeInfo ot_otp_eg_info = { @@ -1439,7 +1439,7 @@ static const TypeInfo ot_otp_eg_info = { .parent = TYPE_OT_OTP, .instance_size = sizeof(OtOTPEgState), .instance_init = &ot_otp_eg_init, - .class_size = sizeof(OtOTPStateClass), + .class_size = sizeof(OtOTPClass), .class_init = &ot_otp_eg_class_init, }; diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index 6e50de332db62..5717695146db8 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -280,8 +280,7 @@ static void ot_sram_ctrl_reseed(OtSramCtrlState *s) * neither PRINCE block cipher nor shallow substitution-permutation. * Seed and Nonce are combined to initialize a QEMU PRNG instance. */ - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); if (!oc->get_otp_key) { /* on EarlGrey, OTP key handing has not been implemented */ qemu_log_mask(LOG_UNIMP, "%s: %s OTP does not support key generation\n", @@ -690,8 +689,7 @@ static void ot_sram_ctrl_reset(DeviceState *dev) s->regs[R_READBACK] = OT_MULTIBITBOOL4_FALSE; if (s->otp_ctrl) { - OtOTPStateClass *oc = - OBJECT_GET_CLASS(OtOTPStateClass, s->otp_ctrl, TYPE_OT_OTP); + OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); s->otp_ifetch = oc->get_hw_cfg(s->otp_ctrl)->en_sram_ifetch == OT_MULTIBITBOOL8_TRUE; } else { diff --git a/include/hw/opentitan/ot_otp.h b/include/hw/opentitan/ot_otp.h index a5c60b27e2b51..adc23ce36efdf 100644 --- a/include/hw/opentitan/ot_otp.h +++ b/include/hw/opentitan/ot_otp.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan One Time Programmable (OTP) memory controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -33,7 +33,7 @@ #include "hw/sysbus.h" #define TYPE_OT_OTP "ot-otp" -OBJECT_DECLARE_TYPE(OtOTPState, OtOTPStateClass, OT_OTP) +OBJECT_DECLARE_TYPE(OtOTPState, OtOTPClass, OT_OTP) /* Input signals from life cycle */ typedef enum { @@ -110,7 +110,7 @@ struct OtOTPState { typedef void (*ot_otp_program_ack_fn)(void *opaque, bool ack); -struct OtOTPStateClass { +struct OtOTPClass { SysBusDeviceClass parent_class; /* diff --git a/include/hw/opentitan/ot_otp_dj.h b/include/hw/opentitan/ot_otp_dj.h index 1417098c9a34e..0a7b148cc2001 100644 --- a/include/hw/opentitan/ot_otp_dj.h +++ b/include/hw/opentitan/ot_otp_dj.h @@ -32,6 +32,6 @@ #include "hw/opentitan/ot_otp.h" #define TYPE_OT_OTP_DJ "ot-otp-dj" -OBJECT_DECLARE_TYPE(OtOTPDjState, OtOTPStateClass, OT_OTP_DJ) +OBJECT_DECLARE_TYPE(OtOTPDjState, OtOTPClass, OT_OTP_DJ) #endif /* HW_OPENTITAN_OT_OTP_DJ_H */ diff --git a/include/hw/opentitan/ot_otp_eg.h b/include/hw/opentitan/ot_otp_eg.h index 47e5ad999038f..9a7f7759f593c 100644 --- a/include/hw/opentitan/ot_otp_eg.h +++ b/include/hw/opentitan/ot_otp_eg.h @@ -32,6 +32,6 @@ #include "hw/opentitan/ot_otp.h" #define TYPE_OT_OTP_EG "ot-otp-eg" -OBJECT_DECLARE_TYPE(OtOTPEgState, OtOTPStateClass, OT_OTP_EG) +OBJECT_DECLARE_TYPE(OtOTPEgState, OtOTPClass, OT_OTP_EG) #endif /* HW_OPENTITAN_OT_OTP_EG_H */ From ef6f68a849a72372c83195b5bfdd91742d8db5a4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 15 Apr 2025 18:48:58 +0200 Subject: [PATCH 26/69] [ot] hw/opentitan: ot_otp: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 34 +++++++++++++++++++++++++++------- hw/opentitan/ot_otp_eg.c | 28 ++++++++++++++++++++++++---- hw/opentitan/trace-events | 2 +- include/hw/opentitan/ot_otp.h | 1 + 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index c87e9ff496aa6..fedb471548b5e 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3809,20 +3809,22 @@ static const MemoryRegionOps ot_otp_dj_swcfg_ops = { .impl.max_access_size = 4, }; -static void ot_otp_dj_reset(DeviceState *dev) +static void ot_otp_dj_reset_enter(Object *obj, ResetType type) { - OtOTPDjState *s = OT_OTP_DJ(dev); + OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPDjState *s = OT_OTP_DJ(obj); - trace_ot_otp_reset(s->ot_id); + trace_ot_otp_reset(s->ot_id, "enter"); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->dai->delay); timer_del(s->lci->prog_delay); qemu_bh_cancel(s->keygen->entropy_bh); s->keygen->edn_sched = false; - ot_edn_connect_endpoint(s->edn, s->edn_ep, &ot_otp_dj_keygen_push_entropy, - s); - memset(s->regs, 0, REGS_COUNT * sizeof(uint32_t)); s->regs[R_DIRECT_ACCESS_REGWEN] = 0x1u; @@ -3872,6 +3874,21 @@ static void ot_otp_dj_reset(DeviceState *dev) } DAI_CHANGE_STATE(s, OTP_DAI_RESET); LCI_CHANGE_STATE(s, OTP_LCI_RESET); +} + +static void ot_otp_dj_reset_exit(Object *obj, ResetType type) +{ + OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPDjState *s = OT_OTP_DJ(obj); + + trace_ot_otp_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + ot_edn_connect_endpoint(s->edn, s->edn_ep, &ot_otp_dj_keygen_push_entropy, + s); qemu_bh_schedule(s->keygen->entropy_bh); } @@ -3975,12 +3992,15 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) g_assert(OTP_PART_LIFE_CYCLE_SIZE == OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size); - device_class_set_legacy_reset(dc, &ot_otp_dj_reset); dc->realize = &ot_otp_dj_realize; device_class_set_props(dc, ot_otp_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); OtOTPClass *oc = OT_OTP_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_dj_reset_enter, NULL, + &ot_otp_dj_reset_exit, + &oc->parent_phases); oc->get_lc_info = &ot_otp_dj_get_lc_info; oc->get_hw_cfg = &ot_otp_dj_get_hw_cfg; diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index fdcef75504c8d..e44ed2fa5dafe 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1353,11 +1353,16 @@ static void ot_otp_eg_load(OtOTPEgState *s, Error **errp) ot_otp_eg_load_hw_cfg(s); } -static void ot_otp_eg_reset(DeviceState *dev) +static void ot_otp_eg_reset_enter(Object *obj, ResetType type) { - OtOTPEgState *s = OT_OTP_EG(dev); + OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPEgState *s = OT_OTP_EG(obj); + + trace_ot_otp_reset(s->ot_id, "enter"); - trace_ot_otp_reset(s->ot_id); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->dai_delay); @@ -1378,6 +1383,18 @@ static void ot_otp_eg_reset(DeviceState *dev) ot_otp_eg_update_alerts(s); } +static void ot_otp_eg_reset_exit(Object *obj, ResetType type) +{ + OtOTPClass *c = OT_OTP_GET_CLASS(obj); + OtOTPEgState *s = OT_OTP_EG(obj); + + trace_ot_otp_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } +} + static void ot_otp_eg_realize(DeviceState *dev, Error **errp) { OtOTPEgState *s = OT_OTP_EG(dev); @@ -1422,12 +1439,15 @@ static void ot_otp_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_otp_eg_reset); dc->realize = &ot_otp_eg_realize; device_class_set_props(dc, ot_otp_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); OtOTPClass *oc = OT_OTP_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_eg_reset_enter, NULL, + &ot_otp_eg_reset_exit, + &oc->parent_phases); oc->get_lc_info = &ot_otp_eg_ctrl_get_lc_info; oc->get_hw_cfg = &ot_otp_eg_ctrl_get_hw_cfg; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 69e3a54d0ac57..29599b93dfcb1 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -354,7 +354,7 @@ ot_otp_load_backend(const char * id, unsigned ver, const char *mode, unsigned ec ot_otp_load_token(const char * id, const char *token, unsigned tkx, uint64_t hi, uint64_t lo, const char *valid) "%s: %s (%u) 0x%016" PRIx64 "%016" PRIx64 ": %svalid" ot_otp_mismatch_digest(const char * id, const char* part, unsigned pix, uint64_t sdig, uint64_t ldig) "%s: mismatch digest on %s (#%u), computed 0x%" PRIx64 " stored 0x%" PRIx64 ot_otp_pwr_otp_req(const char * id, const char *where) "%s: %s" -ot_otp_reset(const char * id) "%s" +ot_otp_reset(const char * id, const char *phase) "%s: %s" ot_otp_set_error(const char * id, const char *part, unsigned pix, const char* err, unsigned eix) "%s: %s (#%u): %s (%u)" ot_otp_skip_digest(const char * id, const char* part, unsigned pix) "%s: skipping empty digest on %s (#%u)" diff --git a/include/hw/opentitan/ot_otp.h b/include/hw/opentitan/ot_otp.h index adc23ce36efdf..ac94e2cfafb75 100644 --- a/include/hw/opentitan/ot_otp.h +++ b/include/hw/opentitan/ot_otp.h @@ -112,6 +112,7 @@ typedef void (*ot_otp_program_ack_fn)(void *opaque, bool ack); struct OtOTPClass { SysBusDeviceClass parent_class; + ResettablePhases parent_phases; /* * Provide OTP lifecycle information. From fe8af2b229f74f2e05400c929f13a472e633bc90 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 15 Apr 2025 19:30:30 +0200 Subject: [PATCH 27/69] [ot] hw/opentitan: ot_otp: ensure OTP content is reloaded on each reset. OTP content used to be loaded in PoR reset, the OTP implementation maintaining a synchronized copy of its buffer in the file backend. However this led to never checking the actual back end content in the current QEMU session, which is a far too strong departure from the actual HW. This new implementation reloads the OTP content from the back end file on each reset, to ensure that data committed to the back end is actually used on each OT run session. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 470 +++++++++++++++++++++------------------ 1 file changed, 251 insertions(+), 219 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index fedb471548b5e..b82a6a7ebc033 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1770,31 +1770,6 @@ static void ot_otp_dj_check_partition_integrity(OtOTPDjState *s, unsigned ix) } } -static void ot_otp_dj_initialize_partitions(OtOTPDjState *s) -{ - for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { - if (ot_otp_dj_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { - if (ot_otp_dj_apply_ecc(s, ix)) { - continue; - } - } - - if (OtOTPPartDescs[ix].sw_digest) { - uint64_t digest = ot_otp_dj_get_part_digest(s, (int)ix); - s->partctrls[ix].locked = digest != 0; - continue; - } - - if (OtOTPPartDescs[ix].buffered) { - ot_otp_dj_bufferize_partition(s, ix); - if (OtOTPPartDescs[ix].hw_digest) { - ot_otp_dj_check_partition_integrity(s, ix); - } - continue; - } - } -} - static bool ot_otp_dj_is_backend_writable(OtOTPDjState *s) { return (s->blk != NULL) && blk_is_writable(s->blk); @@ -3041,64 +3016,6 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( return MEMTX_OK; } -static void ot_otp_dj_load_hw_cfg(OtOTPDjState *s) -{ - OtOTPStorage *otp = s->otp; - OtOTPHWCfg *hw_cfg = s->hw_cfg; - - memcpy(hw_cfg->device_id, &otp->data[R_HW_CFG0_DEVICE_ID], - sizeof(*hw_cfg->device_id)); - memcpy(hw_cfg->manuf_state, &otp->data[R_HW_CFG0_MANUF_STATE], - sizeof(*hw_cfg->manuf_state)); - memcpy(&hw_cfg->soc_dbg_state[0], &otp->data[R_HW_CFG1_SOC_DBG_STATE], - sizeof(uint32_t)); - hw_cfg->en_sram_ifetch = (uint8_t)otp->data[R_HW_CFG1_EN_SRAM_IFETCH]; -} - -static void ot_otp_dj_load_tokens(OtOTPDjState *s) -{ - memset(s->tokens, 0, sizeof(*s->tokens)); - - const uint32_t *data = s->otp->data; - OtOTPTokens *tokens = s->tokens; - - static_assert(sizeof(OtOTPTokenValue) == 16u, "Invalid token size"); - - for (unsigned tkx = 0; tkx < OTP_TOKEN_COUNT; tkx++) { - unsigned partition; - uint32_t reg; - - switch (tkx) { - case OTP_TOKEN_TEST_UNLOCK: - partition = OTP_PART_SECRET0; - reg = R_SECRET0_TEST_UNLOCK_TOKEN; - break; - case OTP_TOKEN_TEST_EXIT: - partition = OTP_PART_SECRET0; - reg = R_SECRET0_TEST_EXIT_TOKEN; - break; - case OTP_TOKEN_RMA: - partition = OTP_PART_SECRET2; - reg = R_SECRET2_RMA_TOKEN; - break; - default: - g_assert_not_reached(); - break; - } - - OtOTPTokenValue value; - memcpy(&value, &data[reg], sizeof(OtOTPTokenValue)); - if (s->partctrls[partition].locked) { - tokens->values[tkx] = value; - tokens->valid_bm |= 1u << tkx; - } - trace_ot_otp_load_token(s->ot_id, OTP_TOKEN_NAME(tkx), tkx, value.hi, - value.lo, - (s->tokens->valid_bm & (1u << tkx)) ? "" : - "in"); - } -} - static void ot_otp_dj_get_lc_info( const OtOTPState *s, uint16_t *lc_tcount, uint16_t *lc_state, uint8_t *lc_valid, uint8_t *secret_valid, const OtOTPTokens **tokens) @@ -3497,15 +3414,243 @@ static void ot_otp_dj_pwr_otp_req(void *opaque, int n, int level) } } +static void ot_otp_dj_pwr_load(OtOTPDjState *s) +{ + /* + * HEADER_FORMAT + * + * | magic | 4 char | "vOFTP" | + * | hlength | uint32_t | count of header bytes after this point | + * | version | uint32_t | version of the header (v2) | + * | eccbits | uint16_t | ECC size in bits | + * | eccgran | uint16_t | ECC granule | + * | dlength | uint32_t | count of data bytes (% uint64_t) | + * | elength | uint32_t | count of ecc bytes (% uint64_t) | + * | -------- | ---------- | only in V2 | + * | dig_iv | 8 uint8_t | Present digest initialization vector | + * | dig_iv | 16 uint8_t | Present digest initialization vector | + */ + + struct otp_header { + char magic[4]; + uint32_t hlength; + uint32_t version; + uint16_t eccbits; + uint16_t eccgran; + uint32_t data_len; + uint32_t ecc_len; + /* added in V2 */ + uint8_t digest_iv[8u]; + uint8_t digest_constant[16u]; + }; + + static_assert(sizeof(struct otp_header) == 48u, "Invalid header size"); + + /* data following header should always be 64-bit aligned */ + static_assert((sizeof(struct otp_header) % sizeof(uint64_t)) == 0, + "invalid header definition"); + + size_t header_size = sizeof(struct otp_header); + size_t data_size = 0u; + size_t ecc_size = 0u; + + for (unsigned ix = 0u; ix < OTP_PART_COUNT; ix++) { + size_t psize = (size_t)OtOTPPartDescs[ix].size; + size_t dsize = ROUND_UP(psize, sizeof(uint64_t)); + data_size += dsize; + /* up to 1 ECC byte for 2 data bytes */ + ecc_size += DIV_ROUND_UP(dsize, 2u); + } + size_t otp_size = header_size + data_size + ecc_size; + + otp_size = ROUND_UP(otp_size, 4096u); + + OtOTPStorage *otp = s->otp; + + /* always allocates the requested size even if blk is NULL */ + if (!otp->storage) { + /* only allocated once on PoR */ + otp->storage = blk_blockalign(s->blk, otp_size); + } + + uintptr_t base = (uintptr_t)otp->storage; + g_assert(!(base & (sizeof(uint64_t) - 1u))); + + memset(otp->storage, 0, otp_size); + + otp->data = (uint32_t *)(base + sizeof(struct otp_header)); + otp->ecc = (uint32_t *)(base + sizeof(struct otp_header) + data_size); + otp->ecc_bit_count = 0u; + otp->ecc_granule = 0u; + + if (s->blk) { + bool write = blk_supports_write_perm(s->blk); + uint64_t perm = BLK_PERM_CONSISTENT_READ | (write ? BLK_PERM_WRITE : 0); + if (blk_set_perm(s->blk, perm, perm, &error_fatal)) { + warn_report("%s: OTP backend is R/O", __func__); + write = false; + } + + int rc = blk_pread(s->blk, 0, (int64_t)otp_size, otp->storage, 0); + if (rc < 0) { + error_setg(&error_fatal, + "failed to read the initial OTP content: %d", rc); + return; + } + + const struct otp_header *otp_hdr = (const struct otp_header *)base; + + if (memcmp(otp_hdr->magic, "vOTP", sizeof(otp_hdr->magic)) != 0) { + error_setg(&error_fatal, "OTP file is not a valid OTP backend"); + return; + } + if (otp_hdr->version != 1u && otp_hdr->version != 2u) { + error_setg(&error_fatal, "OTP file version %u is not supported", + otp_hdr->version); + return; + } + + uintptr_t data_offset = otp_hdr->hlength + 8u; /* magic & length */ + uintptr_t ecc_offset = data_offset + otp_hdr->data_len; + + otp->data = (uint32_t *)(base + data_offset); + otp->ecc = (uint32_t *)(base + ecc_offset); + otp->ecc_bit_count = otp_hdr->eccbits; + otp->ecc_granule = otp_hdr->eccgran; + + if (otp->ecc_bit_count != 6u || !ot_otp_dj_is_ecc_enabled(s)) { + qemu_log_mask(LOG_UNIMP, + "%s: support for ECC %u/%u not implemented\n", + __func__, otp->ecc_granule, otp->ecc_bit_count); + } + + trace_ot_otp_load_backend(s->ot_id, otp_hdr->version, + write ? "R/W" : "R/O", otp->ecc_bit_count, + otp->ecc_granule); + + if (otp_hdr->version == 2u) { + /* + * Version 2 is deprecated and digest const/IV are now ignored. + * Nonetheless, keep checking for inconsistencies. + */ + if (s->digest_iv != ldq_le_p(otp_hdr->digest_iv)) { + error_report("%s: %s: OTP file digest IV mismatch", __func__, + s->ot_id); + } + if (memcmp(s->digest_const, otp_hdr->digest_constant, + sizeof(s->digest_const)) != 0) { + error_report("%s: %s: OTP file digest const mismatch", __func__, + s->ot_id); + } + } + } + + otp->data_size = data_size; + otp->ecc_size = ecc_size; + otp->size = otp_size; +} + +static void ot_otp_dj_pwr_load_hw_cfg(OtOTPDjState *s) +{ + OtOTPStorage *otp = s->otp; + OtOTPHWCfg *hw_cfg = s->hw_cfg; + + memcpy(hw_cfg->device_id, &otp->data[R_HW_CFG0_DEVICE_ID], + sizeof(*hw_cfg->device_id)); + memcpy(hw_cfg->manuf_state, &otp->data[R_HW_CFG0_MANUF_STATE], + sizeof(*hw_cfg->manuf_state)); + memcpy(&hw_cfg->soc_dbg_state[0], &otp->data[R_HW_CFG1_SOC_DBG_STATE], + sizeof(uint32_t)); + hw_cfg->en_sram_ifetch = (uint8_t)otp->data[R_HW_CFG1_EN_SRAM_IFETCH]; +} + +static void ot_otp_dj_pwr_load_tokens(OtOTPDjState *s) +{ + memset(s->tokens, 0, sizeof(*s->tokens)); + + const uint32_t *data = s->otp->data; + OtOTPTokens *tokens = s->tokens; + + static_assert(sizeof(OtOTPTokenValue) == 16u, "Invalid token size"); + + for (unsigned tkx = 0; tkx < OTP_TOKEN_COUNT; tkx++) { + unsigned partition; + uint32_t reg; + + switch (tkx) { + case OTP_TOKEN_TEST_UNLOCK: + partition = OTP_PART_SECRET0; + reg = R_SECRET0_TEST_UNLOCK_TOKEN; + break; + case OTP_TOKEN_TEST_EXIT: + partition = OTP_PART_SECRET0; + reg = R_SECRET0_TEST_EXIT_TOKEN; + break; + case OTP_TOKEN_RMA: + partition = OTP_PART_SECRET2; + reg = R_SECRET2_RMA_TOKEN; + break; + default: + g_assert_not_reached(); + break; + } + + OtOTPTokenValue value; + memcpy(&value, &data[reg], sizeof(OtOTPTokenValue)); + if (s->partctrls[partition].locked) { + tokens->values[tkx] = value; + tokens->valid_bm |= 1u << tkx; + } + trace_ot_otp_load_token(s->ot_id, OTP_TOKEN_NAME(tkx), tkx, value.hi, + value.lo, + (s->tokens->valid_bm & (1u << tkx)) ? "" : + "in"); + } +} + +static void ot_otp_dj_pwr_initialize_partitions(OtOTPDjState *s) +{ + for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { + if (ot_otp_dj_is_ecc_enabled(s) && OtOTPPartDescs[ix].integrity) { + if (ot_otp_dj_apply_ecc(s, ix)) { + continue; + } + } + + if (OtOTPPartDescs[ix].sw_digest) { + uint64_t digest = ot_otp_dj_get_part_digest(s, (int)ix); + s->partctrls[ix].locked = digest != 0; + continue; + } + + if (OtOTPPartDescs[ix].buffered) { + ot_otp_dj_bufferize_partition(s, ix); + if (OtOTPPartDescs[ix].hw_digest) { + ot_otp_dj_check_partition_integrity(s, ix); + } + continue; + } + } +} + static void ot_otp_dj_pwr_otp_bh(void *opaque) { OtOTPDjState *s = opaque; + /* + * This sequence is triggered from the Power Manager, in the early boot + * sequence while the OT IPs are maintained in reset. + * This means that all ot_otp_dj_pwr_* functions are called before the OTP + * IP is relased from reset. + * + * The QEMU reset is not a 1:1 mapping to the actual HW. + */ trace_ot_otp_pwr_otp_req(s->ot_id, "initialize"); - ot_otp_dj_initialize_partitions(s); - ot_otp_dj_load_hw_cfg(s); - ot_otp_dj_load_tokens(s); + ot_otp_dj_pwr_load(s); + ot_otp_dj_pwr_initialize_partitions(s); + ot_otp_dj_pwr_load_hw_cfg(s); + ot_otp_dj_pwr_load_tokens(s); ot_otp_dj_dai_init(s); ot_otp_dj_lci_init(s); @@ -3647,138 +3792,6 @@ static void ot_otp_dj_configure_sram(OtOTPDjState *s) s->sram_iv = ldq_le_p(sram_iv); } -static void ot_otp_dj_load(OtOTPDjState *s) -{ - /* - * HEADER_FORMAT - * - * | magic | 4 char | "vOFTP" | - * | hlength | uint32_t | count of header bytes after this point | - * | version | uint32_t | version of the header (v2) | - * | eccbits | uint16_t | ECC size in bits | - * | eccgran | uint16_t | ECC granule | - * | dlength | uint32_t | count of data bytes (% uint64_t) | - * | elength | uint32_t | count of ecc bytes (% uint64_t) | - * | -------- | ---------- | only in V2 | - * | dig_iv | 8 uint8_t | Present digest initialization vector | - * | dig_iv | 16 uint8_t | Present digest initialization vector | - */ - - struct otp_header { - char magic[4]; - uint32_t hlength; - uint32_t version; - uint16_t eccbits; - uint16_t eccgran; - uint32_t data_len; - uint32_t ecc_len; - /* added in V2 */ - uint8_t digest_iv[8u]; - uint8_t digest_constant[16u]; - }; - - static_assert(sizeof(struct otp_header) == 48u, "Invalid header size"); - - /* data following header should always be 64-bit aligned */ - static_assert((sizeof(struct otp_header) % sizeof(uint64_t)) == 0, - "invalid header definition"); - - size_t header_size = sizeof(struct otp_header); - size_t data_size = 0u; - size_t ecc_size = 0u; - - for (unsigned ix = 0u; ix < OTP_PART_COUNT; ix++) { - size_t psize = (size_t)OtOTPPartDescs[ix].size; - size_t dsize = ROUND_UP(psize, sizeof(uint64_t)); - data_size += dsize; - /* up to 1 ECC byte for 2 data bytes */ - ecc_size += DIV_ROUND_UP(dsize, 2u); - } - size_t otp_size = header_size + data_size + ecc_size; - - otp_size = ROUND_UP(otp_size, 4096u); - - OtOTPStorage *otp = s->otp; - - /* always allocates the requested size even if blk is NULL */ - otp->storage = blk_blockalign(s->blk, otp_size); - uintptr_t base = (uintptr_t)otp->storage; - g_assert(!(base & (sizeof(uint64_t) - 1u))); - - memset(otp->storage, 0, otp_size); - - otp->data = (uint32_t *)(base + sizeof(struct otp_header)); - otp->ecc = (uint32_t *)(base + sizeof(struct otp_header) + data_size); - otp->ecc_bit_count = 0u; - otp->ecc_granule = 0u; - - if (s->blk) { - bool write = blk_supports_write_perm(s->blk); - uint64_t perm = BLK_PERM_CONSISTENT_READ | (write ? BLK_PERM_WRITE : 0); - if (blk_set_perm(s->blk, perm, perm, &error_fatal)) { - warn_report("%s: OTP backend is R/O", __func__); - write = false; - } - - int rc = blk_pread(s->blk, 0, (int64_t)otp_size, otp->storage, 0); - if (rc < 0) { - error_setg(&error_fatal, - "failed to read the initial OTP content: %d", rc); - return; - } - - const struct otp_header *otp_hdr = (const struct otp_header *)base; - - if (memcmp(otp_hdr->magic, "vOTP", sizeof(otp_hdr->magic)) != 0) { - error_setg(&error_fatal, "OTP file is not a valid OTP backend"); - return; - } - if (otp_hdr->version != 1u && otp_hdr->version != 2u) { - error_setg(&error_fatal, "OTP file version %u is not supported", - otp_hdr->version); - return; - } - - uintptr_t data_offset = otp_hdr->hlength + 8u; /* magic & length */ - uintptr_t ecc_offset = data_offset + otp_hdr->data_len; - - otp->data = (uint32_t *)(base + data_offset); - otp->ecc = (uint32_t *)(base + ecc_offset); - otp->ecc_bit_count = otp_hdr->eccbits; - otp->ecc_granule = otp_hdr->eccgran; - - if (otp->ecc_bit_count != 6u || !ot_otp_dj_is_ecc_enabled(s)) { - qemu_log_mask(LOG_UNIMP, - "%s: support for ECC %u/%u not implemented\n", - __func__, otp->ecc_granule, otp->ecc_bit_count); - } - - trace_ot_otp_load_backend(s->ot_id, otp_hdr->version, - write ? "R/W" : "R/O", otp->ecc_bit_count, - otp->ecc_granule); - - if (otp_hdr->version == 2u) { - /* - * Version 2 is deprecated and digest const/IV are now ignored. - * Nonetheless, keep checking for inconsistencies. - */ - if (s->digest_iv != ldq_le_p(otp_hdr->digest_iv)) { - error_report("%s: %s: OTP file digest IV mismatch", __func__, - s->ot_id); - } - if (memcmp(s->digest_const, otp_hdr->digest_constant, - sizeof(s->digest_const)) != 0) { - error_report("%s: %s: OTP file digest const mismatch", __func__, - s->ot_id); - } - } - } - - otp->data_size = data_size; - otp->ecc_size = ecc_size; - otp->size = otp_size; -} - static Property ot_otp_dj_properties[] = { DEFINE_PROP_STRING("ot_id", OtOTPDjState, ot_id), DEFINE_PROP_DRIVE("drive", OtOTPDjState, blk), @@ -3814,6 +3827,26 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) OtOTPClass *c = OT_OTP_GET_CLASS(obj); OtOTPDjState *s = OT_OTP_DJ(obj); + /* + * Note: beware of the special reset sequence for the OTP controller, + * see comments from ot_otp_dj_pwr_otp_bh, as this very QEMU reset may be + * called after ot_otp_dj_pwr_otp_bh is invoked, hereby changing the usual + * realize-reset sequence. + * + * File back-end storage (loading) is processed from + * the ot_otp_dj_pwr_otp_bh handler, to ensure data are reloaded from the + * backend on each reset, prior to this very reset fuction. This reset + * function should not alter the storage content. + * + * Ideally the OTP reset functions should be decoupled from the regular + * IP reset, which are exercised automatically from the SoC, since all the + * OT SysBysDevice IPs are connected to the private system bus of the Ibex. + * This is by-design in QEMU. The reset management is already far too + * complex to create a special case for the OTP. Kind in mind that the OTP + * reset_enter/reset_exit functions are QEMU regular reset functions called + * as part of the private bus reset and do not represent the actual OTP HW + * reset. Part of this reset is handled in the Power Manager handler. + */ trace_ot_otp_reset(s->ot_id, "enter"); if (c->parent_phases.enter) { @@ -3906,7 +3939,6 @@ static void ot_otp_dj_realize(DeviceState *dev, Error **errp) ot_otp_dj_configure_scrmbl_key(s); ot_otp_dj_configure_digest(s); ot_otp_dj_configure_sram(s); - ot_otp_dj_load(s); } static void ot_otp_dj_init(Object *obj) From bd542356ee7db1e85924e3bcb104f867b26c751b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 15:43:44 +0200 Subject: [PATCH 28/69] [ot] hw/opentitan: ot_spi_device: emulate byte inversion in JEDEC ID. For some reason, the flash density byte is sent first in the current HW version. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_device.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 66ec896708d16..eb9a4b70fa600 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -935,8 +935,17 @@ static void ot_spi_device_flash_decode_read_jedec(OtSPIDeviceState *s) memset(f->buffer, (int)(uint8_t)cc_code, (size_t)cc_count); f->len = cc_count; f->buffer[f->len++] = jedec_manuf; - f->buffer[f->len++] = (uint8_t)(jedec_device >> 8u); + /* + * For some reason, OpenTitan device byte-swaps the device field. + * This is not documented, but the "flash density" field is sent first + * + * JEDEC_ID register: | 00 | MF | DEVICE_ID | content + * |31..24|23..16| 15 .. 0 | bits + * | B3 | B2 | B1 | B0 | bytes + * is sent in the following order: B2-B0-B1 + */ f->buffer[f->len++] = (uint8_t)(jedec_device >> 0u); + f->buffer[f->len++] = (uint8_t)(jedec_device >> 8u); memset(&f->buffer[f->len], (int)SPI_DEFAULT_TX_VALUE, SPI_FLASH_BUFFER_SIZE - f->len); f->src = f->buffer; From 10f69b1154962194ce9e22a0ab7cdc775a5d8332 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 10:44:42 +0200 Subject: [PATCH 29/69] [ot] hw/opentitan: ot_aes: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_aes.c | 56 ++++++++++++++++++++++++++++------- include/hw/opentitan/ot_aes.h | 4 +-- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_aes.c b/hw/opentitan/ot_aes.c index 4fde673f8ae86..63809ecd5f2e3 100644 --- a/hw/opentitan/ot_aes.c +++ b/hw/opentitan/ot_aes.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan AES device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -256,6 +256,11 @@ struct OtAESState { bool fast_mode; }; +struct OtAESClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #ifdef DEBUG_AES static const char *ot_aes_hexdump(OtAESState *s, const uint8_t *buf, size_t size) @@ -1284,18 +1289,18 @@ static const MemoryRegionOps ot_aes_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_aes_reset(DeviceState *dev) +static void ot_aes_reset_enter(Object *obj, ResetType type) { - OtAESState *s = OT_AES(dev); - OtAESRegisters *r = s->regs; + OtAESClass *c = OT_AES_GET_CLASS(obj); + OtAESState *s = OT_AES(obj); OtAESEDN *e = &s->edn; + OtAESRegisters *r = s->regs; - timer_del(s->retard_timer); - - g_assert(e->device); - g_assert(e->ep != UINT8_MAX); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } - s->prng = ot_prng_allocate(); + timer_del(s->retard_timer); memset(s->ctx, 0, sizeof(*s->ctx)); memset(r, 0, sizeof(*r)); @@ -1312,11 +1317,36 @@ static void ot_aes_reset(DeviceState *dev) for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { ibex_irq_set(&s->alerts[ix], 0); } +} + +static void ot_aes_reset_exit(Object *obj, ResetType type) +{ + OtAESClass *c = OT_AES_GET_CLASS(obj); + OtAESState *s = OT_AES(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + qemu_bh_cancel(s->process_bh); trace_ot_aes_reseed("reset"); ot_aes_handle_trigger(s); } +static void ot_aes_realize(DeviceState *dev, Error **errp) +{ + OtAESState *s = OT_AES(dev); + OtAESEDN *e = &s->edn; + + (void)errp; + + g_assert(e->device); + g_assert(e->ep != UINT8_MAX); + + s->prng = ot_prng_allocate(); +} + static void ot_aes_init(Object *obj) { OtAESState *s = OT_AES(obj); @@ -1349,9 +1379,14 @@ static void ot_aes_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_aes_reset); + dc->realize = &ot_aes_realize; device_class_set_props(dc, ot_aes_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtAESClass *ac = OT_AES_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_aes_reset_enter, NULL, + &ot_aes_reset_exit, &ac->parent_phases); } static const TypeInfo ot_aes_info = { @@ -1359,6 +1394,7 @@ static const TypeInfo ot_aes_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtAESState), .instance_init = &ot_aes_init, + .class_size = sizeof(OtAESClass), .class_init = &ot_aes_class_init, }; diff --git a/include/hw/opentitan/ot_aes.h b/include/hw/opentitan/ot_aes.h index fb15eed569d25..6b0cac34c8b09 100644 --- a/include/hw/opentitan/ot_aes.h +++ b/include/hw/opentitan/ot_aes.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan AES device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_AES "ot-aes" -OBJECT_DECLARE_SIMPLE_TYPE(OtAESState, OT_AES) +OBJECT_DECLARE_TYPE(OtAESState, OtAESClass, OT_AES) #endif /* HW_OPENTITAN_OT_AES_H */ From 921068b9aeaf2b1467480680e7e517dfccc0fcce Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 10:56:00 +0200 Subject: [PATCH 30/69] [ot] hw/opentitan: ot_alert: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_alert.c | 24 ++++++++++++++++++++---- include/hw/opentitan/ot_alert.h | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_alert.c b/hw/opentitan/ot_alert.c index 68de0eae932bc..5168737e3cded 100644 --- a/hw/opentitan/ot_alert.c +++ b/hw/opentitan/ot_alert.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Alert handler device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -237,6 +237,11 @@ struct OtAlertState { uint8_t n_classes; }; +struct OtAlertClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + /* clang-format off */ #define ST_NAME_ENTRY(_name_) [STATE_##_name_] = stringify(_name_) #define ST_NAME(_st_) ((_st_) < ARRAY_SIZE(ST_NAMES) ? ST_NAMES[(_st_)] : "?") @@ -973,11 +978,17 @@ static const MemoryRegionOps ot_alert_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_alert_reset(DeviceState *dev) +static void ot_alert_reset_enter(Object *obj, ResetType type) { - OtAlertState *s = OT_ALERT(dev); + OtAlertClass *c = OT_ALERT_GET_CLASS(obj); + OtAlertState *s = OT_ALERT(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } for (unsigned ix = 0; ix < s->n_classes; ix++) { + qemu_bh_cancel(s->schedulers[ix].esc_releaser); timer_del(&s->schedulers[ix].timer); } @@ -1150,16 +1161,21 @@ static void ot_alert_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_alert_reset); dc->realize = &ot_alert_realize; device_class_set_props(dc, ot_alert_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtAlertClass *ac = OT_ALERT_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_alert_reset_enter, NULL, NULL, + &ac->parent_phases); } static const TypeInfo ot_alert_info = { .name = TYPE_OT_ALERT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtAlertState), + .class_size = sizeof(OtAlertClass), .class_init = &ot_alert_class_init, }; diff --git a/include/hw/opentitan/ot_alert.h b/include/hw/opentitan/ot_alert.h index 8f93ed4c88194..c45d4af70da9c 100644 --- a/include/hw/opentitan/ot_alert.h +++ b/include/hw/opentitan/ot_alert.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Alert handler device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -33,7 +33,7 @@ #include "hw/sysbus.h" #define TYPE_OT_ALERT "ot-alert" -OBJECT_DECLARE_TYPE(OtAlertState, OtAlertStateClass, OT_ALERT) +OBJECT_DECLARE_TYPE(OtAlertState, OtAlertClass, OT_ALERT) #define OT_DEVICE_ALERT TYPE_OT_ALERT "-sig" #define OT_ALERT_ESCALATE TYPE_OT_ALERT "-esc" From 14b775d8cce40382075b6184f9e46d32d8b3314c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:06:52 +0200 Subject: [PATCH 31/69] [ot] hw/opentitan: ot_aon_timer: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_aon_timer.c | 29 ++++++++++++++++++++++------- include/hw/opentitan/ot_aon_timer.h | 4 ++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_aon_timer.c b/hw/opentitan/ot_aon_timer.c index 70b54c89a793c..b0b7a7ccd9d4a 100644 --- a/hw/opentitan/ot_aon_timer.c +++ b/hw/opentitan/ot_aon_timer.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan AON Timer device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -124,6 +124,11 @@ struct OtAonTimerState { uint32_t pclk; }; +struct OtAonTimerClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint64_t ot_aon_timer_ns_to_ticks(OtAonTimerState *s, uint32_t prescaler, int64_t ns) { @@ -510,11 +515,14 @@ static Property ot_aon_timer_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_aon_timer_reset(DeviceState *dev) +static void ot_aon_timer_reset_enter(Object *obj, ResetType type) { - OtAonTimerState *s = OT_AON_TIMER(dev); + OtAonTimerClass *c = OT_AON_TIMER_GET_CLASS(obj); + OtAonTimerState *s = OT_AON_TIMER(obj); - g_assert(s->pclk > 0); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->wkup_timer); timer_del(s->wdog_timer); @@ -529,11 +537,12 @@ static void ot_aon_timer_reset(DeviceState *dev) static void ot_aon_timer_realize(DeviceState *dev, Error **errp) { - (void)errp; - OtAonTimerState *s = OT_AON_TIMER(dev); + (void)errp; + g_assert(s->ot_id); + g_assert(s->pclk > 0); } static void ot_aon_timer_init(Object *obj) @@ -560,9 +569,14 @@ static void ot_aon_timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, ot_aon_timer_reset); dc->realize = ot_aon_timer_realize; device_class_set_props(dc, ot_aon_timer_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtAonTimerClass *ac = OT_AON_TIMER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_aon_timer_reset_enter, NULL, + NULL, &ac->parent_phases); } static const TypeInfo ot_aon_timer_info = { @@ -570,6 +584,7 @@ static const TypeInfo ot_aon_timer_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtAonTimerState), .instance_init = ot_aon_timer_init, + .class_size = sizeof(OtAonTimerClass), .class_init = ot_aon_timer_class_init, }; diff --git a/include/hw/opentitan/ot_aon_timer.h b/include/hw/opentitan/ot_aon_timer.h index 0a240acb533d3..7dea47b6133ec 100644 --- a/include/hw/opentitan/ot_aon_timer.h +++ b/include/hw/opentitan/ot_aon_timer.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan AON Timer device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_AON_TIMER "ot-aon_timer" -OBJECT_DECLARE_SIMPLE_TYPE(OtAonTimerState, OT_AON_TIMER) +OBJECT_DECLARE_TYPE(OtAonTimerState, OtAonTimerClass, OT_AON_TIMER) #define OT_AON_TIMER_WKUP TYPE_OT_AON_TIMER "-wkup" #define OT_AON_TIMER_BARK TYPE_OT_AON_TIMER "-bark" From 17d0f25f3d1540a99fa78212eaf34123e53cbc24 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:12:20 +0200 Subject: [PATCH 32/69] [ot] hw/opentitan: ot_ast_dj: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ast_dj.c | 33 +++++++++++++++++++++++++++++--- include/hw/opentitan/ot_ast_dj.h | 4 ++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/hw/opentitan/ot_ast_dj.c b/hw/opentitan/ot_ast_dj.c index 4dda91601ebd2..be89257d282f3 100644 --- a/hw/opentitan/ot_ast_dj.c +++ b/hw/opentitan/ot_ast_dj.c @@ -146,6 +146,11 @@ struct OtASTDjState { uint32_t *regsb; }; +struct OtASTDjClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define OT_AST_DJ_RANDOM_FILL_RATE_NS 1000000ull /* arbitrary: 1 ms */ /* -------------------------------------------------------------------------- */ @@ -345,11 +350,16 @@ static const MemoryRegionOps ot_ast_dj_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_ast_dj_reset(DeviceState *dev) +static void ot_ast_dj_reset_enter(Object *obj, ResetType type) { - OtASTDjState *s = OT_AST_DJ(dev); + OtASTDjClass *c = OT_AST_DJ_GET_CLASS(obj); + OtASTDjState *s = OT_AST_DJ(obj); OtASTDjRandom *rnd = &s->random; + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + timer_del(rnd->timer); memset(rnd->buffer, 0, OT_RANDOM_SRC_DWORD_COUNT * sizeof(uint64_t)); rnd->avail = false; @@ -395,6 +405,17 @@ static void ot_ast_dj_reset(DeviceState *dev) s->regsa[R_REGA36] = 0x24u; s->regsa[R_REGA37] = 0x25u; s->regsa[R_REGAL] = 0x26u; +} + +static void ot_ast_dj_reset_exit(Object *obj, ResetType type) +{ + OtASTDjClass *c = OT_AST_DJ_GET_CLASS(obj); + OtASTDjState *s = OT_AST_DJ(obj); + OtASTDjRandom *rnd = &s->random; + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); timer_mod(rnd->timer, (int64_t)(now + OT_AST_DJ_RANDOM_FILL_RATE_NS)); @@ -422,10 +443,15 @@ static void ot_ast_dj_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_ast_dj_reset); device_class_set_props(dc, ot_ast_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtASTDjClass *ac = OT_AST_DJ_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_ast_dj_reset_enter, NULL, + &ot_ast_dj_reset_exit, + &ac->parent_phases); + OtRandomSrcIfClass *rdc = OT_RANDOM_SRC_IF_CLASS(klass); rdc->get_random_values = &ot_ast_dj_get_random; } @@ -435,6 +461,7 @@ static const TypeInfo ot_ast_dj_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtASTDjState), .instance_init = &ot_ast_dj_init, + .class_size = sizeof(OtASTDjClass), .class_init = &ot_ast_dj_class_init, .interfaces = (InterfaceInfo[]){ diff --git a/include/hw/opentitan/ot_ast_dj.h b/include/hw/opentitan/ot_ast_dj.h index eebee46caeb31..cecda3ac17073 100644 --- a/include/hw/opentitan/ot_ast_dj.h +++ b/include/hw/opentitan/ot_ast_dj.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling Analog Sensor Top device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_AST_DJ "ot-ast-dj" -OBJECT_DECLARE_SIMPLE_TYPE(OtASTDjState, OT_AST_DJ) +OBJECT_DECLARE_TYPE(OtASTDjState, OtASTDjClass, OT_AST_DJ) #endif /* HW_OPENTITAN_OT_AST_DJ_H */ From 50615b18fa60353e70d4cc2dd19fea8745ab115a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:16:23 +0200 Subject: [PATCH 33/69] [ot] hw/opentitan: ot_ast_eg: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ast_eg.c | 23 +++++++++++++++++++---- include/hw/opentitan/ot_ast_eg.h | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_ast_eg.c b/hw/opentitan/ot_ast_eg.c index f996cac695722..9981f05f0c6f7 100644 --- a/hw/opentitan/ot_ast_eg.c +++ b/hw/opentitan/ot_ast_eg.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey Analog Sensor Top device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -136,6 +136,11 @@ struct OtASTEgState { uint32_t *regsb; }; +struct OtASTEgClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + /* -------------------------------------------------------------------------- */ /* Public API */ /* -------------------------------------------------------------------------- */ @@ -299,9 +304,14 @@ static const MemoryRegionOps ot_ast_eg_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_ast_eg_reset(DeviceState *dev) +static void ot_ast_eg_reset_enter(Object *obj, ResetType type) { - OtASTEgState *s = OT_AST_EG(dev); + OtASTEgClass *c = OT_AST_EG_GET_CLASS(obj); + OtASTEgState *s = OT_AST_EG(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(s->regsa, 0, REGSA_SIZE); memset(s->regsb, 0, REGSB_SIZE); @@ -363,9 +373,13 @@ static void ot_ast_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_ast_eg_reset); device_class_set_props(dc, ot_ast_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtASTEgClass *ac = OT_AST_EG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_ast_eg_reset_enter, NULL, NULL, + &ac->parent_phases); } static const TypeInfo ot_ast_eg_info = { @@ -373,6 +387,7 @@ static const TypeInfo ot_ast_eg_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtASTEgState), .instance_init = &ot_ast_eg_init, + .class_size = sizeof(OtASTEgClass), .class_init = &ot_ast_eg_class_init, }; diff --git a/include/hw/opentitan/ot_ast_eg.h b/include/hw/opentitan/ot_ast_eg.h index 6189009d172d2..04e297a60bd7b 100644 --- a/include/hw/opentitan/ot_ast_eg.h +++ b/include/hw/opentitan/ot_ast_eg.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey Analog Sensor Top device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_AST_EG "ot-ast-eg" -OBJECT_DECLARE_SIMPLE_TYPE(OtASTEgState, OT_AST_EG) +OBJECT_DECLARE_TYPE(OtASTEgState, OtASTEgClass, OT_AST_EG) #define OT_AST_EG_RANDOM_4BIT_RATE 50000u /* 50 kHz */ From b9379f8a9afc403d86dc2d8bdad0a95087407627 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:19:59 +0200 Subject: [PATCH 34/69] [ot] hw/opentitan: ot_clkmgr: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_clkmgr.c | 23 +++++++++++++++++++---- include/hw/opentitan/ot_clkmgr.h | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_clkmgr.c b/hw/opentitan/ot_clkmgr.c index 41739d5186bdc..2efb51977685f 100644 --- a/hw/opentitan/ot_clkmgr.c +++ b/hw/opentitan/ot_clkmgr.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Clock manager device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -197,6 +197,11 @@ struct OtClkMgrState { OtClkMgrRegisters sdw_regs; }; +struct OtClkMgrClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static const char *CLOCK_NAMES[OT_CLKMGR_HINT_COUNT] = { [OT_CLKMGR_HINT_AES] = "AES", [OT_CLKMGR_HINT_HMAC] = "HMAC", @@ -538,9 +543,14 @@ static const MemoryRegionOps ot_clkmgr_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_clkmgr_reset(DeviceState *dev) +static void ot_clkmgr_reset_enter(Object *obj, ResetType type) { - OtClkMgrState *s = OT_CLKMGR(dev); + OtClkMgrClass *c = OT_CLKMGR_GET_CLASS(obj); + OtClkMgrState *s = OT_CLKMGR(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(s->regs, 0, sizeof(s->regs)); @@ -590,9 +600,13 @@ static void ot_clkmgr_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_clkmgr_reset); device_class_set_props(dc, ot_clkmgr_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtClkMgrClass *cc = OT_CLKMGR_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_clkmgr_reset_enter, NULL, NULL, + &cc->parent_phases); } static const TypeInfo ot_clkmgr_info = { @@ -600,6 +614,7 @@ static const TypeInfo ot_clkmgr_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtClkMgrState), .instance_init = &ot_clkmgr_init, + .class_size = sizeof(OtClkMgrClass), .class_init = &ot_clkmgr_class_init, }; diff --git a/include/hw/opentitan/ot_clkmgr.h b/include/hw/opentitan/ot_clkmgr.h index 7aa37d98a9c25..be92509ea4ab0 100644 --- a/include/hw/opentitan/ot_clkmgr.h +++ b/include/hw/opentitan/ot_clkmgr.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Clock manager device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_CLKMGR "ot-clkmgr" -OBJECT_DECLARE_SIMPLE_TYPE(OtClkMgrState, OT_CLKMGR) +OBJECT_DECLARE_TYPE(OtClkMgrState, OtClkMgrClass, OT_CLKMGR) typedef enum { OT_CLKMGR_HINT_AES, From 6475533a6c0e01913f9f5ed0bb5abbbfcb72da3a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:23:19 +0200 Subject: [PATCH 35/69] [ot] hw/opentitan: ot_csrng: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 42 +++++++++++++++++++++++++++------ include/hw/opentitan/ot_csrng.h | 4 ++-- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index c78e500354f41..a1ff8f37cacc9 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Cryptographically Secure Random Number Generator * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -356,6 +356,11 @@ struct OtCSRNGState { OtOTPState *otp_ctrl; }; +struct OtCSRNGClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + /* clang-format off */ static const uint8_t OtCSRNGFsmStateCode[] = { [CSRNG_IDLE] = 0b01001110, // 0x4e: idle @@ -1959,17 +1964,24 @@ static const MemoryRegionOps ot_csrng_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_csrng_reset(DeviceState *dev) +static void ot_csrng_reset_enter(Object *obj, ResetType type) { - OtCSRNGState *s = OT_CSRNG(dev); + OtCSRNGClass *c = OT_CSRNG_GET_CLASS(obj); + OtCSRNGState *s = OT_CSRNG(obj); trace_ot_csrng_reset(); - g_assert(s->random_src); - OBJECT_CHECK(OtRandomSrcIf, s->random_src, TYPE_OT_RANDOM_SRC_IF); - g_assert(s->otp_ctrl); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + qemu_bh_cancel(s->cmd_scheduler); timer_del(s->entropy_scheduler); + for (unsigned ix = 0; ix < OT_CSRNG_HW_APP_MAX; ix++) { + g_assert(ix != SW_INSTANCE_ID); + OtCSRNGInstance *inst = &s->instances[ix]; + qemu_bh_cancel(inst->hw.filler_bh); + } s->entropy_delay = 0; memset(s->regs, 0, REGS_SIZE); @@ -2007,6 +2019,16 @@ static void ot_csrng_reset(DeviceState *dev) } } +static void ot_csrng_realize(DeviceState *dev, Error **errp) +{ + OtCSRNGState *s = OT_CSRNG(dev); + (void)errp; + + g_assert(s->random_src); + OBJECT_CHECK(OtRandomSrcIf, s->random_src, TYPE_OT_RANDOM_SRC_IF); + g_assert(s->otp_ctrl); +} + static void ot_csrng_init(Object *obj) { OtCSRNGState *s = OT_CSRNG(obj); @@ -2061,9 +2083,14 @@ static void ot_csrng_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_csrng_reset); + dc->realize = &ot_csrng_realize; device_class_set_props(dc, ot_csrng_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtCSRNGClass *cc = OT_CSRNG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_csrng_reset_enter, NULL, NULL, + &cc->parent_phases); } static const TypeInfo ot_csrng_info = { @@ -2071,6 +2098,7 @@ static const TypeInfo ot_csrng_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtCSRNGState), .instance_init = &ot_csrng_init, + .class_size = sizeof(OtCSRNGClass), .class_init = &ot_csrng_class_init, }; diff --git a/include/hw/opentitan/ot_csrng.h b/include/hw/opentitan/ot_csrng.h index 5e609f773f466..4c2722269b7b0 100644 --- a/include/hw/opentitan/ot_csrng.h +++ b/include/hw/opentitan/ot_csrng.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Cryptographically Secure Random Number Generator * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -33,7 +33,7 @@ #include "hw/registerfields.h" #define TYPE_OT_CSRNG "ot-csrng" -OBJECT_DECLARE_SIMPLE_TYPE(OtCSRNGState, OT_CSRNG) +OBJECT_DECLARE_TYPE(OtCSRNGState, OtCSRNGClass, OT_CSRNG) #define OT_CSRNG_HW_APP_MAX 2u From b01033d3c4858bae56e40b8db299d9a082530bcc Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:27:26 +0200 Subject: [PATCH 36/69] [ot] hw/opentitan: ot_dev_proxy: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 23 +++++++++++++++++++---- include/hw/opentitan/ot_dev_proxy.h | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index 03b4a1f67ea9c..95bd603153bf6 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Device Proxy * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -150,6 +150,11 @@ struct OtDevProxyState { guint watch_tag; /* tracker for comm device change */ }; +struct OtDevProxyClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + typedef void ot_dev_proxy_register_device_fn(GArray *array, Object *obj); typedef struct { @@ -1887,9 +1892,14 @@ static Property ot_dev_proxy_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_dev_proxy_reset(DeviceState *dev) +static void ot_dev_proxy_reset_enter(Object *obj, ResetType type) { - OtDevProxyState *s = OT_DEV_PROXY(dev); + OtDevProxyClass *c = OT_DEV_PROXY_GET_CLASS(obj); + OtDevProxyState *s = OT_DEV_PROXY(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } if (!s->items) { /* only done once */ @@ -1926,10 +1936,14 @@ static void ot_dev_proxy_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_dev_proxy_reset); dc->realize = &ot_dev_proxy_realize; device_class_set_props(dc, ot_dev_proxy_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtDevProxyClass *pc = OT_DEV_PROXY_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_dev_proxy_reset_enter, NULL, + NULL, &pc->parent_phases); } static const TypeInfo ot_dev_proxy_info = { @@ -1937,6 +1951,7 @@ static const TypeInfo ot_dev_proxy_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(OtDevProxyState), .instance_init = &ot_dev_proxy_init, + .class_size = sizeof(OtDevProxyClass), .class_init = &ot_dev_proxy_class_init, }; diff --git a/include/hw/opentitan/ot_dev_proxy.h b/include/hw/opentitan/ot_dev_proxy.h index 78a957f90ece7..90b55a840df96 100644 --- a/include/hw/opentitan/ot_dev_proxy.h +++ b/include/hw/opentitan/ot_dev_proxy.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Device Proxy * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_DEV_PROXY "ot-dev_proxy" -OBJECT_DECLARE_SIMPLE_TYPE(OtDevProxyState, OT_DEV_PROXY) +OBJECT_DECLARE_TYPE(OtDevProxyState, OtDevProxyClass, OT_DEV_PROXY) #define TYPE_OT_DEV_PROXY_WATCHER "ot-dev_proxy_watcher" OBJECT_DECLARE_SIMPLE_TYPE(OtDevProxyWatcherState, OT_DEV_PROXY_WATCHER) From da428d9ca584914f83c3baac32238737237fadd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:32:42 +0200 Subject: [PATCH 37/69] [ot] hw/opentitan: ot_dm_tl: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dm_tl.c | 33 ++++++++++++++++++++++++--------- include/hw/opentitan/ot_dm_tl.h | 4 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_dm_tl.c b/hw/opentitan/ot_dm_tl.c index ff2785ee98524..b8cf13b10eeed 100644 --- a/hw/opentitan/ot_dm_tl.c +++ b/hw/opentitan/ot_dm_tl.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Debug Module to TileLink bridge * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -57,6 +57,11 @@ struct OtDMTLState { uint8_t role; }; +struct OtDMTLClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + /* -------------------------------------------------------------------------- */ /* DTM interface implementation */ /* -------------------------------------------------------------------------- */ @@ -142,30 +147,35 @@ static Property ot_dm_tl_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_dm_tl_reset(DeviceState *dev) +static void ot_dm_tl_reset_exit(Object *obj, ResetType type) { - OtDMTLState *dmtl = OT_DM_TL(dev); + OtDMTLClass *c = OT_DM_TL_GET_CLASS(obj); + OtDMTLState *dmtl = OT_DM_TL(obj); g_assert(dmtl->dtm != NULL); g_assert(dmtl->dmi_size); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + if (!dmtl->dtm_ok) { RISCVDTMClass *dtmc = RISCV_DTM_GET_CLASS(OBJECT(dmtl->dtm)); dmtl->dtm_ok = - (*dtmc->register_dm)(DEVICE(dmtl->dtm), RISCV_DEBUG_DEVICE(dev), + (*dtmc->register_dm)(DEVICE(dmtl->dtm), RISCV_DEBUG_DEVICE(obj), dmtl->dmi_addr, dmtl->dmi_size, dmtl->enable); trace_ot_dm_tl_register(dmtl->dev_name, dmtl->dmi_addr, dmtl->dmi_size, dmtl->enable, dmtl->dtm_ok); } if (dmtl->dtm_ok) { - Object *soc = OBJECT(dev)->parent; - Object *obj; + Object *soc = obj->parent; + Object *as; OtAddressSpaceState *oas; g_assert(dmtl->tl_as_name); - obj = object_property_get_link(soc, dmtl->tl_as_name, &error_fatal); - oas = OBJECT_CHECK(OtAddressSpaceState, obj, TYPE_OT_ADDRESS_SPACE); + as = object_property_get_link(soc, dmtl->tl_as_name, &error_fatal); + oas = OBJECT_CHECK(OtAddressSpaceState, as, TYPE_OT_ADDRESS_SPACE); dmtl->as = ot_address_space_get(oas); g_assert(dmtl->as); } @@ -205,11 +215,15 @@ static void ot_dm_tl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_dm_tl_reset); dc->realize = &ot_dm_tl_realize; device_class_set_props(dc, ot_dm_tl_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtDMTLClass *tc = OT_DM_TL_CLASS(klass); + resettable_class_set_parent_phases(rc, NULL, NULL, &ot_dm_tl_reset_exit, + &tc->parent_phases); + RISCVDebugDeviceClass *dmc = RISCV_DEBUG_DEVICE_CLASS(klass); dmc->write_rq = &ot_dm_tl_write_rq; dmc->read_rq = &ot_dm_tl_read_rq; @@ -221,6 +235,7 @@ static const TypeInfo ot_dm_tl_info = { .parent = TYPE_RISCV_DEBUG_DEVICE, .instance_init = &ot_dm_tl_init, .instance_size = sizeof(OtDMTLState), + .class_size = sizeof(OtDMTLClass), .class_init = &ot_dm_tl_class_init, }; diff --git a/include/hw/opentitan/ot_dm_tl.h b/include/hw/opentitan/ot_dm_tl.h index 55e0c9acb0ebe..46c104d56e012 100644 --- a/include/hw/opentitan/ot_dm_tl.h +++ b/include/hw/opentitan/ot_dm_tl.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Debug Module to TileLink bridge * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_DM_TL "ot-dm_tl" -OBJECT_DECLARE_SIMPLE_TYPE(OtDMTLState, OT_DM_TL) +OBJECT_DECLARE_TYPE(OtDMTLState, OtDMTLClass, OT_DM_TL) #endif /* HW_OPENTITAN_OT_DM_TL_H */ From b9f2cc47a5c2dc6922609e9bdb4da8b4ab296787 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:41:07 +0200 Subject: [PATCH 38/69] [ot] hw/opentitan: ot_dma: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 78 +++++++++++++++++++++++------------ include/hw/opentitan/ot_dma.h | 4 +- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 2c441b7130fae..c1a4c0429ad93 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -271,6 +271,11 @@ struct OtDMAState { #endif }; +struct OtDMAClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) #define R_LAST_REG (R_INT_SRC_WR_VAL_10) @@ -1342,38 +1347,20 @@ static const MemoryRegionOps ot_dma_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_dma_reset(DeviceState *dev) +static void ot_dma_reset_enter(Object *obj, ResetType type) { - OtDMAState *s = OT_DMA(dev); + OtDMAClass *c = OT_DMA_GET_CLASS(obj); + OtDMAState *s = OT_DMA(obj); g_assert(s->ot_id); - timer_del(s->timer); - - Object *soc = OBJECT(dev)->parent; - - Object *obj; - OtAddressSpaceState *oas; - - CHANGE_STATE(s, IDLE); - - if (!s->ases[AS_OT]) { - obj = object_property_get_link(soc, s->ot_as_name, &error_fatal); - oas = OBJECT_CHECK(OtAddressSpaceState, obj, TYPE_OT_ADDRESS_SPACE); - s->ases[AS_OT] = ot_address_space_get(oas); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); } - if (!s->ases[AS_CTN] && s->ctn_as_name) { - obj = object_property_get_link(soc, s->ctn_as_name, &error_fatal); - oas = OBJECT_CHECK(OtAddressSpaceState, obj, TYPE_OT_ADDRESS_SPACE); - s->ases[AS_CTN] = ot_address_space_get(oas); - } + timer_del(s->timer); - if (!s->ases[AS_SYS] && s->sys_as_name) { - obj = object_property_get_link(soc, s->sys_as_name, &error_fatal); - oas = OBJECT_CHECK(OtAddressSpaceState, obj, TYPE_OT_ADDRESS_SPACE); - s->ases[AS_SYS] = ot_address_space_get(oas); - } + CHANGE_STATE(s, IDLE); memset(s->regs, 0, REGS_SIZE); memset(&s->control, 0, sizeof(s->control)); @@ -1391,6 +1378,40 @@ static void ot_dma_reset(DeviceState *dev) } } +static void ot_dma_reset_exit(Object *obj, ResetType type) +{ + OtDMAClass *c = OT_DMA_GET_CLASS(obj); + OtDMAState *s = OT_DMA(obj); + + g_assert(s->ot_id); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + Object *soc = obj->parent; + Object *as; + OtAddressSpaceState *oas; + + if (!s->ases[AS_OT]) { + as = object_property_get_link(soc, s->ot_as_name, &error_fatal); + oas = OBJECT_CHECK(OtAddressSpaceState, as, TYPE_OT_ADDRESS_SPACE); + s->ases[AS_OT] = ot_address_space_get(oas); + } + + if (!s->ases[AS_CTN] && s->ctn_as_name) { + as = object_property_get_link(soc, s->ctn_as_name, &error_fatal); + oas = OBJECT_CHECK(OtAddressSpaceState, as, TYPE_OT_ADDRESS_SPACE); + s->ases[AS_CTN] = ot_address_space_get(oas); + } + + if (!s->ases[AS_SYS] && s->sys_as_name) { + as = object_property_get_link(soc, s->sys_as_name, &error_fatal); + oas = OBJECT_CHECK(OtAddressSpaceState, as, TYPE_OT_ADDRESS_SPACE); + s->ases[AS_SYS] = ot_address_space_get(oas); + } +} + static void ot_dma_realize(DeviceState *dev, Error **errp) { OtDMAState *s = OT_DMA(dev); @@ -1431,9 +1452,13 @@ static void ot_dma_class_init(ObjectClass *klass, void *data) (void)data; dc->realize = &ot_dma_realize; - device_class_set_legacy_reset(dc, &ot_dma_reset); device_class_set_props(dc, ot_dma_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtDMAClass *mc = OT_DMA_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_dma_reset_enter, NULL, + &ot_dma_reset_exit, &mc->parent_phases); } static const TypeInfo ot_dma_info = { @@ -1441,6 +1466,7 @@ static const TypeInfo ot_dma_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtDMAState), .instance_init = &ot_dma_init, + .class_size = sizeof(OtDMAClass), .class_init = &ot_dma_class_init, }; diff --git a/include/hw/opentitan/ot_dma.h b/include/hw/opentitan/ot_dma.h index ff6b8d1a57e4b..ac5afc92a8d54 100644 --- a/include/hw/opentitan/ot_dma.h +++ b/include/hw/opentitan/ot_dma.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan DMA device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_DMA "ot-dma" -OBJECT_DECLARE_SIMPLE_TYPE(OtDMAState, OT_DMA) +OBJECT_DECLARE_TYPE(OtDMAState, OtDMAClass, OT_DMA) #endif /* HW_OPENTITAN_OT_DMA_H */ From 9ddd9ec174349fbd3f5ad7f09904e890e82a13b8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 11:50:37 +0200 Subject: [PATCH 39/69] [ot] hw/opentitan: ot_edn: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_edn.c | 44 ++++++++++++++++++++++------------- include/hw/opentitan/ot_edn.h | 2 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index 1c355501c1dc3..44f2345eee91c 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Entropy Distribution Network device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -231,11 +231,6 @@ typedef struct OtEDNEndPoint { typedef QSIMPLEQ_HEAD(OtEndpointQueue, OtEDNEndPoint) OtEndpointQueue; -struct OtEDNClass { - SysBusDeviceClass parent_class; - ResettablePhases parent_phases; -}; - struct OtEDNState { SysBusDevice parent_obj; @@ -255,6 +250,11 @@ struct OtEDNState { bool sw_cmd_ready; /* ready to receive command in SW port mode */ }; +struct OtEDNClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static const uint16_t OtEDNFsmStateCode[] = { [EDN_IDLE] = 0b011000001, [EDN_BOOT_LOAD_INS] = 0b111000111, @@ -1492,16 +1492,18 @@ static const MemoryRegionOps ot_edn_regs_ops = { static void ot_edn_reset_enter(Object *obj, ResetType type) { - OtEDNClass *k = OT_EDN_GET_CLASS(obj); + OtEDNClass *c = OT_EDN_GET_CLASS(obj); OtEDNState *s = OT_EDN(obj); - OtEDNCSRNG *c = &s->rng; + OtEDNCSRNG *r = &s->rng; trace_ot_edn_reset(s->rng.appid, "enter"); - if (k->parent_phases.enter) { - k->parent_phases.enter(obj, type); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); } + qemu_bh_cancel(s->ep_bh); + memset(s->regs, 0, REGS_SIZE); s->regs[R_REGWEN] = 0x1u; s->regs[R_CTRL] = 0x9999u; @@ -1511,13 +1513,22 @@ static void ot_edn_reset_enter(Object *obj, ResetType type) ot_edn_clean_up(s, true); ot_edn_change_state(s, EDN_IDLE); + /* do not reset connection info since reset order is not known */ + (void)r->genbits_ready; + ot_fifo32_reset(&r->cmd_gen_fifo); + ot_fifo32_reset(&r->cmd_reseed_fifo); +} + +static void ot_edn_realize(DeviceState *dev, Error **errp) +{ + OtEDNState *s = OT_EDN(dev); + OtEDNCSRNG *r = &s->rng; - ot_fifo32_reset(&c->cmd_gen_fifo); - ot_fifo32_reset(&c->cmd_reseed_fifo); + (void)errp; /* check that properties have been initialized */ - g_assert(c->device); - g_assert(c->appid < OT_CSRNG_HW_APP_MAX); + g_assert(r->device); + g_assert(r->appid < OT_CSRNG_HW_APP_MAX); } static void ot_edn_init(Object *obj) @@ -1558,13 +1569,14 @@ static void ot_edn_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; + dc->realize = &ot_edn_realize; device_class_set_props(dc, ot_edn_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); ResettableClass *rc = RESETTABLE_CLASS(klass); - OtEDNClass *uc = OT_EDN_CLASS(klass); + OtEDNClass *ec = OT_EDN_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_edn_reset_enter, NULL, NULL, - &uc->parent_phases); + &ec->parent_phases); } static const TypeInfo ot_edn_info = { diff --git a/include/hw/opentitan/ot_edn.h b/include/hw/opentitan/ot_edn.h index 679ca01ca993c..888deee4b2007 100644 --- a/include/hw/opentitan/ot_edn.h +++ b/include/hw/opentitan/ot_edn.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Entropy Distribution Network device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): From a203f8fd31180406bbbef439b9db72043e953ecb Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:00:22 +0200 Subject: [PATCH 40/69] [ot] hw/opentitan: ot_entropy_src: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_entropy_src.c | 38 +++++++++++++++++++++------ include/hw/opentitan/ot_entropy_src.h | 4 +-- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index 4276cd7011169..367cda7083b63 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Entropy Source device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -405,6 +405,11 @@ struct OtEntropySrcState { OtOTPState *otp_ctrl; }; +struct OtEntropySrcClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static const uint16_t OtEDNFsmStateCode[] = { [ENTROPY_SRC_IDLE] = 0b011110101, [ENTROPY_SRC_BOOT_HT_RUNNING] = 0b111010010, @@ -469,7 +474,6 @@ static bool ot_entropy_src_is_module_enabled(const OtEntropySrcState *s); static bool ot_entropy_src_is_fips_enabled(const OtEntropySrcState *s); static bool ot_entropy_src_is_hw_route(const OtEntropySrcState *s); static bool ot_entropy_src_is_fips_capable(const OtEntropySrcState *s); -static void ot_entropy_src_reset(DeviceState *dev); static void ot_entropy_src_update_alerts(OtEntropySrcState *s); static void ot_entropy_src_update_filler(OtEntropySrcState *s); @@ -1326,7 +1330,7 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, CHECK_MULTIBOOT(s, MODULE_ENABLE, MODULE_ENABLE); if (ot_entropy_src_is_module_disabled(s)) { /* reset takes care of cancelling the scheduler timer */ - ot_entropy_src_reset(DEVICE(s)); + resettable_reset(OBJECT(s), RESET_TYPE_COLD); break; } if ((old ^ s->regs[reg]) && ot_entropy_src_is_module_enabled(s)) { @@ -1547,14 +1551,16 @@ static const MemoryRegionOps ot_entropy_src_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_entropy_src_reset(DeviceState *dev) +static void ot_entropy_src_reset_enter(Object *obj, ResetType type) { - OtEntropySrcState *s = OT_ENTROPY_SRC(dev); + OtEntropySrcClass *c = OT_ENTROPY_SRC_GET_CLASS(obj); + OtEntropySrcState *s = OT_ENTROPY_SRC(obj); trace_ot_entropy_src_reset(); - g_assert(s->ast); - g_assert(s->otp_ctrl); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->scheduler); @@ -1607,6 +1613,16 @@ static void ot_entropy_src_reset(DeviceState *dev) ot_entropy_src_change_state(s, ENTROPY_SRC_IDLE); } +static void ot_entropy_src_realize(DeviceState *dev, Error **errp) +{ + (void)errp; + + OtEntropySrcState *s = OT_ENTROPY_SRC(dev); + + g_assert(s->ast); + g_assert(s->otp_ctrl); +} + static void ot_entropy_src_init(Object *obj) { OtEntropySrcState *s = OT_ENTROPY_SRC(obj); @@ -1638,10 +1654,15 @@ static void ot_entropy_src_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_entropy_src_reset); + dc->realize = &ot_entropy_src_realize; device_class_set_props(dc, ot_entropy_src_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtEntropySrcClass *ec = OT_ENTROPY_SRC_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_entropy_src_reset_enter, NULL, + NULL, &ec->parent_phases); + OtRandomSrcIfClass *rdc = OT_RANDOM_SRC_IF_CLASS(klass); rdc->get_random_values = &ot_entropy_src_get_random; } @@ -1651,6 +1672,7 @@ static const TypeInfo ot_entropy_src_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtEntropySrcState), .instance_init = &ot_entropy_src_init, + .class_size = sizeof(OtEntropySrcClass), .class_init = &ot_entropy_src_class_init, .interfaces = (InterfaceInfo[]){ diff --git a/include/hw/opentitan/ot_entropy_src.h b/include/hw/opentitan/ot_entropy_src.h index f8bf089cc690a..3802fc1cbc3bc 100644 --- a/include/hw/opentitan/ot_entropy_src.h +++ b/include/hw/opentitan/ot_entropy_src.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Entropy Source device * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_ENTROPY_SRC "ot-entropy_src" -OBJECT_DECLARE_SIMPLE_TYPE(OtEntropySrcState, OT_ENTROPY_SRC) +OBJECT_DECLARE_TYPE(OtEntropySrcState, OtEntropySrcClass, OT_ENTROPY_SRC) #endif /* HW_OPENTITAN_OT_ENTROPY_SRC_H */ From 687ac86d21ee277141ab7c4169364489f50a882d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:09:48 +0200 Subject: [PATCH 41/69] [ot] hw/opentitan: ot_flash: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_flash.c | 211 +++++++++++++++++--------------- include/hw/opentitan/ot_flash.h | 4 +- 2 files changed, 115 insertions(+), 100 deletions(-) diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index 2824961d81da9..2a444cad66cca 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Flash controller device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -766,6 +766,11 @@ struct OtFlashState { bool no_mem_prot; /* Flag to disable mem protection features */ }; +struct OtFlashClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_flash_update_irqs(OtFlashState *s) { uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -2245,102 +2250,6 @@ static const MemoryRegionOps ot_flash_csrs_ops = { .impl.max_access_size = 4u, }; -static void ot_flash_reset(DeviceState *dev) -{ - OtFlashState *s = OT_FLASH(dev); - - timer_del(s->op_delay); - s->op.kind = OP_NONE; - - memset(s->regs, 0, REGS_SIZE); - s->regs[R_INTR_STATE] = 0x3u; - s->regs[R_DIS] = 0x9u; - s->regs[R_CTRL_REGWEN] = 0x1u; - s->regs[R_PROG_TYPE_EN] = 0x3u; - s->regs[R_REGION_CFG_REGWEN_0] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_1] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_2] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_3] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_4] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_5] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_6] = 0x1u; - s->regs[R_REGION_CFG_REGWEN_7] = 0x1u; - s->regs[R_MP_REGION_CFG_0] = 0x9999999u; - s->regs[R_MP_REGION_CFG_1] = 0x9999999u; - s->regs[R_MP_REGION_CFG_2] = 0x9999999u; - s->regs[R_MP_REGION_CFG_3] = 0x9999999u; - s->regs[R_MP_REGION_CFG_4] = 0x9999999u; - s->regs[R_MP_REGION_CFG_5] = 0x9999999u; - s->regs[R_MP_REGION_CFG_6] = 0x9999999u; - s->regs[R_MP_REGION_CFG_7] = 0x9999999u; - s->regs[R_DEFAULT_REGION] = 0x999999u; - s->regs[R_BANK0_INFO0_REGWEN_0] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_1] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_2] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_3] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_4] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_5] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_6] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_7] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_8] = 0x1u; - s->regs[R_BANK0_INFO0_REGWEN_9] = 0x1u; - s->regs[R_BANK0_INFO0_PAGE_CFG_0] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_1] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_2] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_3] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_4] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_5] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_6] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_7] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_8] = 0x9999999u; - s->regs[R_BANK0_INFO0_PAGE_CFG_9] = 0x9999999u; - s->regs[R_BANK0_INFO1_REGWEN] = 0x1u; - s->regs[R_BANK0_INFO1_PAGE_CFG] = 0x9999999u; - s->regs[R_BANK0_INFO2_REGWEN_0] = 0x1u; - s->regs[R_BANK0_INFO2_REGWEN_1] = 0x1u; - s->regs[R_BANK0_INFO2_PAGE_CFG_0] = 0x9999999u; - s->regs[R_BANK0_INFO2_PAGE_CFG_1] = 0x9999999u; - s->regs[R_BANK1_INFO0_REGWEN_0] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_1] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_2] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_3] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_4] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_5] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_6] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_7] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_8] = 0x1u; - s->regs[R_BANK1_INFO0_REGWEN_9] = 0x1u; - s->regs[R_BANK1_INFO0_PAGE_CFG_0] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_1] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_2] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_3] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_4] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_5] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_6] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_7] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_8] = 0x9999999u; - s->regs[R_BANK1_INFO0_PAGE_CFG_9] = 0x9999999u; - s->regs[R_BANK1_INFO1_REGWEN] = 0x1u; - s->regs[R_BANK1_INFO1_PAGE_CFG] = 0x9999999u; - s->regs[R_BANK1_INFO2_REGWEN_0] = 0x1u; - s->regs[R_BANK1_INFO2_REGWEN_1] = 0x1u; - s->regs[R_BANK1_INFO2_PAGE_CFG_0] = 0x9999999u; - s->regs[R_BANK1_INFO2_PAGE_CFG_1] = 0x9999999u; - s->regs[R_HW_INFO_CFG_OVERRIDE] = 0x99u; - s->regs[R_BANK_CFG_REGWEN] = 0x1u; - s->regs[R_STATUS] = 0xau; - s->regs[R_PHY_STATUS] = 0x6u; - s->regs[R_FIFO_LVL] = 0xf0fu; - - s->csrs[R_CSR0_REGWEN] = 0x1u; - - ot_flash_update_irqs(s); - ot_flash_update_alerts(s); - - ot_flash_reset_rd_fifo(s); - ot_flash_reset_prog_fifo(s); -} - #ifdef USE_HEXDUMP static char dbg_hexbuf[256]; static const char *ot_flash_hexdump(const uint8_t *buf, size_t size) @@ -2571,6 +2480,107 @@ static const MemoryRegionOps ot_flash_mem_ops = { #endif #endif /* DATA_PART_USE_IO_OPS */ +static void ot_flash_reset_enter(Object *obj, ResetType type) +{ + OtFlashClass *c = OT_FLASH_GET_CLASS(obj); + OtFlashState *s = OT_FLASH(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + timer_del(s->op_delay); + s->op.kind = OP_NONE; + + memset(s->regs, 0, REGS_SIZE); + s->regs[R_INTR_STATE] = 0x3u; + s->regs[R_DIS] = 0x9u; + s->regs[R_CTRL_REGWEN] = 0x1u; + s->regs[R_PROG_TYPE_EN] = 0x3u; + s->regs[R_REGION_CFG_REGWEN_0] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_1] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_2] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_3] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_4] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_5] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_6] = 0x1u; + s->regs[R_REGION_CFG_REGWEN_7] = 0x1u; + s->regs[R_MP_REGION_CFG_0] = 0x9999999u; + s->regs[R_MP_REGION_CFG_1] = 0x9999999u; + s->regs[R_MP_REGION_CFG_2] = 0x9999999u; + s->regs[R_MP_REGION_CFG_3] = 0x9999999u; + s->regs[R_MP_REGION_CFG_4] = 0x9999999u; + s->regs[R_MP_REGION_CFG_5] = 0x9999999u; + s->regs[R_MP_REGION_CFG_6] = 0x9999999u; + s->regs[R_MP_REGION_CFG_7] = 0x9999999u; + s->regs[R_DEFAULT_REGION] = 0x999999u; + s->regs[R_BANK0_INFO0_REGWEN_0] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_1] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_2] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_3] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_4] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_5] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_6] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_7] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_8] = 0x1u; + s->regs[R_BANK0_INFO0_REGWEN_9] = 0x1u; + s->regs[R_BANK0_INFO0_PAGE_CFG_0] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_1] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_2] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_3] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_4] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_5] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_6] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_7] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_8] = 0x9999999u; + s->regs[R_BANK0_INFO0_PAGE_CFG_9] = 0x9999999u; + s->regs[R_BANK0_INFO1_REGWEN] = 0x1u; + s->regs[R_BANK0_INFO1_PAGE_CFG] = 0x9999999u; + s->regs[R_BANK0_INFO2_REGWEN_0] = 0x1u; + s->regs[R_BANK0_INFO2_REGWEN_1] = 0x1u; + s->regs[R_BANK0_INFO2_PAGE_CFG_0] = 0x9999999u; + s->regs[R_BANK0_INFO2_PAGE_CFG_1] = 0x9999999u; + s->regs[R_BANK1_INFO0_REGWEN_0] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_1] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_2] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_3] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_4] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_5] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_6] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_7] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_8] = 0x1u; + s->regs[R_BANK1_INFO0_REGWEN_9] = 0x1u; + s->regs[R_BANK1_INFO0_PAGE_CFG_0] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_1] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_2] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_3] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_4] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_5] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_6] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_7] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_8] = 0x9999999u; + s->regs[R_BANK1_INFO0_PAGE_CFG_9] = 0x9999999u; + s->regs[R_BANK1_INFO1_REGWEN] = 0x1u; + s->regs[R_BANK1_INFO1_PAGE_CFG] = 0x9999999u; + s->regs[R_BANK1_INFO2_REGWEN_0] = 0x1u; + s->regs[R_BANK1_INFO2_REGWEN_1] = 0x1u; + s->regs[R_BANK1_INFO2_PAGE_CFG_0] = 0x9999999u; + s->regs[R_BANK1_INFO2_PAGE_CFG_1] = 0x9999999u; + s->regs[R_HW_INFO_CFG_OVERRIDE] = 0x99u; + s->regs[R_BANK_CFG_REGWEN] = 0x1u; + s->regs[R_STATUS] = 0xau; + s->regs[R_PHY_STATUS] = 0x6u; + s->regs[R_FIFO_LVL] = 0xf0fu; + + s->csrs[R_CSR0_REGWEN] = 0x1u; + + ot_flash_update_irqs(s); + ot_flash_update_alerts(s); + + ot_flash_reset_rd_fifo(s); + ot_flash_reset_prog_fifo(s); +} + static void ot_flash_realize(DeviceState *dev, Error **errp) { OtFlashState *s = OT_FLASH(dev); @@ -2625,10 +2635,14 @@ static void ot_flash_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_flash_reset); dc->realize = &ot_flash_realize; device_class_set_props(dc, ot_flash_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtFlashClass *fc = OT_FLASH_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_flash_reset_enter, NULL, NULL, + &fc->parent_phases); } static const TypeInfo ot_flash_info = { @@ -2636,6 +2650,7 @@ static const TypeInfo ot_flash_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtFlashState), .instance_init = &ot_flash_init, + .class_size = sizeof(OtFlashClass), .class_init = &ot_flash_class_init, }; diff --git a/include/hw/opentitan/ot_flash.h b/include/hw/opentitan/ot_flash.h index 3b74409889166..59e483bc5230f 100644 --- a/include/hw/opentitan/ot_flash.h +++ b/include/hw/opentitan/ot_flash.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Flash controller device * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_FLASH "ot-flash" -OBJECT_DECLARE_SIMPLE_TYPE(OtFlashState, OT_FLASH) +OBJECT_DECLARE_TYPE(OtFlashState, OtFlashClass, OT_FLASH) #endif /* HW_OPENTITAN_OT_FLASH_H */ From a7fa937adf875180ea4332fac77c11e56f4c5186 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:18:24 +0200 Subject: [PATCH 42/69] [ot] hw/opentitan: ot_gpio: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_gpio_dj.c | 8 +++--- hw/opentitan/ot_gpio_eg.c | 47 +++++++++++++++++++++++++++---- include/hw/opentitan/ot_gpio_eg.h | 4 +-- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_gpio_dj.c b/hw/opentitan/ot_gpio_dj.c index 2e10d8b9beb93..80aa8480d7abb 100644 --- a/hw/opentitan/ot_gpio_dj.c +++ b/hw/opentitan/ot_gpio_dj.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling GPIO device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Samuel Ortiz @@ -935,10 +935,10 @@ static void ot_gpio_dj_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); ResettableClass *rc = RESETTABLE_CLASS(dc); - OtGpioDjClass *pc = OT_GPIO_DJ_CLASS(klass); + OtGpioDjClass *gc = OT_GPIO_DJ_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_gpio_dj_reset_enter, NULL, &ot_gpio_dj_reset_exit, - &pc->parent_phases); + &gc->parent_phases); } static const TypeInfo ot_gpio_dj_info = { @@ -946,8 +946,8 @@ static const TypeInfo ot_gpio_dj_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtGpioDjState), .instance_init = &ot_gpio_dj_init, + .class_size = sizeof(OtGpioDjClass), .class_init = &ot_gpio_dj_class_init, - .class_size = sizeof(OtGpioDjClass) }; static void ot_gpio_dj_register_types(void) diff --git a/hw/opentitan/ot_gpio_eg.c b/hw/opentitan/ot_gpio_eg.c index e24af2bbe5b25..5a338b8655b73 100644 --- a/hw/opentitan/ot_gpio_eg.c +++ b/hw/opentitan/ot_gpio_eg.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Earlgrey GPIO device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -142,6 +142,12 @@ struct OtGpioEgState { bool wipe; /* whether to wipe the backend at reset */ }; +struct OtGpioEgClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + + static const char DEFAULT_OT_ID[] = ""; static void ot_gpio_eg_update_backend(OtGpioEgState *s, bool force); @@ -700,14 +706,20 @@ static Property ot_gpio_eg_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_gpio_eg_reset(DeviceState *dev) +static void ot_gpio_eg_reset_enter(Object *obj, ResetType type) { - OtGpioEgState *s = OT_GPIO_EG(dev); + OtGpioEgClass *c = OT_GPIO_EG_GET_CLASS(obj); + OtGpioEgState *s = OT_GPIO_EG(obj); if (!s->ot_id) { s->ot_id = g_strdup(DEFAULT_OT_ID); } + trace_ot_gpio_reset(s->ot_id, "> enter"); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + memset(s->regs, 0, sizeof(s->regs)); memset(&s->backend_state, 0, sizeof(s->backend_state)); @@ -731,14 +743,33 @@ static void ot_gpio_eg_reset(DeviceState *dev) ot_gpio_eg_update_irqs(s); ibex_irq_set(&s->alert, 0); + trace_ot_gpio_reset(s->ot_id, "< enter"); +} + +static void ot_gpio_eg_reset_exit(Object *obj, ResetType type) +{ + /* + * use of a Resettable full API enables performing I/O updates only once + * the pinmux configuration has been received (from its own reset stage) + */ + OtGpioEgClass *c = OT_GPIO_EG_GET_CLASS(obj); + OtGpioEgState *s = OT_GPIO_EG(obj); + + trace_ot_gpio_reset(s->ot_id, "> exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + ot_gpio_eg_init_backend(s); ot_gpio_eg_update_data_out(s); ot_gpio_eg_update_backend(s, true); /* - * do not reset the input backed buffer as external GPIO changes is fully + * do not reset the input backend buffer as external GPIO changes is fully * async with OT reset. However, it should be reset when the backend changes */ + trace_ot_gpio_reset(s->ot_id, "< exit"); } static void ot_gpio_eg_realize(DeviceState *dev, Error **errp) @@ -779,10 +810,15 @@ static void ot_gpio_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_gpio_eg_reset); dc->realize = &ot_gpio_eg_realize; device_class_set_props(dc, ot_gpio_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(dc); + OtGpioEgClass *gc = OT_GPIO_EG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_gpio_eg_reset_enter, NULL, + &ot_gpio_eg_reset_exit, + &gc->parent_phases); } static const TypeInfo ot_gpio_eg_info = { @@ -790,6 +826,7 @@ static const TypeInfo ot_gpio_eg_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtGpioEgState), .instance_init = &ot_gpio_eg_init, + .class_size = sizeof(OtGpioEgClass), .class_init = &ot_gpio_eg_class_init, }; diff --git a/include/hw/opentitan/ot_gpio_eg.h b/include/hw/opentitan/ot_gpio_eg.h index f01d5b0c3bd4c..782839fd7e85a 100644 --- a/include/hw/opentitan/ot_gpio_eg.h +++ b/include/hw/opentitan/ot_gpio_eg.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey GPIO device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "hw/opentitan/ot_gpio.h" #define TYPE_OT_GPIO_EG "ot-gpio-eg" -OBJECT_DECLARE_SIMPLE_TYPE(OtGpioEgState, OT_GPIO_EG) +OBJECT_DECLARE_TYPE(OtGpioEgState, OtGpioEgClass, OT_GPIO_EG) #endif /* HW_OPENTITAN_OT_GPIO_EG_H */ From 352faaac19420276612ea3032d472d34f9a3def2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:21:05 +0200 Subject: [PATCH 43/69] [ot] hw/opentitan: ot_hmac: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_hmac.c | 23 +++++++++++++++++++---- include/hw/opentitan/ot_hmac.h | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_hmac.c b/hw/opentitan/ot_hmac.c index ba5cd002dad81..1cdd82e670787 100644 --- a/hw/opentitan/ot_hmac.c +++ b/hw/opentitan/ot_hmac.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan HMAC device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2024-2025 lowRISC contributors. * * Author(s): @@ -284,6 +284,11 @@ struct OtHMACState { char *ot_id; }; +struct OtHMACClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static inline OtHMACDigestSize ot_hmac_get_digest_size(uint32_t cfg_reg) { switch ((cfg_reg & R_CFG_DIGEST_SIZE_MASK) >> R_CFG_DIGEST_SIZE_SHIFT) { @@ -1244,11 +1249,16 @@ static void ot_hmac_realize(DeviceState *dev, Error **errp) g_assert(s->ot_id); } -static void ot_hmac_reset(DeviceState *dev) +static void ot_hmac_reset_enter(Object *obj, ResetType type) { - OtHMACState *s = OT_HMAC(dev); + OtHMACClass *c = OT_HMAC_GET_CLASS(obj); + OtHMACState *s = OT_HMAC(obj); OtHMACRegisters *r = s->regs; + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + ibex_irq_set(&s->clkmgr, false); memset(s->ctx, 0, sizeof(*(s->ctx))); @@ -1267,10 +1277,14 @@ static void ot_hmac_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_hmac_reset); dc->realize = &ot_hmac_realize; device_class_set_props(dc, ot_hmac_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtHMACClass *hc = OT_HMAC_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_hmac_reset_enter, NULL, NULL, + &hc->parent_phases); } static const TypeInfo ot_hmac_info = { @@ -1278,6 +1292,7 @@ static const TypeInfo ot_hmac_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtHMACState), .instance_init = &ot_hmac_init, + .class_size = sizeof(OtHMACClass), .class_init = &ot_hmac_class_init, }; diff --git a/include/hw/opentitan/ot_hmac.h b/include/hw/opentitan/ot_hmac.h index 63f980cde5b75..a3da4c6b45dd0 100644 --- a/include/hw/opentitan/ot_hmac.h +++ b/include/hw/opentitan/ot_hmac.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan HMAC device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_HMAC "ot-hmac" -OBJECT_DECLARE_SIMPLE_TYPE(OtHMACState, OT_HMAC) +OBJECT_DECLARE_TYPE(OtHMACState, OtHMACClass, OT_HMAC) #endif /* HW_OPENTITAN_OT_HMAC_H */ From 04c8b20e78182347287c891f4dc614b9b414839a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:28:25 +0200 Subject: [PATCH 44/69] [ot] hw/opentitan: ot_i2c_dj: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_i2c_dj.c | 26 ++++++++++++++++++++------ include/hw/opentitan/ot_i2c_dj.h | 6 +++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_i2c_dj.c b/hw/opentitan/ot_i2c_dj.c index 060688c67c8d0..bb4cbc1559930 100644 --- a/hw/opentitan/ot_i2c_dj.c +++ b/hw/opentitan/ot_i2c_dj.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan I2C Darjeeling device * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Duncan Laurie @@ -293,6 +293,11 @@ struct OtI2CDjState { uint32_t pclk; }; +struct OtI2CDjClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + struct OtI2CDjTarget { I2CSlave i2c; }; @@ -937,8 +942,8 @@ static const TypeInfo ot_i2c_dj_target_info = { .name = TYPE_OT_I2C_DJ_TARGET, .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(OtI2CDjState), - .class_init = &ot_i2c_dj_target_class_init, .class_size = sizeof(I2CSlaveClass), + .class_init = &ot_i2c_dj_target_class_init, }; static const MemoryRegionOps ot_i2c_dj_ops = { @@ -955,9 +960,14 @@ static Property ot_i2c_dj_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_i2c_dj_reset(DeviceState *dev) +static void ot_i2c_dj_reset_enter(Object *obj, ResetType type) { - OtI2CDjState *s = OT_I2C_DJ(dev); + OtI2CDjClass *c = OT_I2C_DJ_GET_CLASS(obj); + OtI2CDjState *s = OT_I2C_DJ(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } i2c_end_transfer(s->bus); @@ -1012,10 +1022,13 @@ static void ot_i2c_dj_class_init(ObjectClass *klass, void *data) dc->desc = "OpenTitan I2C Host"; dc->realize = ot_i2c_dj_realize; - device_class_set_legacy_reset(dc, ot_i2c_dj_reset); - device_class_set_props(dc, ot_i2c_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtI2CDjClass *ic = OT_I2C_DJ_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_i2c_dj_reset_enter, NULL, NULL, + &ic->parent_phases); } static const TypeInfo ot_i2c_dj_info = { @@ -1023,6 +1036,7 @@ static const TypeInfo ot_i2c_dj_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtI2CDjState), .instance_init = &ot_i2c_dj_init, + .class_size = sizeof(OtI2CDjClass), .class_init = &ot_i2c_dj_class_init, }; diff --git a/include/hw/opentitan/ot_i2c_dj.h b/include/hw/opentitan/ot_i2c_dj.h index 3eb6afd70439b..2933e5697bc8c 100644 --- a/include/hw/opentitan/ot_i2c_dj.h +++ b/include/hw/opentitan/ot_i2c_dj.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan I2C Darjeeling device * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Duncan Laurie @@ -32,9 +32,9 @@ #include "hw/sysbus.h" #define TYPE_OT_I2C_DJ "ot-i2c-dj" -OBJECT_DECLARE_SIMPLE_TYPE(OtI2CDjState, OT_I2C_DJ) +OBJECT_DECLARE_TYPE(OtI2CDjState, OtI2CDjClass, OT_I2C_DJ) -#define TYPE_OT_I2C_DJ_TARGET "ot-i2c-dj-target" +#define TYPE_OT_I2C_DJ_TARGET TYPE_OT_I2C_DJ "-target" OBJECT_DECLARE_SIMPLE_TYPE(OtI2CDjTarget, OT_I2C_DJ_TARGET) #endif /* HW_OPENTITAN_OT_I2C_DJ_H */ From bc80e842f42781aa378b8bcd426773a2cd2e47a3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:34:23 +0200 Subject: [PATCH 45/69] [ot] hw/opentitan: ot_kmac: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_kmac.c | 27 +++++++++++++++++++++------ include/hw/opentitan/ot_kmac.h | 4 ++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/hw/opentitan/ot_kmac.c b/hw/opentitan/ot_kmac.c index f87e6f7be5a52..15e941da42a51 100644 --- a/hw/opentitan/ot_kmac.c +++ b/hw/opentitan/ot_kmac.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan KMAC device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -49,7 +49,6 @@ #include "tomcrypt.h" #include "trace.h" - #define KMAC_PARAM_NUM_ALERTS 2u /* clang-format off */ @@ -406,6 +405,11 @@ struct OtKMACState { uint8_t num_app; }; +struct OtKMACClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_kmac_change_fsm_state_line(OtKMACState *s, OtKMACFsmState state, int line) { @@ -1572,13 +1576,19 @@ static const MemoryRegionOps ot_kmac_msgfifo_ops = { }, }; -static void ot_kmac_reset(DeviceState *dev) +static void ot_kmac_reset_enter(Object *obj, ResetType type) { - OtKMACState *s = OT_KMAC(dev); + OtKMACClass *c = OT_KMAC_GET_CLASS(obj); + OtKMACState *s = OT_KMAC(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + ot_kmac_cancel_bh(s); ot_kmac_change_fsm_state(s, KMAC_ST_IDLE); ot_kmac_reset_state(s); - ot_kmac_cancel_bh(s); memset(&s->sw_cfg, 0, sizeof(OtKMACAppCfg)); s->current_app = NULL; s->pending_apps = 0; @@ -1651,9 +1661,13 @@ static void ot_kmac_class_init(ObjectClass *klass, void *data) (void)data; dc->realize = &ot_kmac_realize; - device_class_set_legacy_reset(dc, &ot_kmac_reset); device_class_set_props(dc, ot_kmac_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtKMACClass *kc = OT_KMAC_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_kmac_reset_enter, NULL, NULL, + &kc->parent_phases); } static const TypeInfo ot_kmac_info = { @@ -1661,6 +1675,7 @@ static const TypeInfo ot_kmac_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtKMACState), .instance_init = &ot_kmac_init, + .class_size = sizeof(OtKMACClass), .class_init = &ot_kmac_class_init, }; diff --git a/include/hw/opentitan/ot_kmac.h b/include/hw/opentitan/ot_kmac.h index 84ab01dea0d16..292d6e1ffd09f 100644 --- a/include/hw/opentitan/ot_kmac.h +++ b/include/hw/opentitan/ot_kmac.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan KMAC device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -34,7 +34,7 @@ #include "qom/object.h" #define TYPE_OT_KMAC "ot-kmac" -OBJECT_DECLARE_SIMPLE_TYPE(OtKMACState, OT_KMAC) +OBJECT_DECLARE_TYPE(OtKMACState, OtKMACClass, OT_KMAC) enum OtKMACMode { OT_KMAC_MODE_NONE, From de4597bb8acf941f06f61d0979bbae4531e3bde8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:39:08 +0200 Subject: [PATCH 46/69] [ot] hw/opentitan: ot_lc_ctrl: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_lc_ctrl.c | 91 ++++++++++++++++++------------- include/hw/opentitan/ot_lc_ctrl.h | 4 +- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index c6516d4cb29c8..b66ea382c88ca 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Life Cycle controller device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -412,6 +412,11 @@ struct OtLcCtrlState { bool socdbg; /* whether this instance use SoCDbg state */ }; +struct OtLcCtrlClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + typedef struct { unsigned word_count; /* sequence size (count of 16-bit words) */ unsigned step_count; /* how many different steps/stages, incl. raw/blank */ @@ -2095,46 +2100,19 @@ static const MemoryRegionOps ot_lc_ctrl_dmi_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_lc_ctrl_reset(DeviceState *dev) +static void ot_lc_ctrl_reset_enter(Object *obj, ResetType type) { - OtLcCtrlState *s = OT_LC_CTRL(dev); + OtLcCtrlClass *c = OT_LC_CTRL_GET_CLASS(obj); + OtLcCtrlState *s = OT_LC_CTRL(obj); trace_ot_lc_ctrl_reset(s->ot_id); - g_assert(s->otp_ctrl); - g_assert(s->kmac); - g_assert(s->kmac_app != UINT8_MAX); - - /* - * "ID of the silicon creator. Assigned by the OpenTitan project. - * 0x0000: invalid value - * 0x0001 - 0x3FFF: reserved for use in the open-source OpenTitan project - * 0x4000 - 0x7FFF: reserved for real integrations of OpenTitan - * 0x8000 - 0xFFFF: reserved for future use" - */ - if (s->silicon_creator_id == 0 || s->silicon_creator_id > 0x8000) { - error_setg(&error_fatal, "Invalid silicon_creator_id: 0x%04x", - s->silicon_creator_id); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); } - /* - * "Used to identify a class of devices. Assigned by the Silicon Creator - * 0x0000: invalid value - * 0x0001 - 0x3FFF: reserved for discrete chip products - * 0x4000 - 0x7FFF: reserved for integrated IP products - * 0x8000 - 0xFFFF: reserved for future use" - */ - if (s->product_id == 0 || s->product_id > 0x8000) { - error_setg(&error_fatal, "Invalid product_id: 0x%04x", s->product_id); - } - - /* - * "Product revision ID. Assigned by the Silicon Creator - * Zero is an invalid value." - */ - if (s->revision_id == 0) { - error_setg(&error_fatal, "Invalid revision_id: 0x%02x", s->revision_id); - } + qemu_bh_cancel(s->escalate_bh); + qemu_bh_cancel(s->pwc_lc_bh); memset(s->regs, 0, REGS_SIZE); memset(s->xregs, 0, sizeof(s->xregs)); @@ -2172,10 +2150,44 @@ static void ot_lc_ctrl_reset(DeviceState *dev) static void ot_lc_ctrl_realize(DeviceState *dev, Error **errp) { - (void)errp; OtLcCtrlState *s = OT_LC_CTRL(dev); + (void)errp; g_assert(s->ot_id); + g_assert(s->otp_ctrl); + g_assert(s->kmac); + g_assert(s->kmac_app != UINT8_MAX); + + /* + * "ID of the silicon creator. Assigned by the OpenTitan project. + * 0x0000: invalid value + * 0x0001 - 0x3FFF: reserved for use in the open-source OpenTitan project + * 0x4000 - 0x7FFF: reserved for real integrations of OpenTitan + * 0x8000 - 0xFFFF: reserved for future use" + */ + if (s->silicon_creator_id == 0 || s->silicon_creator_id > 0x8000) { + error_setg(&error_fatal, "Invalid silicon_creator_id: 0x%04x", + s->silicon_creator_id); + } + + /* + * "Used to identify a class of devices. Assigned by the Silicon Creator + * 0x0000: invalid value + * 0x0001 - 0x3FFF: reserved for discrete chip products + * 0x4000 - 0x7FFF: reserved for integrated IP products + * 0x8000 - 0xFFFF: reserved for future use" + */ + if (s->product_id == 0 || s->product_id > 0x8000) { + error_setg(&error_fatal, "Invalid product_id: 0x%04x", s->product_id); + } + + /* + * "Product revision ID. Assigned by the Silicon Creator + * Zero is an invalid value." + */ + if (s->revision_id == 0) { + error_setg(&error_fatal, "Invalid revision_id: 0x%02x", s->revision_id); + } ot_lc_ctrl_configure_lc_states(s); ot_lc_ctrl_configure_transitions(s, LC_CTRL_TRANS_LC_TCOUNT, @@ -2237,10 +2249,14 @@ static void ot_lc_ctrl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_lc_ctrl_reset); dc->realize = &ot_lc_ctrl_realize; device_class_set_props(dc, ot_lc_ctrl_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtLcCtrlClass *lc = OT_LC_CTRL_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_lc_ctrl_reset_enter, NULL, NULL, + &lc->parent_phases); } static const TypeInfo ot_lc_ctrl_info = { @@ -2248,6 +2264,7 @@ static const TypeInfo ot_lc_ctrl_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtLcCtrlState), .instance_init = &ot_lc_ctrl_init, + .class_size = sizeof(OtLcCtrlClass), .class_init = &ot_lc_ctrl_class_init, }; diff --git a/include/hw/opentitan/ot_lc_ctrl.h b/include/hw/opentitan/ot_lc_ctrl.h index 499146c339f77..05b2f301fda8e 100644 --- a/include/hw/opentitan/ot_lc_ctrl.h +++ b/include/hw/opentitan/ot_lc_ctrl.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Life Cycle controller device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_LC_CTRL "ot-lc_ctrl" -OBJECT_DECLARE_SIMPLE_TYPE(OtLcCtrlState, OT_LC_CTRL) +OBJECT_DECLARE_TYPE(OtLcCtrlState, OtLcCtrlClass, OT_LC_CTRL) /* input lines */ #define OT_LC_PWR TYPE_OT_LC_CTRL "-pwr" From 1504cc14ff31db3ad90ce34f03aecf047f3568c1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 12:47:55 +0200 Subject: [PATCH 47/69] [ot] hw/opentitan: ot_mbx: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_mbx.c | 61 +++++++++++++++++++++++++++-------- include/hw/opentitan/ot_mbx.h | 4 +-- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/hw/opentitan/ot_mbx.c b/hw/opentitan/ot_mbx.c index 8ff455bc40ed1..3e95b8f93e11b 100644 --- a/hw/opentitan/ot_mbx.c +++ b/hw/opentitan/ot_mbx.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Data Object Exchange Mailbox * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -210,6 +210,11 @@ struct OtMbxState { char *ram_as_name; }; +struct OtMbxClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_mbx_host_update_irqs(OtMbxState *s) { OtMbxHost *host = &s->host; @@ -746,11 +751,14 @@ static const MemoryRegionOps ot_mbx_sys_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_mbx_reset(DeviceState *dev) +static void ot_mbx_reset_enter(Object *obj, ResetType type) { - OtMbxState *s = OT_MBX(dev); + OtMbxClass *c = OT_MBX_GET_CLASS(obj); + OtMbxState *s = OT_MBX(obj); - g_assert(s->ot_id); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } OtMbxHost *host = &s->host; OtMbxSys *sys = &s->sys; @@ -760,22 +768,43 @@ static void ot_mbx_reset(DeviceState *dev) host->regs[R_HOST_ADDRESS_RANGE_REGWEN] = OT_MULTIBITBOOL4_TRUE; host->regs[R_HOST_STATUS] = R_SYS_STATUS_BUSY_MASK; + ot_mbx_host_update_irqs(s); + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->host.alerts[ix], 0); + } + + xtrace_ot_mbx_status(s); +} + +static void ot_mbx_reset_exit(Object *obj, ResetType type) +{ + OtMbxClass *c = OT_MBX_GET_CLASS(obj); + OtMbxState *s = OT_MBX(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + OtMbxSys *sys = &s->sys; + if (!sys->ram_as) { - Object *soc = OBJECT(dev)->parent; - Object *obj = + Object *soc = obj->parent; + Object *as = object_property_get_link(soc, s->ram_as_name, &error_fatal); OtAddressSpaceState *oas = - OBJECT_CHECK(OtAddressSpaceState, obj, TYPE_OT_ADDRESS_SPACE); + OBJECT_CHECK(OtAddressSpaceState, as, TYPE_OT_ADDRESS_SPACE); sys->ram_as = ot_address_space_get(oas); g_assert(sys->ram_as); } +} - ot_mbx_host_update_irqs(s); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->host.alerts[ix], 0); - } +static void ot_mbx_realize(DeviceState *dev, Error **errp) +{ + OtMbxState *s = OT_MBX(dev); + (void)errp; - xtrace_ot_mbx_status(s); + g_assert(s->ot_id); + g_assert(s->ram_as_name); } static void ot_mbx_init(Object *obj) @@ -802,9 +831,14 @@ static void ot_mbx_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_mbx_reset); + dc->realize = &ot_mbx_realize; device_class_set_props(dc, ot_mbx_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtMbxClass *mc = OT_MBX_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_mbx_reset_enter, NULL, + &ot_mbx_reset_exit, &mc->parent_phases); } static const TypeInfo ot_mbx_info = { @@ -812,6 +846,7 @@ static const TypeInfo ot_mbx_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtMbxState), .instance_init = &ot_mbx_init, + .class_size = sizeof(OtMbxClass), .class_init = &ot_mbx_class_init, }; diff --git a/include/hw/opentitan/ot_mbx.h b/include/hw/opentitan/ot_mbx.h index 3a3a15a2f6388..fc5fc35e43564 100644 --- a/include/hw/opentitan/ot_mbx.h +++ b/include/hw/opentitan/ot_mbx.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Data Object Exchange Mailbox * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_MBX "ot-mbx" -OBJECT_DECLARE_SIMPLE_TYPE(OtMbxState, OT_MBX) +OBJECT_DECLARE_TYPE(OtMbxState, OtMbxClass, OT_MBX) #define OT_MBX_HOST_REGS_COUNT 17u #define OT_MBX_SYS_REGS_COUNT 8u From 4f1e6869d6588ebc61afafeef1b53be8b204b39c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 13:00:39 +0200 Subject: [PATCH 48/69] [ot] hw/opentitan: ot_otbn: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otbn.c | 50 +++++++++++++++++++++++++++------- include/hw/opentitan/ot_otbn.h | 4 +-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_otbn.c b/hw/opentitan/ot_otbn.c index 7a63b3b5bd8df..11b3d5da15607 100644 --- a/hw/opentitan/ot_otbn.c +++ b/hw/opentitan/ot_otbn.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Big Number device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -169,6 +169,11 @@ struct OtOTBNState { bool log_asm; }; +struct OtOTBNClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_otbn_request_entropy(OtOTBNRandom *rnd); static bool ot_otbn_is_idle(OtOTBNState *s) @@ -621,9 +626,14 @@ static const MemoryRegionOps ot_otbn_dmem_ops = { .impl.max_access_size = 4u, }; -static void ot_otbn_reset(DeviceState *dev) +static void ot_otbn_reset_enter(Object *obj, ResetType type) { - OtOTBNState *s = OT_OTBN(dev); + OtOTBNClass *c = OT_OTBN_GET_CLASS(obj); + OtOTBNState *s = OT_OTBN(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->proxy_defer); @@ -647,6 +657,16 @@ static void ot_otbn_reset(DeviceState *dev) rnd->entropy_requested = false; ot_fifo32_reset(&rnd->packer); } +} + +static void ot_otbn_reset_exit(Object *obj, ResetType type) +{ + OtOTBNClass *c = OT_OTBN_GET_CLASS(obj); + OtOTBNState *s = OT_OTBN(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } if (!s->log_file) { s->log_asm = false; @@ -655,6 +675,17 @@ static void ot_otbn_reset(DeviceState *dev) ot_otbn_proxy_start(s->proxy, false, s->log_file, s->log_asm); } +static void ot_otbn_realize(DeviceState *dev, Error **errp) +{ + (void)errp; + + OtOTBNState *s = OT_OTBN(dev); + + g_assert(s->rnds[OT_OTBN_URND].device); + g_assert(s->rnds[OT_OTBN_RND].device); + g_assert(s->rnds[OT_OTBN_URND].ep != UINT8_MAX); + g_assert(s->rnds[OT_OTBN_RND].ep != UINT8_MAX); +} static void ot_otbn_init(Object *obj) { @@ -701,21 +732,19 @@ static void ot_otbn_init(Object *obj) &ot_otbn_signal_on_completion, s); } -static void ot_otbn_realize(DeviceState *dev, Error **errp) -{ - (void)dev; - (void)errp; -} - static void ot_otbn_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_otbn_reset); dc->realize = &ot_otbn_realize; device_class_set_props(dc, ot_otbn_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtOTBNClass *oc = OT_OTBN_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otbn_reset_enter, NULL, + &ot_otbn_reset_exit, &oc->parent_phases); } static const TypeInfo ot_otbn_info = { @@ -723,6 +752,7 @@ static const TypeInfo ot_otbn_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtOTBNState), .instance_init = &ot_otbn_init, + .class_size = sizeof(OtOTBNClass), .class_init = &ot_otbn_class_init, }; diff --git a/include/hw/opentitan/ot_otbn.h b/include/hw/opentitan/ot_otbn.h index 7c77c27af1147..3c60ccd608f7b 100644 --- a/include/hw/opentitan/ot_otbn.h +++ b/include/hw/opentitan/ot_otbn.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Big Number device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_OTBN "ot-otbn" -OBJECT_DECLARE_SIMPLE_TYPE(OtOTBNState, OT_OTBN) +OBJECT_DECLARE_TYPE(OtOTBNState, OtOTBNClass, OT_OTBN) #endif /* HW_OPENTITAN_OT_OTBN_H */ From 79d7b804f5fec1bb9076f3072f5de08e4429371d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 13:07:59 +0200 Subject: [PATCH 49/69] [ot] hw/opentitan: ot_otp_ot_be: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_ot_be.c | 31 +++++++++++++++++++++++++---- include/hw/opentitan/ot_otp_ot_be.h | 4 ++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_otp_ot_be.c b/hw/opentitan/ot_otp_ot_be.c index 2030b3676f641..e71fbf9005eae 100644 --- a/hw/opentitan/ot_otp_ot_be.c +++ b/hw/opentitan/ot_otp_ot_be.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan OTP backend * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -123,6 +123,11 @@ struct OtOtpOtBeState { DeviceState *parent; }; +struct OtOtpOtBeClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint64_t ot_otp_ot_be_read(void *opaque, hwaddr addr, unsigned size) { OtOtpOtBeState *s = opaque; @@ -190,6 +195,13 @@ static void ot_otp_ot_be_write(void *opaque, hwaddr addr, uint64_t value, } } +static bool ot_otp_ot_be_is_ecc_enabled(OtOtpBeIf *beif) +{ + (void)beif; + + return true; +} + static Property ot_otp_ot_be_properties[] = { DEFINE_PROP_STRING("ot_id", OtOtpOtBeState, ot_id), DEFINE_PROP_LINK("parent", OtOtpOtBeState, parent, TYPE_DEVICE, @@ -205,11 +217,16 @@ static const MemoryRegionOps ot_otp_ot_be_ops = { .impl.max_access_size = 4, }; -static bool ot_otp_ot_be_is_ecc_enabled(OtOtpBeIf *beif) +static void ot_otp_ot_be_reset_enter(Object *obj, ResetType type) { - (void)beif; + OtOtpOtBeClass *c = OT_OTP_OT_BE_GET_CLASS(obj); + OtOtpOtBeState *s = OT_OTP_OT_BE(obj); - return true; + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + memset(s->regs, 0, sizeof(s->regs)); } static void ot_otp_ot_be_init(Object *obj) @@ -229,6 +246,11 @@ static void ot_otp_ot_be_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, ot_otp_ot_be_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtOtpOtBeClass *oc = OT_OTP_OT_BE_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_otp_ot_be_reset_enter, NULL, + NULL, &oc->parent_phases); + OtOtpBeIfClass *bec = OT_OTP_BE_IF_CLASS(klass); bec->is_ecc_enabled = &ot_otp_ot_be_is_ecc_enabled; } @@ -238,6 +260,7 @@ static const TypeInfo ot_otp_ot_be_init_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtOtpOtBeState), .instance_init = &ot_otp_ot_be_init, + .class_size = sizeof(OtOtpOtBeClass), .class_init = &ot_otp_ot_be_class_init, .interfaces = (InterfaceInfo[]){ diff --git a/include/hw/opentitan/ot_otp_ot_be.h b/include/hw/opentitan/ot_otp_ot_be.h index 1815d09a6ba20..52705b2e97209 100644 --- a/include/hw/opentitan/ot_otp_ot_be.h +++ b/include/hw/opentitan/ot_otp_ot_be.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan OTP backend * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -29,6 +29,6 @@ #define HW_OPENTITAN_OT_OTP_OT_BE_H #define TYPE_OT_OTP_OT_BE "ot-otp_ot_be" -OBJECT_DECLARE_SIMPLE_TYPE(OtOtpOtBeState, OT_OTP_OT_BE) +OBJECT_DECLARE_TYPE(OtOtpOtBeState, OtOtpOtBeClass, OT_OTP_OT_BE) #endif /* HW_OPENTITAN_OT_OTP_OT_BE_H */ From eb29832c3917131eab40a6b1b565bcca671787b2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:17:50 +0200 Subject: [PATCH 50/69] [ot] hw/opentitan: ot_pinmux_dj: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_pinmux_dj.c | 22 ++++++++++++++++++---- include/hw/opentitan/ot_pinmux_dj.h | 4 ++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_pinmux_dj.c b/hw/opentitan/ot_pinmux_dj.c index 630e12eaa2362..9c46b708c739d 100644 --- a/hw/opentitan/ot_pinmux_dj.c +++ b/hw/opentitan/ot_pinmux_dj.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling PinMux device * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -200,6 +200,11 @@ struct OtPinmuxDjState { OtPinmuxDjStateRegs *regs; }; +struct OtPinmuxDjClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint32_t ot_pinmux_dj_sel_mask(unsigned val) { return (1u << (32 - clz32(val))) - 1u; @@ -495,10 +500,14 @@ static const MemoryRegionOps ot_pinmux_dj_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_pinmux_dj_reset(DeviceState *dev) +static void ot_pinmux_dj_reset_enter(Object *obj, ResetType type) { - OtPinmuxDjState *s = OT_PINMUX_DJ(dev); + OtPinmuxDjClass *c = OT_PINMUX_DJ_GET_CLASS(obj); + OtPinmuxDjState *s = OT_PINMUX_DJ(obj); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } OtPinmuxDjStateRegs *regs = s->regs; memset(regs, 0, sizeof(*regs)); @@ -548,9 +557,13 @@ static void ot_pinmux_dj_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_pinmux_dj_reset); device_class_set_props(dc, ot_pinmux_dj_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtPinmuxDjClass *pc = OT_PINMUX_DJ_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_pinmux_dj_reset_enter, NULL, + NULL, &pc->parent_phases); } static const TypeInfo ot_pinmux_info = { @@ -558,6 +571,7 @@ static const TypeInfo ot_pinmux_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtPinmuxDjState), .instance_init = &ot_pinmux_dj_init, + .class_size = sizeof(OtPinmuxDjClass), .class_init = &ot_pinmux_dj_class_init, }; diff --git a/include/hw/opentitan/ot_pinmux_dj.h b/include/hw/opentitan/ot_pinmux_dj.h index d0939f3327d32..58fc77aa0127a 100644 --- a/include/hw/opentitan/ot_pinmux_dj.h +++ b/include/hw/opentitan/ot_pinmux_dj.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Darjeeling PinMux device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "hw/opentitan/ot_pinmux.h" #define TYPE_OT_PINMUX_DJ TYPE_OT_PINMUX "-dj" -OBJECT_DECLARE_SIMPLE_TYPE(OtPinmuxDjState, OT_PINMUX_DJ) +OBJECT_DECLARE_TYPE(OtPinmuxDjState, OtPinmuxDjClass, OT_PINMUX_DJ) #endif /* HW_OPENTITAN_OT_PINMUX_DJ_H */ From 38687e50e2b6424af37c28da7f000ca822c72d80 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:18:05 +0200 Subject: [PATCH 51/69] [ot] hw/opentitan: ot_pinmux_eg: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_pinmux_eg.c | 23 +++++++++++++++++++---- include/hw/opentitan/ot_pinmux_eg.h | 4 ++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_pinmux_eg.c b/hw/opentitan/ot_pinmux_eg.c index 39fb231785ffa..4167b636d743a 100644 --- a/hw/opentitan/ot_pinmux_eg.c +++ b/hw/opentitan/ot_pinmux_eg.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey PinMux device * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -201,6 +201,11 @@ struct OtPinmuxEgState { OtPinmuxEgStateRegs *regs; }; +struct OtPinmuxEgClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint32_t ot_pinmux_eg_sel_mask(unsigned val) { return (1u << (32 - clz32(val))) - 1u; @@ -497,9 +502,14 @@ static const MemoryRegionOps ot_pinmux_eg_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_pinmux_eg_reset(DeviceState *dev) +static void ot_pinmux_eg_reset_enter(Object *obj, ResetType type) { - OtPinmuxEgState *s = OT_PINMUX_EG(dev); + OtPinmuxEgClass *c = OT_PINMUX_EG_GET_CLASS(obj); + OtPinmuxEgState *s = OT_PINMUX_EG(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } OtPinmuxEgStateRegs *regs = s->regs; memset(regs, 0, sizeof(*regs)); @@ -550,9 +560,13 @@ static void ot_pinmux_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_pinmux_eg_reset); device_class_set_props(dc, ot_pinmux_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtPinmuxEgClass *pc = OT_PINMUX_EG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_pinmux_eg_reset_enter, NULL, + NULL, &pc->parent_phases); } static const TypeInfo ot_pinmux_eg_info = { @@ -560,6 +574,7 @@ static const TypeInfo ot_pinmux_eg_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtPinmuxEgState), .instance_init = &ot_pinmux_eg_init, + .class_size = sizeof(OtPinmuxEgClass), .class_init = &ot_pinmux_eg_class_init, }; diff --git a/include/hw/opentitan/ot_pinmux_eg.h b/include/hw/opentitan/ot_pinmux_eg.h index 4aee81e19430d..20122898cfea0 100644 --- a/include/hw/opentitan/ot_pinmux_eg.h +++ b/include/hw/opentitan/ot_pinmux_eg.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan EarlGrey PinMux device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "hw/opentitan/ot_pinmux.h" #define TYPE_OT_PINMUX_EG TYPE_OT_PINMUX "-eg" -OBJECT_DECLARE_SIMPLE_TYPE(OtPinmuxEgState, OT_PINMUX_EG) +OBJECT_DECLARE_TYPE(OtPinmuxEgState, OtPinmuxEgClass, OT_PINMUX_EG) #endif /* HW_OPENTITAN_OT_PINMUX_EG_H */ From 8271236cadb8dd2cdb13d0cfa5b77d0479a23317 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:20:36 +0200 Subject: [PATCH 52/69] [ot] hw/opentitan: ot_plic_ext: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_plic_ext.c | 32 ++++++++++++++++++++++++++---- include/hw/opentitan/ot_plic_ext.h | 4 ++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_plic_ext.c b/hw/opentitan/ot_plic_ext.c index bf38aec6b615c..7565b851b97ea 100644 --- a/hw/opentitan/ot_plic_ext.c +++ b/hw/opentitan/ot_plic_ext.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan PLIC extension * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -84,6 +84,11 @@ struct OtPlicExtState { char *ot_id; }; +struct OtPlicExtClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) static const char *MSIP_REG_NAMES[MSIP_REGS_COUNT] = { REG_NAME_ENTRY(MSIP0), @@ -229,14 +234,27 @@ static const MemoryRegionOps ot_plic_ext_alert_ops = { .impl.max_access_size = 4u, }; -static void ot_plic_ext_reset(DeviceState *dev) +static void ot_plic_ext_reset_enter(Object *obj, ResetType type) { - OtPlicExtState *s = OT_PLIC_EXT(dev); + OtPlicExtClass *c = OT_PLIC_EXT_GET_CLASS(obj); + OtPlicExtState *s = OT_PLIC_EXT(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } ibex_irq_set(&s->irq, 0); ibex_irq_set(&s->alert, 0); } +static void ot_plic_ext_realize(DeviceState *dev, Error **errp) +{ + OtPlicExtState *s = OT_PLIC_EXT(dev); + (void)errp; + + g_assert(s->ot_id); +} + static void ot_plic_ext_init(Object *obj) { OtPlicExtState *s = OT_PLIC_EXT(obj); @@ -263,9 +281,14 @@ static void ot_plic_ext_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_plic_ext_reset); + dc->realize = &ot_plic_ext_realize; device_class_set_props(dc, ot_plic_ext_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtPlicExtClass *pc = OT_PLIC_EXT_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_plic_ext_reset_enter, NULL, NULL, + &pc->parent_phases); } static const TypeInfo ot_plic_ext_info = { @@ -273,6 +296,7 @@ static const TypeInfo ot_plic_ext_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtPlicExtState), .instance_init = &ot_plic_ext_init, + .class_size = sizeof(OtPlicExtClass), .class_init = &ot_plic_ext_class_init, }; diff --git a/include/hw/opentitan/ot_plic_ext.h b/include/hw/opentitan/ot_plic_ext.h index 0ca3afae21ad5..e6c29f692e5fb 100644 --- a/include/hw/opentitan/ot_plic_ext.h +++ b/include/hw/opentitan/ot_plic_ext.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan PLIC extension * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_PLIC_EXT "ot-plic_ext" -OBJECT_DECLARE_SIMPLE_TYPE(OtPlicExtState, OT_PLIC_EXT) +OBJECT_DECLARE_TYPE(OtPlicExtState, OtPlicExtClass, OT_PLIC_EXT) #endif /* HW_OPENTITAN_OT_PLIC_EXT_H */ From 28b06ee19a9375c9edb49a770d09e4a047776e30 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:40:43 +0200 Subject: [PATCH 53/69] [ot] hw/opentitan: ot_rstmgr: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_rstmgr.c | 91 +++++++++++++++++++++++--------- hw/opentitan/trace-events | 12 ++--- include/hw/opentitan/ot_rstmgr.h | 4 +- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index ebf1ab1a100e0..6cc0a74c77469 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -159,6 +159,11 @@ struct OtRstMgrState { bool por; /* Power-On Reset property */ }; +struct OtRstMgrClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + typedef struct { const char *typename; unsigned idx; @@ -233,6 +238,7 @@ static void ot_rstmgr_reset_bus(void *opaque) static int ot_rstmgr_sw_rst_walker(DeviceState *dev, void *opaque) { + OtRstMgrState *s = OT_RSTMGR(dev); OtRstMgrResetDesc *desc = opaque; int match = @@ -243,7 +249,7 @@ static int ot_rstmgr_sw_rst_walker(DeviceState *dev, void *opaque) return 0; } - trace_ot_rstmgr_sw_rst(desc->path, desc->reset); + trace_ot_rstmgr_sw_rst(s->ot_id, desc->path, desc->reset); if (desc->reset) { resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD); @@ -261,8 +267,9 @@ static void ot_rstmgr_update_sw_reset(OtRstMgrState *s, unsigned devix) const OtRstMgrResettable *rst = &SW_RESETTABLE_DEVICES[devix]; if (!rst->typename) { - qemu_log_mask(LOG_UNIMP, "%s: %s Reset for slot %u not yet implemented", - __func__, s->ot_id, devix); + qemu_log_mask(LOG_UNIMP, + "%s: %s: Reset for slot %u not yet implemented", __func__, + s->ot_id, devix); return; } @@ -271,7 +278,7 @@ static void ot_rstmgr_update_sw_reset(OtRstMgrState *s, unsigned devix) desc.path = g_strdup_printf("%s[%d]", rst->typename, rst->idx); desc.reset = !s->regs[R_SW_RST_CTRL_N_0 + devix]; - trace_ot_rstmgr_sw_reset(desc.path); + trace_ot_rstmgr_sw_reset(s->ot_id, desc.path); /* search for the device on the same local bus */ int res = @@ -304,7 +311,7 @@ static void ot_rstmgr_reset_req(void *opaque, int irq, int level) OtRstMgrResetReq req = (OtRstMgrResetReq)level; s->regs[R_RESET_INFO] = 1u << req; - trace_ot_rstmgr_reset_req(REQ_NAME(req), req, fastclk); + trace_ot_rstmgr_reset_req(s->ot_id, REQ_NAME(req), req, fastclk); qemu_bh_schedule(s->bus_reset_bh); } @@ -362,7 +369,8 @@ static uint64_t ot_rstmgr_regs_read(void *opaque, hwaddr addr, unsigned size) } uint32_t pc = ibex_get_current_pc(); - trace_ot_rstmgr_io_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_rstmgr_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); return (uint64_t)val32; }; @@ -377,7 +385,8 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, hwaddr reg = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_rstmgr_io_write((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_rstmgr_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); switch (reg) { case R_RESET_REQ: @@ -392,7 +401,7 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, if (s->fatal_reset) { s->fatal_reset--; if (!s->fatal_reset) { - error_report("fatal reset triggered"); + error_report("%s: fatal reset triggered", s->ot_id); qemu_system_shutdown_request_with_code( SHUTDOWN_CAUSE_GUEST_SHUTDOWN, 1); } @@ -499,27 +508,19 @@ static const MemoryRegionOps ot_rstmgr_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_rstmgr_reset(DeviceState *dev) +static void ot_rstmgr_reset_enter(Object *obj, ResetType type) { - OtRstMgrState *s = OT_RSTMGR(dev); - - if (!s->ot_id) { - s->ot_id = - g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); - } + OtRstMgrClass *c = OT_RSTMGR_GET_CLASS(obj); + OtRstMgrState *s = OT_RSTMGR(obj); - trace_ot_rstmgr_reset(); + trace_ot_rstmgr_reset(s->ot_id, "enter"); - if (!s->cpu) { - CPUState *cpu = ot_common_get_local_cpu(DEVICE(s)); - if (!cpu) { - error_setg(&error_fatal, "%s: Could not find the associated vCPU", - s->ot_id); - g_assert_not_reached(); - } - s->cpu = cpu; + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); } + qemu_bh_cancel(s->bus_reset_bh); + uint32_t reset_info = s->regs[R_RESET_INFO]; memset(s->regs, 0, REGS_SIZE); @@ -545,6 +546,39 @@ static void ot_rstmgr_reset(DeviceState *dev) ot_rstmgr_update_alerts(s); } +static void ot_rstmgr_reset_exit(Object *obj, ResetType type) +{ + OtRstMgrClass *c = OT_RSTMGR_GET_CLASS(obj); + OtRstMgrState *s = OT_RSTMGR(obj); + + trace_ot_rstmgr_reset(s->ot_id, "exit"); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + if (!s->cpu) { + CPUState *cpu = ot_common_get_local_cpu(DEVICE(s)); + if (!cpu) { + error_setg(&error_fatal, "%s: Could not find the associated vCPU", + s->ot_id); + g_assert_not_reached(); + } + s->cpu = cpu; + } +} + +static void ot_rstmgr_realize(DeviceState *dev, Error **errp) +{ + OtRstMgrState *s = OT_RSTMGR(dev); + (void)errp; + + if (!s->ot_id) { + s->ot_id = + g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); + } +} + static void ot_rstmgr_init(Object *obj) { OtRstMgrState *s = OT_RSTMGR(obj); @@ -570,9 +604,15 @@ static void ot_rstmgr_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_rstmgr_reset); + dc->realize = &ot_rstmgr_realize; device_class_set_props(dc, ot_rstmgr_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtRstMgrClass *mc = OT_RSTMGR_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_rstmgr_reset_enter, NULL, + &ot_rstmgr_reset_exit, + &mc->parent_phases); } static const TypeInfo ot_rstmgr_info = { @@ -580,6 +620,7 @@ static const TypeInfo ot_rstmgr_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtRstMgrState), .instance_init = &ot_rstmgr_init, + .class_size = sizeof(OtRstMgrClass), .class_init = &ot_rstmgr_class_init, }; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 29599b93dfcb1..5c8f1b4e6adac 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -410,12 +410,12 @@ ot_rom_ctrl_reset(const char *id, const char *phase) "%s: %s" # ot_rstmgr.c -ot_rstmgr_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_rstmgr_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_rstmgr_reset(void) "" -ot_rstmgr_reset_req(const char *req, unsigned reqix, bool fastclk) "%s(%u) @%u" -ot_rstmgr_sw_reset(const char *devpath) "SW reset %s" -ot_rstmgr_sw_rst(const char *path, bool reset) "%s: reset:%u" +ot_rstmgr_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_rstmgr_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_rstmgr_reset(const char *id, const char *phase) "%s: %s" +ot_rstmgr_reset_req(const char *id, const char *req, unsigned reqix, bool fastclk) "%s: %s(%u) @%u" +ot_rstmgr_sw_reset(const char *id, const char *devpath) "%s: SW reset %s" +ot_rstmgr_sw_rst(const char *id, const char *path, bool reset) "%s: %s: reset:%u" # ot_sensor.c diff --git a/include/hw/opentitan/ot_rstmgr.h b/include/hw/opentitan/ot_rstmgr.h index 546202d970bb6..15afe9a6c0c60 100644 --- a/include/hw/opentitan/ot_rstmgr.h +++ b/include/hw/opentitan/ot_rstmgr.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Reset Manager device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_RSTMGR "ot-rstmgr" -OBJECT_DECLARE_SIMPLE_TYPE(OtRstMgrState, OT_RSTMGR) +OBJECT_DECLARE_TYPE(OtRstMgrState, OtRstMgrClass, OT_RSTMGR) typedef enum { OT_RSTMGR_RESET_POR, From 4974994233d0f5d305c47913d35ebc191e9da168 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:44:03 +0200 Subject: [PATCH 54/69] [ot] hw/opentitan: ot_sensor: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_sensor_eg.c | 21 ++++++++++++++++++--- include/hw/opentitan/ot_sensor_eg.h | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/hw/opentitan/ot_sensor_eg.c b/hw/opentitan/ot_sensor_eg.c index 5332c3859b5e6..70841a57e2cfc 100644 --- a/hw/opentitan/ot_sensor_eg.c +++ b/hw/opentitan/ot_sensor_eg.c @@ -196,6 +196,11 @@ struct OtSensorEgState { uint32_t *regs; }; +struct OtSensorEgClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_sensor_eg_update_irqs(OtSensorEgState *s) { uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -390,9 +395,14 @@ static const MemoryRegionOps ot_sensor_eg_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_sensor_eg_reset(DeviceState *dev) +static void ot_sensor_eg_reset_enter(Object *obj, ResetType type) { - OtSensorEgState *s = OT_SENSOR_EG(dev); + OtSensorEgClass *c = OT_SENSOR_EG_GET_CLASS(obj); + OtSensorEgState *s = OT_SENSOR_EG(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(s->regs, 0, REGS_SIZE); @@ -431,9 +441,13 @@ static void ot_sensor_eg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_sensor_eg_reset); device_class_set_props(dc, ot_sensor_eg_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSensorEgClass *sc = OT_SENSOR_EG_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_sensor_eg_reset_enter, NULL, + NULL, &sc->parent_phases); } static const TypeInfo ot_sensor_eg_info = { @@ -441,6 +455,7 @@ static const TypeInfo ot_sensor_eg_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtSensorEgState), .instance_init = &ot_sensor_eg_init, + .class_size = sizeof(OtSensorEgClass), .class_init = &ot_sensor_eg_class_init, }; diff --git a/include/hw/opentitan/ot_sensor_eg.h b/include/hw/opentitan/ot_sensor_eg.h index b57895c0ecb90..696bbe6071bcd 100644 --- a/include/hw/opentitan/ot_sensor_eg.h +++ b/include/hw/opentitan/ot_sensor_eg.h @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_SENSOR_EG "ot-sensor-eg" -OBJECT_DECLARE_SIMPLE_TYPE(OtSensorEgState, OT_SENSOR_EG) +OBJECT_DECLARE_TYPE(OtSensorEgState, OtSensorEgClass, OT_SENSOR_EG) #endif /* HW_OPENTITAN_OT_SENSOR_EG_H */ From 37ef22478fcba66a9aa8f810a2f6dccefbb64ce3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:47:14 +0200 Subject: [PATCH 55/69] [ot] hw/opentitan: ot_soc_proxy: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_soc_proxy.c | 32 ++++++++++++++++++++++++----- include/hw/opentitan/ot_soc_proxy.h | 4 ++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_soc_proxy.c b/hw/opentitan/ot_soc_proxy.c index 328e8f22a59e7..269a5c705c04b 100644 --- a/hw/opentitan/ot_soc_proxy.c +++ b/hw/opentitan/ot_soc_proxy.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SocProxy * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -110,6 +110,11 @@ struct OtSoCProxyState { char *ot_id; }; +struct OtSoCProxyClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static void ot_soc_proxy_update_irqs(OtSoCProxyState *s) { uint32_t levels = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; @@ -234,11 +239,14 @@ static const MemoryRegionOps ot_soc_proxy_regs_ops = { .impl.max_access_size = 4u, }; -static void ot_soc_proxy_reset(DeviceState *dev) +static void ot_soc_proxy_reset_enter(Object *obj, ResetType type) { - OtSoCProxyState *s = OT_SOC_PROXY(dev); + OtSoCProxyClass *c = OT_SOC_PROXY_GET_CLASS(obj); + OtSoCProxyState *s = OT_SOC_PROXY(obj); - g_assert(s->ot_id); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(s->regs, 0, sizeof(s->regs)); @@ -246,6 +254,14 @@ static void ot_soc_proxy_reset(DeviceState *dev) ot_soc_proxy_update_alerts(s); } +static void ot_soc_proxy_realize(DeviceState *dev, Error **errp) +{ + OtSoCProxyState *s = OT_SOC_PROXY(dev); + (void)errp; + + g_assert(s->ot_id); +} + static void ot_soc_proxy_init(Object *obj) { OtSoCProxyState *s = OT_SOC_PROXY(obj); @@ -271,9 +287,14 @@ static void ot_soc_proxy_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_soc_proxy_reset); + dc->realize = &ot_soc_proxy_realize; device_class_set_props(dc, ot_soc_proxy_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSoCProxyClass *sc = OT_SOC_PROXY_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_soc_proxy_reset_enter, NULL, + NULL, &sc->parent_phases); } static const TypeInfo ot_soc_proxy_info = { @@ -281,6 +302,7 @@ static const TypeInfo ot_soc_proxy_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtSoCProxyState), .instance_init = &ot_soc_proxy_init, + .class_size = sizeof(OtSoCProxyClass), .class_init = &ot_soc_proxy_class_init, }; diff --git a/include/hw/opentitan/ot_soc_proxy.h b/include/hw/opentitan/ot_soc_proxy.h index 55694f5b2af26..99d320cc8c295 100644 --- a/include/hw/opentitan/ot_soc_proxy.h +++ b/include/hw/opentitan/ot_soc_proxy.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SocProxy * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_SOC_PROXY "ot-soc_proxy" -OBJECT_DECLARE_SIMPLE_TYPE(OtSoCProxyState, OT_SOC_PROXY) +OBJECT_DECLARE_TYPE(OtSoCProxyState, OtSoCProxyClass, OT_SOC_PROXY) #define OT_SOC_PROXY_REGS_COUNT 4u From 09ec27898075f95e6540963cee3d19ca0cfa443e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:51:37 +0200 Subject: [PATCH 56/69] [ot] hw/opentitan: ot_socdbg_ctrl: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_socdbg_ctrl.c | 34 ++++++++++++++++++++------- include/hw/opentitan/ot_socdbg_ctrl.h | 4 ++-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/hw/opentitan/ot_socdbg_ctrl.c b/hw/opentitan/ot_socdbg_ctrl.c index 5729dcadc8b00..df0b79d12f05d 100644 --- a/hw/opentitan/ot_socdbg_ctrl.c +++ b/hw/opentitan/ot_socdbg_ctrl.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SoC Debug Controller * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -160,6 +160,11 @@ struct OtSoCDbgCtrlState { bool dft_ignore; }; +struct OtSoCDbgCtrlClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define ROM_MASK ((1u << (R_BOOT_STATUS_ROM_CTRL_DONE_LENGTH - 1u)) - 1u) #define REG_NAME(_kind_, _reg_) \ @@ -731,10 +736,14 @@ static const MemoryRegionOps ot_socdbg_ctrl_dmi_ops = { .impl.max_access_size = 4u, }; -static void ot_socdbg_ctrl_reset_enter(Object *dev, ResetType type) +static void ot_socdbg_ctrl_reset_enter(Object *obj, ResetType type) { - OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(dev); - (void)type; + OtSoCDbgCtrlClass *c = OT_SOCDBG_CTRL_GET_CLASS(obj); + OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(s->regs, 0, sizeof(s->regs)); @@ -756,8 +765,14 @@ static void ot_socdbg_ctrl_reset_enter(Object *dev, ResetType type) static void ot_socdbg_ctrl_reset_exit(Object *obj, ResetType type) { + OtSoCDbgCtrlClass *c = OT_SOCDBG_CTRL_GET_CLASS(obj); OtSoCDbgCtrlState *s = OT_SOCDBG_CTRL(obj); - (void)type; + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + + qemu_bh_cancel(s->fsm_tick_bh); /* * ROM signal which does not comes from a ROM but from this device to @@ -825,9 +840,11 @@ static void ot_socdbg_ctrl_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, ot_socdbg_ctrl_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); - ResettableClass *rc = RESETTABLE_CLASS(dc); - rc->phases.enter = &ot_socdbg_ctrl_reset_enter; - rc->phases.exit = &ot_socdbg_ctrl_reset_exit; + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSoCDbgCtrlClass *sc = OT_SOCDBG_CTRL_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_socdbg_ctrl_reset_enter, NULL, + &ot_socdbg_ctrl_reset_exit, + &sc->parent_phases); } static const TypeInfo ot_socdbg_ctrl_info = { @@ -835,6 +852,7 @@ static const TypeInfo ot_socdbg_ctrl_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtSoCDbgCtrlState), .instance_init = &ot_socdbg_ctrl_init, + .class_size = sizeof(OtSoCDbgCtrlClass), .class_init = &ot_socdbg_ctrl_class_init, }; diff --git a/include/hw/opentitan/ot_socdbg_ctrl.h b/include/hw/opentitan/ot_socdbg_ctrl.h index 29fabaf11b43d..3976dc57327eb 100644 --- a/include/hw/opentitan/ot_socdbg_ctrl.h +++ b/include/hw/opentitan/ot_socdbg_ctrl.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SoC Debug Controller * - * Copyright (c) 2024 Rivos, Inc. + * Copyright (c) 2024-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -31,7 +31,7 @@ #include "qom/object.h" #define TYPE_OT_SOCDBG_CTRL "ot-socdbg_ctrl" -OBJECT_DECLARE_SIMPLE_TYPE(OtSoCDbgCtrlState, OT_SOCDBG_CTRL) +OBJECT_DECLARE_TYPE(OtSoCDbgCtrlState, OtSoCDbgCtrlClass, OT_SOCDBG_CTRL) /* SocDbg controller states */ typedef enum { From 2b4a9593ff5c340889d08a58f9a7c4c7ff844c79 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 14:57:03 +0200 Subject: [PATCH 57/69] [ot] hw/opentitan: ot_spi_device: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_device.c | 23 ++++++++++++++++++----- include/hw/opentitan/ot_spi_device.h | 4 ++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index eb9a4b70fa600..bed4795633386 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -447,6 +447,11 @@ struct OtSPIDeviceState { guint watch_tag; /* tracker for comm device change */ }; +struct OtSPIDeviceClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) #define R_SPI_LAST_REG (R_CMD_INFO_WRDI) @@ -2106,14 +2111,19 @@ static const MemoryRegionOps ot_spi_device_buf_ops = { .impl.max_access_size = 4u, }; -static void ot_spi_device_reset(DeviceState *dev) +static void ot_spi_device_reset_enter(Object *obj, ResetType type) { - OtSPIDeviceState *s = OT_SPI_DEVICE(dev); + OtSPIDeviceClass *c = OT_SPI_DEVICE_GET_CLASS(obj); + OtSPIDeviceState *s = OT_SPI_DEVICE(obj); SpiDeviceFlash *f = &s->flash; SpiDeviceBus *bus = &s->bus; trace_ot_spi_device_reset(s->ot_id, "enter"); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + ot_spi_device_clear_modes(s); memset(s->spi_regs, 0, SPI_REGS_SIZE); @@ -2138,8 +2148,6 @@ static void ot_spi_device_reset(DeviceState *dev) ot_spi_device_update_irqs(s); ot_spi_device_update_alerts(s); - - trace_ot_spi_device_reset(s->ot_id, "exit"); } static void ot_spi_device_realize(DeviceState *dev, Error **errp) @@ -2209,10 +2217,14 @@ static void ot_spi_device_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_spi_device_reset); dc->realize = &ot_spi_device_realize; device_class_set_props(dc, ot_spi_device_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSPIDeviceClass *sc = OT_SPI_DEVICE_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_spi_device_reset_enter, NULL, + NULL, &sc->parent_phases); } static const TypeInfo ot_spi_device_info = { @@ -2220,6 +2232,7 @@ static const TypeInfo ot_spi_device_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtSPIDeviceState), .instance_init = &ot_spi_device_init, + .class_size = sizeof(OtSPIDeviceClass), .class_init = &ot_spi_device_class_init, }; diff --git a/include/hw/opentitan/ot_spi_device.h b/include/hw/opentitan/ot_spi_device.h index 4de0717fb8b2c..9f5b40a713b4d 100644 --- a/include/hw/opentitan/ot_spi_device.h +++ b/include/hw/opentitan/ot_spi_device.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SPI Device controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_SPI_DEVICE "ot-spi_device" -OBJECT_DECLARE_SIMPLE_TYPE(OtSPIDeviceState, OT_SPI_DEVICE) +OBJECT_DECLARE_TYPE(OtSPIDeviceState, OtSPIDeviceClass, OT_SPI_DEVICE) #endif /* HW_OPENTITAN_OT_SPI_DEVICE_H */ From 8ef390583ca1356454d8f2dc12770c0f674fbde2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 15:00:23 +0200 Subject: [PATCH 58/69] [ot] hw/opentitan: ot_spi_host: update Resettable API implementation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_host.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_spi_host.c b/hw/opentitan/ot_spi_host.c index a456a039701c9..444b0da06bf3d 100644 --- a/hw/opentitan/ot_spi_host.c +++ b/hw/opentitan/ot_spi_host.c @@ -2,7 +2,7 @@ * QEMU OpenTitan SPI Host controller * * Copyright (C) 2022 Western Digital - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -1322,6 +1322,10 @@ static void ot_spi_host_realize(DeviceState *dev, Error **errp) (void)errp; g_assert(s->pclk); + if (!s->ot_id) { + s->ot_id = + g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); + } s->cs_lines = g_new0(qemu_irq, (size_t)s->num_cs); @@ -1371,6 +1375,7 @@ static void ot_spi_host_class_init(ObjectClass *klass, void *data) dc->realize = ot_spi_host_realize; device_class_set_props(dc, ot_spi_host_properties); + ResettableClass *rc = RESETTABLE_CLASS(klass); OtSPIHostClass *sc = OT_SPI_HOST_CLASS(klass); resettable_class_set_parent_phases(rc, &ot_spi_host_reset_enter, NULL, From 1c080c666fb06ae45b872546941cd9437ff0bbb6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 15:05:10 +0200 Subject: [PATCH 59/69] [ot] hw/opentitan: ot_sram_ctrl: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_sram_ctrl.c | 44 ++++++++++++++++++++++++----- include/hw/opentitan/ot_sram_ctrl.h | 4 +-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index 5717695146db8..aae904726dc51 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SRAM controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -145,6 +145,11 @@ struct OtSramCtrlState { bool noswitch; /* do not switch to performance/host RAM after init */ }; +struct OtSramCtrlClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + #ifdef OT_SRAM_CTRL_DEBUG #define TRACE_SRAM_CTRL(msg, ...) \ qemu_log("%s: " msg "\n", __func__, ##__VA_ARGS__); @@ -673,9 +678,17 @@ static const MemoryRegionOps ot_sram_ctrl_mem_init_ops = { .impl.max_access_size = 4u, }; -static void ot_sram_ctrl_reset(DeviceState *dev) +static void ot_sram_ctrl_reset_enter(Object *obj, ResetType type) { - OtSramCtrlState *s = OT_SRAM_CTRL(dev); + OtSramCtrlClass *c = OT_SRAM_CTRL_GET_CLASS(obj); + OtSramCtrlState *s = OT_SRAM_CTRL(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } + + timer_del(s->init_timer); + qemu_bh_cancel(s->switch_mr_bh); memset(s->regs, 0, REGS_SIZE); @@ -688,6 +701,20 @@ static void ot_sram_ctrl_reset(DeviceState *dev) s->regs[R_READBACK_REGWEN] = 0x1u; s->regs[R_READBACK] = OT_MULTIBITBOOL4_FALSE; + s->cfg_ifetch = 0u; /* not used for now */ + + ibex_irq_set(&s->alert, (int)(bool)s->regs[R_ALERT_TEST]); +} + +static void ot_sram_ctrl_reset_exit(Object *obj, ResetType type) +{ + OtSramCtrlClass *c = OT_SRAM_CTRL_GET_CLASS(obj); + OtSramCtrlState *s = OT_SRAM_CTRL(obj); + + if (c->parent_phases.exit) { + c->parent_phases.exit(obj, type); + } + if (s->otp_ctrl) { OtOTPClass *oc = OBJECT_GET_CLASS(OtOTPClass, s->otp_ctrl, TYPE_OT_OTP); s->otp_ifetch = oc->get_hw_cfg(s->otp_ctrl)->en_sram_ifetch == @@ -695,9 +722,6 @@ static void ot_sram_ctrl_reset(DeviceState *dev) } else { s->otp_ifetch = s->ifetch; } - s->cfg_ifetch = 0u; /* not used for now */ - - ibex_irq_set(&s->alert, (int)(bool)s->regs[R_ALERT_TEST]); int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); ot_prng_reseed(s->prng, (uint32_t)now); @@ -819,10 +843,15 @@ static void ot_sram_ctrl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_sram_ctrl_reset); dc->realize = &ot_sram_ctrl_realize; device_class_set_props(dc, ot_sram_ctrl_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtSramCtrlClass *sc = OT_SRAM_CTRL_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_sram_ctrl_reset_enter, NULL, + &ot_sram_ctrl_reset_exit, + &sc->parent_phases); } static const TypeInfo ot_sram_ctrl_info = { @@ -830,6 +859,7 @@ static const TypeInfo ot_sram_ctrl_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtSramCtrlState), .instance_init = &ot_sram_ctrl_init, + .class_size = sizeof(OtSramCtrlClass), .class_init = &ot_sram_ctrl_class_init, }; diff --git a/include/hw/opentitan/ot_sram_ctrl.h b/include/hw/opentitan/ot_sram_ctrl.h index 598daea582d88..9536f30b7c3f3 100644 --- a/include/hw/opentitan/ot_sram_ctrl.h +++ b/include/hw/opentitan/ot_sram_ctrl.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan SRAM controller * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_SRAM_CTRL "ot-sram_ctrl" -OBJECT_DECLARE_SIMPLE_TYPE(OtSramCtrlState, OT_SRAM_CTRL) +OBJECT_DECLARE_TYPE(OtSramCtrlState, OtSramCtrlClass, OT_SRAM_CTRL) #endif /* HW_OPENTITAN_OT_SRAM_CTRL */ From 820edbf773644f5652c235617a61c82abb0b92ce Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 15:08:30 +0200 Subject: [PATCH 60/69] [ot] hw/opentitan: ot_timer: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_timer.c | 24 +++++++++++++++++++----- include/hw/opentitan/ot_timer.h | 4 ++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_timer.c b/hw/opentitan/ot_timer.c index d1e951540e8af..38fbd5e04c674 100644 --- a/hw/opentitan/ot_timer.c +++ b/hw/opentitan/ot_timer.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Timer device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -95,6 +95,11 @@ struct OtTimerState { uint32_t pclk; }; +struct OtTimerClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint64_t ot_timer_ns_to_ticks(OtTimerState *s, int64_t ns) { uint32_t prescaler = FIELD_EX32(s->regs[R_CFG0], CFG0, PRESCALE); @@ -356,11 +361,14 @@ static Property ot_timer_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ot_timer_reset(DeviceState *dev) +static void ot_timer_reset_enter(Object *obj, ResetType type) { - OtTimerState *s = OT_TIMER(dev); + OtTimerClass *c = OT_TIMER_GET_CLASS(obj); + OtTimerState *s = OT_TIMER(obj); - g_assert(s->pclk > 0); + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } timer_del(s->timer); @@ -379,6 +387,7 @@ static void ot_timer_realize(DeviceState *dev, Error **errp) (void)errp; g_assert(s->ot_id); + g_assert(s->pclk > 0); } static void ot_timer_init(Object *obj) @@ -401,9 +410,13 @@ static void ot_timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; - device_class_set_legacy_reset(dc, &ot_timer_reset); dc->realize = &ot_timer_realize; device_class_set_props(dc, ot_timer_properties); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtTimerClass *tc = OT_TIMER_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_timer_reset_enter, NULL, NULL, + &tc->parent_phases); } static const TypeInfo ot_timer_info = { @@ -411,6 +424,7 @@ static const TypeInfo ot_timer_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtTimerState), .instance_init = &ot_timer_init, + .class_size = sizeof(OtTimerClass), .class_init = &ot_timer_class_init, }; diff --git a/include/hw/opentitan/ot_timer.h b/include/hw/opentitan/ot_timer.h index 0d19d1a2c323b..e043c9ba85630 100644 --- a/include/hw/opentitan/ot_timer.h +++ b/include/hw/opentitan/ot_timer.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Timer device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_TIMER "ot-timer" -OBJECT_DECLARE_SIMPLE_TYPE(OtTimerState, OT_TIMER) +OBJECT_DECLARE_TYPE(OtTimerState, OtTimerClass, OT_TIMER) #endif /* HW_OPENTITAN_OT_TIMER_H */ From 131c08fba80343db6a75eb6464af0b01f6e931e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 15:17:41 +0200 Subject: [PATCH 61/69] [ot] hw/opentitan: ot_uart: replace legacy reset API with Resettable API Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_uart.c | 24 ++++++++++++++++++++---- include/hw/opentitan/ot_uart.h | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_uart.c b/hw/opentitan/ot_uart.c index 61999bf8f5517..41261dea7da4e 100644 --- a/hw/opentitan/ot_uart.c +++ b/hw/opentitan/ot_uart.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan UART device * - * Copyright (c) 2022-2024 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * Copyright (c) 2025 lowRISC contributors. * * Author(s): @@ -162,6 +162,11 @@ struct OtUARTState { CharBackend chr; }; +struct OtUARTClass { + SysBusDeviceClass parent_class; + ResettablePhases parent_phases; +}; + static uint32_t ot_uart_get_tx_watermark_level(OtUARTState *s) { uint32_t tx_ilvl = (s->regs[R_FIFO_CTRL] & R_FIFO_CTRL_TXILVL_MASK) >> @@ -583,9 +588,14 @@ static int ot_uart_be_change(void *opaque) return 0; } -static void ot_uart_reset(DeviceState *dev) +static void ot_uart_reset_enter(Object *obj, ResetType type) { - OtUARTState *s = OT_UART(dev); + OtUARTClass *c = OT_UART_GET_CLASS(obj); + OtUARTState *s = OT_UART(obj); + + if (c->parent_phases.enter) { + c->parent_phases.enter(obj, type); + } memset(&s->regs[0], 0, sizeof(s->regs)); @@ -606,6 +616,7 @@ static void ot_uart_realize(DeviceState *dev, Error **errp) (void)errp; g_assert(s->ot_id); + g_assert(s->pclk); fifo8_create(&s->tx_fifo, OT_UART_TX_FIFO_SIZE); fifo8_create(&s->rx_fifo, OT_UART_RX_FIFO_SIZE); @@ -634,9 +645,13 @@ static void ot_uart_class_init(ObjectClass *klass, void *data) (void)data; dc->realize = ot_uart_realize; - device_class_set_legacy_reset(dc, ot_uart_reset); device_class_set_props(dc, ot_uart_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + + ResettableClass *rc = RESETTABLE_CLASS(klass); + OtUARTClass *uc = OT_UART_CLASS(klass); + resettable_class_set_parent_phases(rc, &ot_uart_reset_enter, NULL, NULL, + &uc->parent_phases); } static const TypeInfo ot_uart_info = { @@ -644,6 +659,7 @@ static const TypeInfo ot_uart_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtUARTState), .instance_init = ot_uart_init, + .class_size = sizeof(OtUARTClass), .class_init = ot_uart_class_init, }; diff --git a/include/hw/opentitan/ot_uart.h b/include/hw/opentitan/ot_uart.h index 923f194d68f91..f334f7b9ce06e 100644 --- a/include/hw/opentitan/ot_uart.h +++ b/include/hw/opentitan/ot_uart.h @@ -1,7 +1,7 @@ /* * QEMU OpenTitan UART device * - * Copyright (c) 2022-2023 Rivos, Inc. + * Copyright (c) 2022-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -31,6 +31,6 @@ #include "qom/object.h" #define TYPE_OT_UART "ot-uart" -OBJECT_DECLARE_SIMPLE_TYPE(OtUARTState, OT_UART) +OBJECT_DECLARE_TYPE(OtUARTState, OtUARTClass, OT_UART) #endif /* HW_OPENTITAN_OT_UART_H */ From c4033472412fbd99af8efd7792c6dbfefb080eec Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 18:17:29 +0200 Subject: [PATCH 62/69] [ot] hw/opentitan: ot_pwrmgr: add missing BH cancellation on reset Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_pwrmgr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index 3a2d15636c47e..f3e9fc5519988 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -969,6 +969,8 @@ static void ot_pwrmgr_reset_exit(Object *obj, ResetType type) c->parent_phases.exit(obj, type); } + qemu_bh_cancel(s->fsm_tick_bh); + ot_pwrmgr_schedule_fsm(s); } From 292a61e0e8000b584ee80a99bc820e1b5de554ea Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 16 Apr 2025 18:17:24 +0200 Subject: [PATCH 63/69] [ot] hw/opentitan: ot_otp_dj: add missing BH cancellation on reset Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index b82a6a7ebc033..fc6ec6afb9733 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3853,6 +3853,11 @@ static void ot_otp_dj_reset_enter(Object *obj, ResetType type) c->parent_phases.enter(obj, type); } + qemu_bh_cancel(s->dai->digest_bh); + qemu_bh_cancel(s->lci->prog_bh); + qemu_bh_cancel(s->lc_broadcast.bh); + qemu_bh_cancel(s->pwr_otp_bh); + timer_del(s->dai->delay); timer_del(s->lci->prog_delay); qemu_bh_cancel(s->keygen->entropy_bh); From 5a133f107c12accd67be627cc3a3401152afeeb3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 17 Apr 2025 13:37:26 +0200 Subject: [PATCH 64/69] [ot] hw/opentitan: ot_dma: abort on IDLE should raise the ABORT flag Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index c1a4c0429ad93..123d1816260d6 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -948,19 +948,19 @@ static bool ot_dma_go(OtDMAState *s) static void ot_dma_abort(OtDMAState *s) { - if (!ot_dma_is_busy(s)) { - /* nothing to do, but ABORTED be signaled? */ - return; - } - trace_ot_dma_abort(s->ot_id); s->abort = true; - /* simulate a delayed response */ timer_del(s->timer); + uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); - timer_mod(s->timer, (int64_t)(now + s->pace_delay)); + if (ot_dma_is_busy(s)) { + /* simulate a delayed response */ + now += s->pace_delay; + } + + timer_mod(s->timer, (int64_t)now); } static void ot_dma_complete(OtDMAState *s) From 82c854e663caba1b640a3ee0ee1e7ba62491a429 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 17 Apr 2025 17:56:58 +0200 Subject: [PATCH 65/69] [ot] hw/opentitan: ot_earlgrey: remove double device identification ot_common_configure_devices_with_id already takes care of identifying devices. Signed-off-by: Emmanuel Blot --- hw/riscv/ot_earlgrey.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 6ce825decfc3b..4600107a2f82d 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -1464,7 +1464,6 @@ ot_earlgrey_configure_verilator_devices(DeviceState **devices, BusState *bus, { ibex_link_devices(devices, defs, count); ibex_define_device_props(devices, defs, count); - ibex_identify_devices(devices, OT_COMMON_DEV_ID, "soc", false, count); ot_common_configure_device_opts(devices, count); ot_earlgrey_update_device_clocks(devices, count); ibex_realize_devices(devices, bus, defs, count); From c5328d1b5e27ea3c1b3df09e82ed2deb795778de Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 17 Apr 2025 18:01:40 +0200 Subject: [PATCH 66/69] [ot] hw/opentitan: ot_darjeeling, ot_earlgrey: use OT_COMMON_DEV_ID Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 22 ++++++++++++---------- hw/riscv/ot_earlgrey.c | 38 +++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index b551e7d6fbd66..6323ab24989cb 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -417,7 +417,8 @@ static const uint32_t ot_dj_pmp_addrs[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, (_irq_) + 2u), \ OT_DJ_SOC_GPIO_ALERT(0, (_alert_)), \ OT_DJ_SOC_GPIO_ALERT(1, (_alert_) + 1)), \ - .prop = IBEXDEVICEPROPDEFS(IBEX_DEV_STRING_PROP("ot_id", stringify(_ix_)), \ + .prop = IBEXDEVICEPROPDEFS(IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, \ + stringify(_ix_)), \ IBEX_DEV_STRING_PROP("ram_as_name", _asname_)) #define OT_DJ_SOC_DEV_MBX_DUAL(_ix_, _addr_, _asname_, _irq_, _alert_, \ @@ -429,7 +430,8 @@ static const uint32_t ot_dj_pmp_addrs[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, (_irq_) + 2u), \ OT_DJ_SOC_GPIO_ALERT(0, (_alert_)), \ OT_DJ_SOC_GPIO_ALERT(1, (_alert_) + 1)), \ - .prop = IBEXDEVICEPROPDEFS(IBEX_DEV_STRING_PROP("ot_id", stringify(_ix_)), \ + .prop = IBEXDEVICEPROPDEFS(IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, \ + stringify(_ix_)), \ IBEX_DEV_STRING_PROP("ram_as_name", _asname_)) #define OT_DJ_PINMUX_LINK(_type_, _name_, _tgt_, _num_) \ @@ -719,7 +721,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("size", 0x10000u), - IBEX_DEV_STRING_PROP("ot_id", "ram") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "ram") ), }, [OT_DJ_SOC_DEV_SRAM_MBX] = { @@ -736,7 +738,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("size", 0x1000u), - IBEX_DEV_STRING_PROP("ot_id", "mbx") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "mbx") ), }, [OT_DJ_SOC_DEV_ROM0] = { @@ -756,7 +758,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_DEVLINK("kmac", KMAC) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "rom0"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "rom0"), IBEX_DEV_UINT_PROP("size", 0x8000u), IBEX_DEV_UINT_PROP("kmac-app", 2u), IBEX_DEV_STRING_PROP("nonce", "a7bdb05fe921615b"), @@ -780,7 +782,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_DEVLINK("kmac", KMAC) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "rom1"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "rom1"), IBEX_DEV_UINT_PROP("size", 0x10000u), IBEX_DEV_UINT_PROP("kmac-app", 3u), IBEX_DEV_STRING_PROP("nonce", "ed2ed45545e927f6"), @@ -932,7 +934,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_ALERT(28, 51) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "0") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "0") ), }, [OT_DJ_SOC_DEV_MBX_PCIE0] = { @@ -1206,7 +1208,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_ALERT(0, 13) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "spi0"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "spi0"), IBEX_DEV_UINT_PROP("bus-num", 0), IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ) ), @@ -1356,13 +1358,13 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("size", 0x1000u), - IBEX_DEV_STRING_PROP("ot_id", "ret") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "ret") ), }, [OT_DJ_SOC_DEV_VMAPPER] = { .type = TYPE_OT_VMAPPER, .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "soc"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "soc"), IBEX_DEV_UINT_PROP("trans_count", OT_DJ_IBEX_WRAPPER_NUM_REGIONS) ), }, diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 4600107a2f82d..656247db064ae 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -390,7 +390,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 0) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "u0"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "u0"), IBEX_DEV_UINT_PROP("pclk", OT_EG_PERIPHERAL_CLK_HZ) ), }, @@ -414,7 +414,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 1) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "u1"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "u1"), IBEX_DEV_UINT_PROP("pclk", OT_EG_PERIPHERAL_CLK_HZ) ), }, @@ -438,7 +438,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 2) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "u2"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "u2"), IBEX_DEV_UINT_PROP("pclk", OT_EG_PERIPHERAL_CLK_HZ) ), }, @@ -462,7 +462,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 3) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "u3"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "u3"), IBEX_DEV_UINT_PROP("pclk", OT_EG_PERIPHERAL_CLK_HZ) ), }, @@ -532,7 +532,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40080000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "i2c0"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "i2c0"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -549,7 +549,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40090000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "i2c1"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "i2c1"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -566,7 +566,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x400a0000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "i2c2"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "i2c2"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 15u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -583,7 +583,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x400e0000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "pattgen"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "pattgen"), IBEX_DEV_UINT_PROP("size", 0x40u), IBEX_DEV_UINT_PROP("irq-count", 2u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -713,7 +713,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 19) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "spi0"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "spi0"), IBEX_DEV_UINT_PROP("bus-num", 0), IBEX_DEV_UINT_PROP("pclk", OT_EG_CORE_CLK_HZ) ), @@ -729,7 +729,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_ALERT(0, 20) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "spi1"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "spi1"), IBEX_DEV_UINT_PROP("bus-num", 1), IBEX_DEV_UINT_PROP("pclk", OT_EG_CORE_CLK_HZ) ), @@ -741,7 +741,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40320000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "usbdev"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "usbdev"), IBEX_DEV_UINT_PROP("size", 0x1000u), IBEX_DEV_UINT_PROP("irq-count", 18u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -803,7 +803,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40430000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "sysrst_ctrl"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "sysrst_ctrl"), IBEX_DEV_UINT_PROP("size", 0x100u), IBEX_DEV_UINT_PROP("irq-count", 1u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -820,7 +820,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40440000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "adc_ctrl"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "adc_ctrl"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("irq-count", 1u), IBEX_DEV_UINT_PROP("alert-count", 1u), @@ -837,7 +837,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x40450000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "pwm"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "pwm"), IBEX_DEV_UINT_PROP("size", 0x80u), IBEX_DEV_UINT_PROP("alert-count", 1u), IBEX_DEV_BOOL_PROP("warn-once", true) @@ -903,7 +903,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("size", 0x1000u), - IBEX_DEV_STRING_PROP("ot_id", "ret") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "ret") ), }, [OT_EG_SOC_DEV_FLASH_CTRL] = { @@ -1005,7 +1005,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { { .base = 0x41140000u } ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "keymgr"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "keymgr"), IBEX_DEV_UINT_PROP("size", 0x100u), IBEX_DEV_UINT_PROP("irq-count", 1u), IBEX_DEV_UINT_PROP("alert-count", 2u), @@ -1102,7 +1102,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("size", SRAM_MAIN_SIZE), - IBEX_DEV_STRING_PROP("ot_id", "ram") + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "ram") ), }, [OT_EG_SOC_DEV_ROM_CTRL] = { @@ -1122,7 +1122,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_DEVLINK("kmac", KMAC) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "rom"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "rom"), IBEX_DEV_UINT_PROP("size", 0x8000u), IBEX_DEV_UINT_PROP("kmac-app", 2u), /* Earlgrey-M2.5.2-RC0 */ @@ -1202,7 +1202,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { [OT_EG_SOC_DEV_VMAPPER] = { .type = TYPE_OT_VMAPPER, .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "soc"), + IBEX_DEV_STRING_PROP(OT_COMMON_DEV_ID, "soc"), IBEX_DEV_UINT_PROP("trans_count", OT_EG_IBEX_WRAPPER_NUM_REGIONS) ), } From de016d898105ed344fcbe30abf97421c17e0b2e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 17 Apr 2025 18:16:32 +0200 Subject: [PATCH 67/69] [ot] hw/opentitan: use OT_COMMON_DEV_ID in OT device properties Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_alert.c | 2 +- hw/opentitan/ot_aon_timer.c | 2 +- hw/opentitan/ot_common.c | 4 ++-- hw/opentitan/ot_dev_proxy.c | 11 ++++++----- hw/opentitan/ot_dma.c | 2 +- hw/opentitan/ot_gpio_dj.c | 2 +- hw/opentitan/ot_gpio_eg.c | 2 +- hw/opentitan/ot_hmac.c | 2 +- hw/opentitan/ot_i2c_dj.c | 3 ++- hw/opentitan/ot_ibex_wrapper.c | 2 +- hw/opentitan/ot_lc_ctrl.c | 2 +- hw/opentitan/ot_mbx.c | 2 +- hw/opentitan/ot_otp_dj.c | 2 +- hw/opentitan/ot_otp_eg.c | 2 +- hw/opentitan/ot_otp_ot_be.c | 3 ++- hw/opentitan/ot_plic_ext.c | 2 +- hw/opentitan/ot_pwrmgr.c | 4 ++-- hw/opentitan/ot_rom_ctrl.c | 4 ++-- hw/opentitan/ot_rstmgr.c | 2 +- hw/opentitan/ot_soc_proxy.c | 3 ++- hw/opentitan/ot_socdbg_ctrl.c | 3 ++- hw/opentitan/ot_spi_device.c | 2 +- hw/opentitan/ot_spi_host.c | 2 +- hw/opentitan/ot_sram_ctrl.c | 2 +- hw/opentitan/ot_timer.c | 2 +- hw/opentitan/ot_uart.c | 3 ++- hw/opentitan/ot_unimp.c | 2 +- hw/opentitan/ot_vmapper.c | 3 ++- 28 files changed, 42 insertions(+), 35 deletions(-) diff --git a/hw/opentitan/ot_alert.c b/hw/opentitan/ot_alert.c index 5168737e3cded..f04593fb627b3 100644 --- a/hw/opentitan/ot_alert.c +++ b/hw/opentitan/ot_alert.c @@ -960,7 +960,7 @@ static void ot_alert_fill_access_table(OtAlertState *s) } static Property ot_alert_properties[] = { - DEFINE_PROP_STRING("ot_id", OtAlertState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtAlertState, ot_id), DEFINE_PROP_UINT16("n_alerts", OtAlertState, n_alerts, 0), DEFINE_PROP_UINT8("n_lpg", OtAlertState, n_low_power_groups, 1u), DEFINE_PROP_UINT8("n_classes", OtAlertState, n_classes, 4u), diff --git a/hw/opentitan/ot_aon_timer.c b/hw/opentitan/ot_aon_timer.c index b0b7a7ccd9d4a..db8e4d4153c89 100644 --- a/hw/opentitan/ot_aon_timer.c +++ b/hw/opentitan/ot_aon_timer.c @@ -510,7 +510,7 @@ static const MemoryRegionOps ot_aon_timer_ops = { }; static Property ot_aon_timer_properties[] = { - DEFINE_PROP_STRING("ot_id", OtAonTimerState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtAonTimerState, ot_id), DEFINE_PROP_UINT32("pclk", OtAonTimerState, pclk, 0u), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_common.c b/hw/opentitan/ot_common.c index 32ff99042c597..9afe83614f945 100644 --- a/hw/opentitan/ot_common.c +++ b/hw/opentitan/ot_common.c @@ -160,8 +160,8 @@ unsigned ot_common_check_rom_configuration(void) OtCommonObjectNode *ctrl_node, *ctrl_next; QSIMPLEQ_FOREACH_SAFE(ctrl_node, &ctrl_nodes.list, node, ctrl_next) { Error *errp; - char *ot_id = - object_property_get_str(ctrl_node->obj, "ot_id", &errp); + char *ot_id = object_property_get_str(ctrl_node->obj, + OT_COMMON_DEV_ID, &errp); bool remove_img = !ot_id; bool remove_ctrl = false; if (ot_id) { diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index 95bd603153bf6..de296fa4be22a 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -369,7 +369,8 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) for (unsigned dix = 0; dix < ARRAY_SIZE(SUPPORTED_DEVICES); dix++) { if (object_dynamic_cast(item->obj, SUPPORTED_DEVICES[dix].typename)) { - oid = object_property_get_str(item->obj, "ot_id", &error_fatal); + oid = object_property_get_str(item->obj, OT_COMMON_DEV_ID, + &error_fatal); (void)snprintf(desc, sizeof(desc), "%s%s", item->prefix, oid); break; } @@ -1031,7 +1032,7 @@ ot_dev_proxy_route_interrupt(OtDevProxyState *s, OtDevProxyItem *item, const char *group, unsigned grp_n, unsigned irq_n) { const char *dev_name = object_get_typename(item->obj); - char *dev_id = object_property_get_str(item->obj, "ot_id", NULL); + char *dev_id = object_property_get_str(item->obj, OT_COMMON_DEV_ID, NULL); char *irq_name = g_strdup_printf("%s[%u]", group, irq_n); OtDevProxyIrq *proxy_irq = @@ -1084,7 +1085,7 @@ static void ot_dev_proxy_restore_interrupt(OtDevProxyItem *item, const char *group, unsigned irq_n) { const char *dev_name = object_get_typename(item->obj); - char *dev_id = object_property_get_str(item->obj, "ot_id", NULL); + char *dev_id = object_property_get_str(item->obj, OT_COMMON_DEV_ID, NULL); char *irq_name = g_strdup_printf("%s[%u]", group, irq_n); if (!item->iirq_ht) { @@ -1271,7 +1272,7 @@ static void ot_dev_proxy_signal_interrupt(OtDevProxyState *s) const char *dev_name = object_get_typename(item->obj); Error *errp = NULL; - char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); + char *dev_id = object_property_get_str(item->obj, OT_COMMON_DEV_ID, &errp); if (errp) { error_free(errp); } @@ -1444,7 +1445,7 @@ static void ot_dev_proxy_intercepted_irq(void *opaque, int irq, int level) const char *dev_name = object_get_typename(item->obj); Error *errp = NULL; - char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); + char *dev_id = object_property_get_str(item->obj, OT_COMMON_DEV_ID, &errp); if (errp) { error_free(errp); } diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 123d1816260d6..2101dc8d833ef 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -1326,7 +1326,7 @@ static void ot_dma_regs_write(void *opaque, hwaddr addr, uint64_t val64, }; static Property ot_dma_properties[] = { - DEFINE_PROP_STRING("ot_id", OtDMAState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtDMAState, ot_id), DEFINE_PROP_STRING("ot_as_name", OtDMAState, ot_as_name), DEFINE_PROP_STRING("ctn_as_name", OtDMAState, ctn_as_name), DEFINE_PROP_STRING("sys_as_name", OtDMAState, sys_as_name), diff --git a/hw/opentitan/ot_gpio_dj.c b/hw/opentitan/ot_gpio_dj.c index 80aa8480d7abb..c8570e7cee90a 100644 --- a/hw/opentitan/ot_gpio_dj.c +++ b/hw/opentitan/ot_gpio_dj.c @@ -798,7 +798,7 @@ static const MemoryRegionOps ot_gpio_dj_regs_ops = { }; static Property ot_gpio_dj_properties[] = { - DEFINE_PROP_STRING("ot_id", OtGpioDjState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtGpioDjState, ot_id), DEFINE_PROP_STRING("log_id", OtGpioDjState, log_id), DEFINE_PROP_UINT32("in", OtGpioDjState, reset_in, 0u), DEFINE_PROP_UINT32("out", OtGpioDjState, reset_out, 0u), diff --git a/hw/opentitan/ot_gpio_eg.c b/hw/opentitan/ot_gpio_eg.c index 5a338b8655b73..7f51c23064a30 100644 --- a/hw/opentitan/ot_gpio_eg.c +++ b/hw/opentitan/ot_gpio_eg.c @@ -697,7 +697,7 @@ static const MemoryRegionOps ot_gpio_eg_regs_ops = { }; static Property ot_gpio_eg_properties[] = { - DEFINE_PROP_STRING("ot_id", OtGpioEgState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtGpioEgState, ot_id), DEFINE_PROP_UINT32("in", OtGpioEgState, reset_in, 0u), DEFINE_PROP_UINT32("out", OtGpioEgState, reset_out, 0u), DEFINE_PROP_UINT32("oe", OtGpioEgState, reset_oe, 0u), diff --git a/hw/opentitan/ot_hmac.c b/hw/opentitan/ot_hmac.c index 1cdd82e670787..2503d43d65989 100644 --- a/hw/opentitan/ot_hmac.c +++ b/hw/opentitan/ot_hmac.c @@ -1188,7 +1188,7 @@ static void ot_hmac_regs_write(void *opaque, hwaddr addr, uint64_t value, } static Property ot_hmac_properties[] = { - DEFINE_PROP_STRING("ot_id", OtHMACState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtHMACState, ot_id), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_i2c_dj.c b/hw/opentitan/ot_i2c_dj.c index bb4cbc1559930..b25a78474f972 100644 --- a/hw/opentitan/ot_i2c_dj.c +++ b/hw/opentitan/ot_i2c_dj.c @@ -55,6 +55,7 @@ #include "qemu/log.h" #include "hw/i2c/i2c.h" #include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_fifo32.h" #include "hw/opentitan/ot_i2c_dj.h" #include "hw/qdev-clock.h" @@ -955,7 +956,7 @@ static const MemoryRegionOps ot_i2c_dj_ops = { }; static Property ot_i2c_dj_properties[] = { - DEFINE_PROP_STRING("ot_id", OtI2CDjState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtI2CDjState, ot_id), DEFINE_PROP_UINT32("pclk", OtI2CDjState, pclk, 0u), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_ibex_wrapper.c b/hw/opentitan/ot_ibex_wrapper.c index db7220bc2a97a..0dd9729961275 100644 --- a/hw/opentitan/ot_ibex_wrapper.c +++ b/hw/opentitan/ot_ibex_wrapper.c @@ -1366,7 +1366,7 @@ static void ot_ibex_wrapper_fill_tables(OtIbexWrapperState *s) /* all properties are optional */ static Property ot_ibex_wrapper_properties[] = { - DEFINE_PROP_STRING("ot_id", OtIbexWrapperState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtIbexWrapperState, ot_id), DEFINE_PROP_LINK("edn", OtIbexWrapperState, edn, TYPE_OT_EDN, OtEDNState *), DEFINE_PROP_LINK("vmapper", OtIbexWrapperState, vmapper, TYPE_OT_VMAPPER, OtVMapperState *), diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index b66ea382c88ca..98843b20230f8 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -2043,7 +2043,7 @@ static void ot_lc_ctrl_configure_transitions( } static Property ot_lc_ctrl_properties[] = { - DEFINE_PROP_STRING("ot_id", OtLcCtrlState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtLcCtrlState, ot_id), DEFINE_PROP_LINK("otp_ctrl", OtLcCtrlState, otp_ctrl, TYPE_OT_OTP, OtOTPState *), DEFINE_PROP_LINK("kmac", OtLcCtrlState, kmac, TYPE_OT_KMAC, OtKMACState *), diff --git a/hw/opentitan/ot_mbx.c b/hw/opentitan/ot_mbx.c index 3e95b8f93e11b..663aef14fc94f 100644 --- a/hw/opentitan/ot_mbx.c +++ b/hw/opentitan/ot_mbx.c @@ -730,7 +730,7 @@ static MemTxResult ot_mbx_sys_regs_write_with_attrs( } static Property ot_mbx_properties[] = { - DEFINE_PROP_STRING("ot_id", OtMbxState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtMbxState, ot_id), DEFINE_PROP_STRING("ram_as_name", OtMbxState, ram_as_name), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index fc6ec6afb9733..46981a4a1d6fa 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3793,7 +3793,7 @@ static void ot_otp_dj_configure_sram(OtOTPDjState *s) } static Property ot_otp_dj_properties[] = { - DEFINE_PROP_STRING("ot_id", OtOTPDjState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOTPDjState, ot_id), DEFINE_PROP_DRIVE("drive", OtOTPDjState, blk), DEFINE_PROP_LINK("backend", OtOTPDjState, otp_backend, TYPE_OT_OTP_BE_IF, OtOtpBeIf *), diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index e44ed2fa5dafe..d473997ffce98 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -1226,7 +1226,7 @@ ot_otp_eg_ctrl_get_entropy_cfg(const OtOTPState *s) } static Property ot_otp_eg_properties[] = { - DEFINE_PROP_STRING("ot_id", OtOTPEgState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOTPEgState, ot_id), DEFINE_PROP_DRIVE("drive", OtOTPEgState, blk), DEFINE_PROP_LINK("backend", OtOTPEgState, otp_backend, TYPE_OT_OTP_BE_IF, OtOtpBeIf *), diff --git a/hw/opentitan/ot_otp_ot_be.c b/hw/opentitan/ot_otp_ot_be.c index e71fbf9005eae..408ea57daf2f8 100644 --- a/hw/opentitan/ot_otp_ot_be.c +++ b/hw/opentitan/ot_otp_ot_be.c @@ -30,6 +30,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_otp.h" #include "hw/opentitan/ot_otp_be_if.h" #include "hw/opentitan/ot_otp_ot_be.h" @@ -203,7 +204,7 @@ static bool ot_otp_ot_be_is_ecc_enabled(OtOtpBeIf *beif) } static Property ot_otp_ot_be_properties[] = { - DEFINE_PROP_STRING("ot_id", OtOtpOtBeState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtOtpOtBeState, ot_id), DEFINE_PROP_LINK("parent", OtOtpOtBeState, parent, TYPE_DEVICE, DeviceState*), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/opentitan/ot_plic_ext.c b/hw/opentitan/ot_plic_ext.c index 7565b851b97ea..b40036c91f378 100644 --- a/hw/opentitan/ot_plic_ext.c +++ b/hw/opentitan/ot_plic_ext.c @@ -214,7 +214,7 @@ static void ot_plic_ext_alert_write(void *opaque, hwaddr addr, uint64_t val64, } static Property ot_plic_ext_properties[] = { - DEFINE_PROP_STRING("ot_id", OtPlicExtState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtPlicExtState, ot_id), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index f3e9fc5519988..02da762d88ee3 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan Power Manager device * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -899,7 +899,7 @@ static void ot_pwrmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, }; static Property ot_pwrmgr_properties[] = { - DEFINE_PROP_STRING("ot_id", OtPwrMgrState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtPwrMgrState, ot_id), DEFINE_PROP_UINT8("num-rom", OtPwrMgrState, num_rom, 0), DEFINE_PROP_UINT8("version", OtPwrMgrState, version, UINT8_MAX), DEFINE_PROP_BOOL("fetch-ctrl", OtPwrMgrState, fetch_ctrl, false), diff --git a/hw/opentitan/ot_rom_ctrl.c b/hw/opentitan/ot_rom_ctrl.c index ca1a1540acab7..775cfe83847f8 100644 --- a/hw/opentitan/ot_rom_ctrl.c +++ b/hw/opentitan/ot_rom_ctrl.c @@ -1,7 +1,7 @@ /* * QEMU OpenTitan ROM controller * - * Copyright (c) 2023-2024 Rivos, Inc. + * Copyright (c) 2023-2025 Rivos, Inc. * * Author(s): * Loïc Lefort @@ -1041,7 +1041,7 @@ static void ot_rom_ctrl_parse_hexstr(const char *name, uint8_t **buf, } static Property ot_rom_ctrl_properties[] = { - DEFINE_PROP_STRING("ot_id", OtRomCtrlState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtRomCtrlState, ot_id), DEFINE_PROP_UINT32("size", OtRomCtrlState, size, 0u), DEFINE_PROP_LINK("kmac", OtRomCtrlState, kmac, TYPE_OT_KMAC, OtKMACState *), DEFINE_PROP_UINT8("kmac-app", OtRomCtrlState, kmac_app, UINT8_MAX), diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 6cc0a74c77469..9412db0f1f6fa 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -493,7 +493,7 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, }; static Property ot_rstmgr_properties[] = { - DEFINE_PROP_STRING("ot_id", OtRstMgrState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtRstMgrState, ot_id), DEFINE_PROP_UINT32("fatal_reset", OtRstMgrState, fatal_reset, 0), /* this property is only used to store initial reset reason state */ DEFINE_PROP_BOOL("por", OtRstMgrState, por, true), diff --git a/hw/opentitan/ot_soc_proxy.c b/hw/opentitan/ot_soc_proxy.c index 269a5c705c04b..e9d0612fd44f1 100644 --- a/hw/opentitan/ot_soc_proxy.c +++ b/hw/opentitan/ot_soc_proxy.c @@ -35,6 +35,7 @@ #include "qemu/log.h" #include "exec/memory.h" #include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_soc_proxy.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" @@ -227,7 +228,7 @@ static void ot_soc_proxy_regs_write(void *opaque, hwaddr addr, uint64_t val64, } static Property ot_soc_proxy_properties[] = { - DEFINE_PROP_STRING("ot_id", OtSoCProxyState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSoCProxyState, ot_id), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_socdbg_ctrl.c b/hw/opentitan/ot_socdbg_ctrl.c index df0b79d12f05d..3dd495f8c8915 100644 --- a/hw/opentitan/ot_socdbg_ctrl.c +++ b/hw/opentitan/ot_socdbg_ctrl.c @@ -30,6 +30,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_lc_ctrl.h" #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_socdbg_ctrl.h" @@ -710,7 +711,7 @@ static void ot_socdbg_ctrl_dmi_write(void *opaque, hwaddr addr, uint64_t value, } static Property ot_socdbg_ctrl_properties[] = { - DEFINE_PROP_STRING("ot_id", OtSoCDbgCtrlState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSoCDbgCtrlState, ot_id), DEFINE_PROP_UINT8("dbg_unlocked", OtSoCDbgCtrlState, dbg_unlocked, DEFAULT_DBG_UNLOCKED), DEFINE_PROP_UINT8("dbg_locked", OtSoCDbgCtrlState, dbg_locked, diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index bed4795633386..af5514a7d58ad 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -2082,7 +2082,7 @@ static int ot_spi_device_chr_be_change(void *opaque) } static Property ot_spi_device_properties[] = { - DEFINE_PROP_STRING("ot_id", OtSPIDeviceState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSPIDeviceState, ot_id), DEFINE_PROP_CHR("chardev", OtSPIDeviceState, chr), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_spi_host.c b/hw/opentitan/ot_spi_host.c index 444b0da06bf3d..0d2c5e06f27d4 100644 --- a/hw/opentitan/ot_spi_host.c +++ b/hw/opentitan/ot_spi_host.c @@ -1269,7 +1269,7 @@ static const MemoryRegionOps ot_spi_host_ops = { /* clang-format on */ static Property ot_spi_host_properties[] = { - DEFINE_PROP_STRING("ot_id", OtSPIHostState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSPIHostState, ot_id), DEFINE_PROP_UINT32("num-cs", OtSPIHostState, num_cs, 1u), DEFINE_PROP_UINT32("bus-num", OtSPIHostState, bus_num, 0u), DEFINE_PROP_UINT32("pclk", OtSPIHostState, pclk, 0u), diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index aae904726dc51..2f7180de10c7b 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -651,7 +651,7 @@ static MemTxResult ot_sram_ctrl_mem_init_write_with_attrs( } static Property ot_sram_ctrl_properties[] = { - DEFINE_PROP_STRING("ot_id", OtSramCtrlState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtSramCtrlState, ot_id), DEFINE_PROP_LINK("otp_ctrl", OtSramCtrlState, otp_ctrl, TYPE_OT_OTP, OtOTPState *), DEFINE_PROP_UINT32("size", OtSramCtrlState, size, 0u), diff --git a/hw/opentitan/ot_timer.c b/hw/opentitan/ot_timer.c index 38fbd5e04c674..f5e6f923eacec 100644 --- a/hw/opentitan/ot_timer.c +++ b/hw/opentitan/ot_timer.c @@ -356,7 +356,7 @@ static const MemoryRegionOps ot_timer_ops = { }; static Property ot_timer_properties[] = { - DEFINE_PROP_STRING("ot_id", OtTimerState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtTimerState, ot_id), DEFINE_PROP_UINT32("pclk", OtTimerState, pclk, 0u), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/opentitan/ot_uart.c b/hw/opentitan/ot_uart.c index 41261dea7da4e..ee636d01c8b78 100644 --- a/hw/opentitan/ot_uart.c +++ b/hw/opentitan/ot_uart.c @@ -36,6 +36,7 @@ #include "qemu/module.h" #include "chardev/char-fe.h" #include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_uart.h" #include "hw/qdev-properties-system.h" #include "hw/qdev-properties.h" @@ -565,7 +566,7 @@ static const MemoryRegionOps ot_uart_ops = { }; static Property ot_uart_properties[] = { - DEFINE_PROP_STRING("ot_id", OtUARTState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtUARTState, ot_id), DEFINE_PROP_CHR("chardev", OtUARTState, chr), DEFINE_PROP_UINT32("pclk", OtUARTState, pclk, 0u), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/opentitan/ot_unimp.c b/hw/opentitan/ot_unimp.c index 038e0f6165490..6e0777d66ed5a 100644 --- a/hw/opentitan/ot_unimp.c +++ b/hw/opentitan/ot_unimp.c @@ -222,7 +222,7 @@ static const MemoryRegionOps ot_unimp_ops = { /* clang-format on */ static Property ot_unimp_properties[] = { - DEFINE_PROP_STRING("ot_id", OtUnimpState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtUnimpState, ot_id), DEFINE_PROP_UINT32("size", OtUnimpState, size, 0), DEFINE_PROP_UINT32("irq-ro-mask", OtUnimpState, irq_ro_mask, 0), DEFINE_PROP_UINT8("irq-count", OtUnimpState, irq_count, 0), diff --git a/hw/opentitan/ot_vmapper.c b/hw/opentitan/ot_vmapper.c index ff89bdd611ba0..8a923e1e9aed3 100644 --- a/hw/opentitan/ot_vmapper.c +++ b/hw/opentitan/ot_vmapper.c @@ -31,6 +31,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "hw/boards.h" +#include "hw/opentitan/ot_common.h" #include "hw/opentitan/ot_vmapper.h" #include "hw/qdev-properties.h" #include "hw/riscv/ibex_common.h" @@ -712,7 +713,7 @@ static void ot_vmapper_override_vcpu_config(OtVMapperState *s) } static Property ot_vmapper_properties[] = { - DEFINE_PROP_STRING("ot_id", OtVMapperState, ot_id), + DEFINE_PROP_STRING(OT_COMMON_DEV_ID, OtVMapperState, ot_id), DEFINE_PROP_UINT8("cpu_index", OtVMapperState, cpu_idx, UINT8_MAX), DEFINE_PROP_UINT8("trans_count", OtVMapperState, trans_count, UINT8_MAX), DEFINE_PROP_END_OF_LIST(), From 05be3d388815ed9888f970a78b6af7df98f78cf4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 18 Apr 2025 12:05:33 +0200 Subject: [PATCH 68/69] [ot] hw/opentitan: all: remove default ot_id string creation. The parent should specify this property. Signed-off-by: Emmanuel Blot (cherry picked from commit 851989ebaaa14c02b366eeeb785b75ebb8aa8151) --- hw/opentitan/ot_gpio_eg.c | 9 ++------- hw/opentitan/ot_otp_dj.c | 7 ++----- hw/opentitan/ot_rstmgr.c | 5 +---- hw/opentitan/ot_spi_host.c | 5 +---- hw/opentitan/ot_unimp.c | 5 +---- 5 files changed, 7 insertions(+), 24 deletions(-) diff --git a/hw/opentitan/ot_gpio_eg.c b/hw/opentitan/ot_gpio_eg.c index 7f51c23064a30..f6b8e2591e644 100644 --- a/hw/opentitan/ot_gpio_eg.c +++ b/hw/opentitan/ot_gpio_eg.c @@ -147,9 +147,6 @@ struct OtGpioEgClass { ResettablePhases parent_phases; }; - -static const char DEFAULT_OT_ID[] = ""; - static void ot_gpio_eg_update_backend(OtGpioEgState *s, bool force); static void ot_gpio_eg_update_irqs(OtGpioEgState *s) @@ -711,10 +708,6 @@ static void ot_gpio_eg_reset_enter(Object *obj, ResetType type) OtGpioEgClass *c = OT_GPIO_EG_GET_CLASS(obj); OtGpioEgState *s = OT_GPIO_EG(obj); - if (!s->ot_id) { - s->ot_id = g_strdup(DEFAULT_OT_ID); - } - trace_ot_gpio_reset(s->ot_id, "> enter"); if (c->parent_phases.enter) { c->parent_phases.enter(obj, type); @@ -777,6 +770,8 @@ static void ot_gpio_eg_realize(DeviceState *dev, Error **errp) OtGpioEgState *s = OT_GPIO_EG(dev); (void)errp; + g_assert(s->ot_id); + qemu_chr_fe_set_handlers(&s->chr, &ot_gpio_eg_chr_can_receive, &ot_gpio_eg_chr_receive, &ot_gpio_eg_chr_event_hander, diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 46981a4a1d6fa..b9d6dae384ea4 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3933,13 +3933,10 @@ static void ot_otp_dj_reset_exit(Object *obj, ResetType type) static void ot_otp_dj_realize(DeviceState *dev, Error **errp) { - (void)errp; OtOTPDjState *s = OT_OTP_DJ(dev); + (void)errp; - if (!s->ot_id) { - s->ot_id = - g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); - } + g_assert(s->ot_id); ot_otp_dj_configure_scrmbl_key(s); ot_otp_dj_configure_digest(s); diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 9412db0f1f6fa..54c1507689133 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -573,10 +573,7 @@ static void ot_rstmgr_realize(DeviceState *dev, Error **errp) OtRstMgrState *s = OT_RSTMGR(dev); (void)errp; - if (!s->ot_id) { - s->ot_id = - g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); - } + g_assert(s->ot_id); } static void ot_rstmgr_init(Object *obj) diff --git a/hw/opentitan/ot_spi_host.c b/hw/opentitan/ot_spi_host.c index 0d2c5e06f27d4..e782aef0cb011 100644 --- a/hw/opentitan/ot_spi_host.c +++ b/hw/opentitan/ot_spi_host.c @@ -1321,11 +1321,8 @@ static void ot_spi_host_realize(DeviceState *dev, Error **errp) OtSPIHostState *s = OT_SPI_HOST(dev); (void)errp; + g_assert(s->ot_id); g_assert(s->pclk); - if (!s->ot_id) { - s->ot_id = - g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); - } s->cs_lines = g_new0(qemu_irq, (size_t)s->num_cs); diff --git a/hw/opentitan/ot_unimp.c b/hw/opentitan/ot_unimp.c index 6e0777d66ed5a..0f060b7d27bc7 100644 --- a/hw/opentitan/ot_unimp.c +++ b/hw/opentitan/ot_unimp.c @@ -258,10 +258,7 @@ static void ot_unimp_realize(DeviceState *dev, Error **errp) { OtUnimpState *s = OT_UNIMP(dev); - if (!s->ot_id) { - error_setg(errp, "ot_id property required for " TYPE_OT_UNIMP); - return; - } + g_assert(s->ot_id); if (!s->size || (s->size & 3u)) { error_setg(errp, "%s: invalid size", s->ot_id); From 1e17ee01273c255ab75395ef45553ebfbfaf8dd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 18 Apr 2025 15:10:20 +0200 Subject: [PATCH 69/69] [ot] scripts/opentitan: pyot: add MMU trace option. Signed-off-by: Emmanuel Blot --- python/qemu/ot/pyot/executer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/qemu/ot/pyot/executer.py b/python/qemu/ot/pyot/executer.py index dec35553e62d3..f33f1114ad047 100644 --- a/python/qemu/ot/pyot/executer.py +++ b/python/qemu/ot/pyot/executer.py @@ -68,6 +68,7 @@ class QEMUExecuter: 'E': 'exec', 'G': 'guest_errors', 'I': 'int', + 'M': 'mmu', 'U': 'unimp', } """Shortcut names for QEMU log sources."""