From f91697aafb07630c372c66fde88ac138ff37cbc9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 14 May 2024 16:25:44 +0200 Subject: [PATCH 01/65] [ot] hw/opentitan: ot_dma: emit only one trace per error type. Also fix an invalid error report type. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 61b9a17da7bbe..56bc6077ee394 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -467,6 +467,14 @@ static bool ot_dma_is_configurable(const OtDMAState *s) return !ot_dma_is_busy(s); } +static bool ot_dma_is_on_error(const OtDMAState *s, unsigned err) +{ + g_assert(err < ERR_COUNT); + + return ((bool)(s->regs[R_STATUS] & R_STATUS_ERROR_MASK)) && + ((bool)(s->regs[R_ERROR_CODE] & DMA_ERROR(err))); +} + static void ot_dma_set_error(OtDMAState *s, unsigned err) { g_assert(err < ERR_COUNT); @@ -736,11 +744,13 @@ static bool ot_dma_go(OtDMAState *s) OtDMAOp *op = &s->op; - op->attrs.unspecified = false; + if (s->state != SM_ERROR) { + op->attrs.unspecified = false; #ifdef OT_DMA_HAS_ROLE - op->attrs.role = (unsigned)s->role; + op->attrs.role = (unsigned)s->role; #endif - op->size = s->regs[R_TOTAL_DATA_SIZE]; + op->size = s->regs[R_TOTAL_DATA_SIZE]; + } /* * The emulation ignores the transfer width as this is already managed @@ -758,7 +768,7 @@ static bool ot_dma_go(OtDMAState *s) unsigned twidth = s->regs[R_TRANSFER_WIDTH]; uint32_t tmask = (1u << twidth) - 1u; - if (soffset & tmask) { + if (!ot_dma_is_on_error(s, ERR_SRC_ADDR) && (soffset & tmask)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Src 0x%" HWADDR_PRIx " not aligned on TRANSFER_WIDTH\n", @@ -766,7 +776,7 @@ static bool ot_dma_go(OtDMAState *s) ot_dma_set_xerror(s, ERR_SRC_ADDR); } - if (doffset & tmask) { + if (!ot_dma_is_on_error(s, ERR_DEST_ADDR) && (doffset & tmask)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Dest 0x%" HWADDR_PRIx " not aligned on TRANSFER_WIDTH\n", @@ -774,7 +784,7 @@ static bool ot_dma_go(OtDMAState *s) ot_dma_set_xerror(s, ERR_DEST_ADDR); } - if (sasix != AS_SYS) { + if (!ot_dma_is_on_error(s, ERR_SRC_ADDR) && (sasix != AS_SYS)) { if (s->regs[R_SRC_ADDR_HI] != 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Src address is too large\n", __func__, s->ot_id); @@ -782,12 +792,12 @@ static bool ot_dma_go(OtDMAState *s) } } - if (dasix != AS_SYS) { + if (!ot_dma_is_on_error(s, ERR_DEST_ADDR) && (dasix != AS_SYS)) { if (s->regs[R_DEST_ADDR_HI] != 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Dest address is too large\n", __func__, s->ot_id); - ot_dma_set_xerror(s, ERR_SRC_ADDR); + ot_dma_set_xerror(s, ERR_DEST_ADDR); } } @@ -803,7 +813,7 @@ static bool ot_dma_go(OtDMAState *s) int ewidth = memory_access_size(smr, sizeof(uint32_t), soffset + op->size - sizeof(uint32_t)); int dwidth = MIN(swidth, ewidth); - if (dwidth < (int)twidth) { + if (!ot_dma_is_on_error(s, ERR_SRC_ADDR) && (dwidth < (int)twidth)) { qemu_log_mask(LOG_UNIMP, "%s: %s: Src device does not supported " "requested width: %u, max %d\n", @@ -817,7 +827,7 @@ static bool ot_dma_go(OtDMAState *s) int ewidth = memory_access_size(dmr, sizeof(uint32_t), doffset + op->size - sizeof(uint32_t)); int dwidth = MIN(swidth, ewidth); - if (dwidth < (int)twidth) { + if (!ot_dma_is_on_error(s, ERR_DEST_ADDR) && (dwidth < (int)twidth)) { qemu_log_mask(LOG_UNIMP, "%s: %s: Dest device does not supported " "requested width: %u, max %d\n", From 7672e76af65bed4db00f7d8d84cc1fc5b1ccea1a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 13 May 2024 17:19:51 +0200 Subject: [PATCH 02/65] [ot] scripts/opentitan: ot.util.log add an option to log function name Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index df41fe0b39316..7f459e0c16e1b 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -45,19 +45,23 @@ def __init__(self, *args, **kwargs): kwargs = dict(kwargs) name_width = kwargs.pop('name_width', 10) self._use_ansi = kwargs.pop('color', isatty(stderr.fileno())) + use_func = kwargs.pop('funcname', False) use_ms = kwargs.pop('ms', False) use_time = kwargs.pop('time', use_ms) use_lineno = kwargs.pop('lineno', False) super().__init__(*args, **kwargs) - format_trail = f' %(name)-{name_width}s %(message)s' if use_time: tfmt = '%(asctime)s ' if not use_ms else '%(asctime)s.%(msecs)03d ' else: tfmt = '' - lno = ' [%(lineno)d] ' if use_lineno else '' - self._plain_format = f'{tfmt}{self.FMT_LEVEL}{lno}{format_trail}' + sep = ' ' if not use_lineno else '' + fnc = f' %(funcName)s{sep}' if use_func else ' ' + sep = ' ' if not use_func else '' + lno = f'{sep}[%(lineno)d] ' if use_lineno else '' + fmt_trail = f' %(name)-{name_width}s{fnc}{lno}%(message)s' + self._plain_format = f'{tfmt}{self.FMT_LEVEL}{fmt_trail}' self._color_formats = { - lvl: f'{tfmt}{clr}{self.FMT_LEVEL}{self.RESET}{lno}{format_trail}' + lvl: f'{tfmt}{clr}{self.FMT_LEVEL}{self.RESET}{fmt_trail}' for lvl, clr in self.COLORS.items() } self._formatter_args = ['%H:%M:%S'] if use_time else [] From b7e6aa774e2a364793a3eff4819650795ac5cfa2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 15 May 2024 14:35:51 +0200 Subject: [PATCH 03/65] [ot] scripts/opentitan: ot.util.misc: add EasyDict helper Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/misc.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/opentitan/ot/util/misc.py b/scripts/opentitan/ot/util/misc.py index 3d2b20cc10d66..7fdb82bda484a 100644 --- a/scripts/opentitan/ot/util/misc.py +++ b/scripts/opentitan/ot/util/misc.py @@ -7,7 +7,7 @@ """ from sys import stdout -from typing import Any, Optional, TextIO +from typing import Any, Iterable, Optional, TextIO try: # only available from Python 3.12+ @@ -37,6 +37,31 @@ def parse(val: Optional[str]) -> Optional[int]: return int(val, val.startswith('0x') and 16 or 10) +class EasyDict(dict): + """Dictionary whose members can be accessed as instance members + """ + + def __init__(self, dictionary=None, **kwargs): + if dictionary is not None: + self.update(dictionary) + self.update(kwargs) + + def __getattr__(self, name): + try: + return self.__getitem__(name) + except KeyError as exc: + raise AttributeError(f"'{self.__class__.__name__}' object has no " + f"attribute '{name}'") from exc + + def __setattr__(self, name, value): + self.__setitem__(name, value) + + def __dir__(self) -> Iterable[Any]: + items = set(super().__dir__()) + items.update(set(self)) + yield from sorted(items) + + def dump_buffer(buffer: Buffer, addr: int = 0, file: Optional[TextIO] = None) \ -> None: """Dump a binary buffer, same format as hexdump -C.""" From 909eb59c6f8124fecd27d6fd38110e64639f513b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 13 May 2024 17:21:54 +0200 Subject: [PATCH 04/65] [ot] scripts/opentitan: ot.devproxy: small improvements - explicit timeout message - add close() to ProxyEngine - add quit() wrapper to DeviceProxy Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/devproxy.py | 43 ++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/scripts/opentitan/ot/devproxy.py b/scripts/opentitan/ot/devproxy.py index 97858f22629b3..5053ee11c2503 100644 --- a/scripts/opentitan/ot/devproxy.py +++ b/scripts/opentitan/ot/devproxy.py @@ -13,7 +13,7 @@ from struct import calcsize as scalc, pack as spack, unpack as sunpack from sys import modules from threading import Event, Thread, get_ident -from time import time as now +from time import sleep, time as now from typing import (Any, Callable, Dict, Iterator, List, NamedTuple, Optional, Tuple, Union) @@ -122,7 +122,7 @@ class DeviceProxy: NO_ROLE = 0xf """Disabled role.""" - def __init__(self, proxy: 'Proxy', name: str, devid: int, addr: int, + def __init__(self, proxy: 'ProxyEngine', name: str, devid: int, addr: int, count: int, offset: int): logname = self.__class__.__name__.lower().replace('proxy', '') self._log = getLogger(f'proxy.{logname}') @@ -138,6 +138,11 @@ def __init__(self, proxy: 'Proxy', name: str, devid: int, addr: int, self._interrupts: Optional[Dict[str, Tuple[int, int, int]]] = None self._new() + @property + def proxy(self) -> 'ProxyEngine': + """Return the proxy engine.""" + return self._proxy + @property def uid(self) -> int: """Provide the unique device identifier in this environment.""" @@ -1104,6 +1109,14 @@ class ProxyEngine: """Maximum time to wait on a blocking operation. """ + POLL_RECOVER_DELAY = 0.1 + """Time to wait on a recovery operation. + """ + + EXIT_DELAY = 0.1 + """Time to wait before resuming once the termination command is sent. + """ + BAUDRATE = 115200 """Default baudrate for serial line communication.""" @@ -1161,18 +1174,26 @@ def open(self, url: str) -> None: baudrate=self.BAUDRATE) self._kick_off() - def quit(self, code: int) -> None: - """Tell the remote target to exit the application. - """ - payload = spack(' None: + """Close the connection with the remote device.""" + self._resume = False + self._log.debug('Closing connection') if self._socket: self._socket.shutdown(SHUT_RDWR) + self._socket.close() self._socket = None if self._port: self._port.close() self._port = None + def quit(self, code: int) -> None: + """Tell the remote target to exit the application. + """ + payload = spack(' None: """Resume execution. """ @@ -1371,7 +1392,7 @@ def exchange(self, command: str, payload: Optional[bytes] = None) -> bytes: if now() > timeout: self._log.error('Timeout on command completion') self._resume = False - raise TimeoutError() + raise TimeoutError('No reply from peer') if self._resp_event.wait(self.POLL_TIMEOUT): break rcmd, payload = self._resp_q.popleft() @@ -1470,8 +1491,10 @@ def _receive(self): resp = False # pylint: disable=broad-except except Exception as exc: - self._resume = False - self._log.fatal('Exception: %s', exc) + # connection shutdown may have been requested + if self._resume: + self._log.fatal('Exception: %s', exc) + self._resume = False break def _notify(self) -> None: From ae099f1dd78b96f52f2591bf53a1d1b526bba044 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 15 May 2024 15:34:10 +0200 Subject: [PATCH 05/65] [ot] .gitlab-ci.d: rename pylint file Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/{pylint.yml => pylint-ot.yml} | 0 .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename .gitlab-ci.d/opentitan/{pylint.yml => pylint-ot.yml} (100%) diff --git a/.gitlab-ci.d/opentitan/pylint.yml b/.gitlab-ci.d/opentitan/pylint-ot.yml similarity index 100% rename from .gitlab-ci.d/opentitan/pylint.yml rename to .gitlab-ci.d/opentitan/pylint-ot.yml diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index d347f34b65d36..f439d8085f8da 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -4,6 +4,6 @@ variables: include: - local: '/.gitlab-ci.d/opentitan/build.yml' - - local: '/.gitlab-ci.d/opentitan/pylint.yml' + - local: '/.gitlab-ci.d/opentitan/pylint-ot.yml' - local: '/.gitlab-ci.d/opentitan/ot-smoke.yml' - local: '/.gitlab-ci.d/opentitan/ot-bmtests.yml' From a296dd91a90902fc15ad3ae6b54edf667e926016 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 14:30:51 +0200 Subject: [PATCH 06/65] [ot] scripts/opentitan: ot-{format.tidy}.sh: fix missing version Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot-format.sh | 4 ++++ scripts/opentitan/ot-tidy.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/scripts/opentitan/ot-format.sh b/scripts/opentitan/ot-format.sh index 39fac6d307b7f..0ea6d2daa7bae 100755 --- a/scripts/opentitan/ot-format.sh +++ b/scripts/opentitan/ot-format.sh @@ -21,6 +21,10 @@ fi version_full="$(${clangformat} --version | \ grep "clang-format version" | head -1 | \ sed -E 's/^.*clang-format version ([0-9]+\.[0-9]+\.[0-9]+).*$/\1/')" +if [ -z "${version_full}" ]; then + echo "Unable to retrieve LLVM version" >&2 + exit 1 +fi version_major="$(echo ${version_full} | cut -d. -f1)" if [ ${version_major} -lt ${EXPECTED_VERSION} ]; then echo "clang-format v${EXPECTED_VERSION} required, found ${version_full}" >&2 diff --git a/scripts/opentitan/ot-tidy.sh b/scripts/opentitan/ot-tidy.sh index 297bf7870e9fe..0196d85ba31c3 100755 --- a/scripts/opentitan/ot-tidy.sh +++ b/scripts/opentitan/ot-tidy.sh @@ -21,6 +21,10 @@ fi version_full="$(${clangtidy} --version | \ grep "LLVM version" | head -1 | \ sed -E 's/^.*LLVM version ([0-9]+\.[0-9]+\.[0-9]+).*$/\1/')" +if [ -z "${version_full}" ]; then + echo "Unable to retrieve LLVM version" >&2 + exit 1 +fi version_major="$(echo ${version_full} | cut -d. -f1)" if [ ${version_major} -lt ${EXPECTED_VERSION} ]; then echo "clang-tidy v${EXPECTED_VERSION} required, found ${version_full}" >&2 From d3af480fe7bcd4315c17cfa62b5fdf179a5f67f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 14:31:52 +0200 Subject: [PATCH 07/65] [ot] scripts/opentitan: ot-{format.tidy}.sh: update to LLVM v18 Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot-format.sh | 2 +- scripts/opentitan/ot-tidy.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/opentitan/ot-format.sh b/scripts/opentitan/ot-format.sh index 0ea6d2daa7bae..9ad4c5946b681 100755 --- a/scripts/opentitan/ot-format.sh +++ b/scripts/opentitan/ot-format.sh @@ -3,7 +3,7 @@ # Copyright (c) 2024 Rivos, Inc. # SPDX-License-Identifier: Apache2 -EXPECTED_VERSION="17" +EXPECTED_VERSION="18" # find clang-format executable: either 'clang-format-17' or 'clang-format' for ver_suffix in "-${EXPECTED_VERSION}" ""; do diff --git a/scripts/opentitan/ot-tidy.sh b/scripts/opentitan/ot-tidy.sh index 0196d85ba31c3..eb1b6a24d449b 100755 --- a/scripts/opentitan/ot-tidy.sh +++ b/scripts/opentitan/ot-tidy.sh @@ -3,7 +3,7 @@ # Copyright (c) 2024 Rivos, Inc. # SPDX-License-Identifier: Apache2 -EXPECTED_VERSION="17" +EXPECTED_VERSION="18" # find clang-tidy executable: either 'clang-tidy-17' or 'clang-tidy' for ver_suffix in "-${EXPECTED_VERSION}" ""; do From 12a50428f7aeeb221205521ca9bd914bfeb0610f Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 14:33:36 +0200 Subject: [PATCH 08/65] [ot] hw/opentitan, hw/riscv: update syntax with clang-format v18 Signed-off-by: Emmanuel Blot --- hw/misc/pulp_rv_dm.c | 2 +- hw/opentitan/ot_lc_ctrl.c | 2 +- hw/riscv/dm.c | 2 +- include/exec/jtagstub.h | 2 +- include/hw/riscv/ibex_common.h | 30 ++++++++++++++++++------------ 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/hw/misc/pulp_rv_dm.c b/hw/misc/pulp_rv_dm.c index e129ac43da94f..7c7d17c720382 100644 --- a/hw/misc/pulp_rv_dm.c +++ b/hw/misc/pulp_rv_dm.c @@ -109,7 +109,7 @@ REG32(FLAGS, RISCV_DM_FLAGS_OFFSET) */ #define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) -#define MEM_OFF(_r_) ((_r_)-R_WHERETO) +#define MEM_OFF(_r_) ((_r_) - R_WHERETO) #define PULP_RV_DM_DMACT_BASE (A_HALTED) #define PULP_RV_DM_DMACT_SIZE (A_EXCEPTION - A_HALTED + sizeof(uint32_t)) diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index b43c3d7883431..e1c374e19adba 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -133,7 +133,7 @@ REG32(MANUF_STATE_7, 0x88u) #define R_FIRST_EXCLUSIVE_REG (R_TRANSITION_TOKEN_0) #define R_LAST_EXCLUSIVE_REG (R_TRANSITION_TARGET) #define EXCLUSIVE_REGS_COUNT (R_LAST_EXCLUSIVE_REG - R_FIRST_EXCLUSIVE_REG + 1u) -#define XREGS_OFFSET(_r_) ((_r_)-R_FIRST_EXCLUSIVE_REG) +#define XREGS_OFFSET(_r_) ((_r_) - R_FIRST_EXCLUSIVE_REG) #define ALERT_TEST_MASK \ (ALERT_FATAL_PROG_ERROR_MASK | ALERT_FATAL_STATE_ERROR_MASK | \ diff --git a/hw/riscv/dm.c b/hw/riscv/dm.c index 0a7e724a47e2b..184c43b4d6bbe 100644 --- a/hw/riscv/dm.c +++ b/hw/riscv/dm.c @@ -255,7 +255,7 @@ REG32(FLAGS, RISCV_DM_FLAGS_OFFSET) static_assert((A_LAST - A_FIRST) < 64u, "too many registers"); -#define REG_BIT(_addr_) (1ull << ((_addr_)-A_FIRST)) +#define REG_BIT(_addr_) (1ull << ((_addr_) - A_FIRST)) #define REG_BIT_DEF(_reg_) REG_BIT(A_##_reg_) #define DM_REG_COUNT (1u << (ADDRESS_BITS)) diff --git a/include/exec/jtagstub.h b/include/exec/jtagstub.h index 45bcf3d0aff63..e4175a95a2694 100644 --- a/include/exec/jtagstub.h +++ b/include/exec/jtagstub.h @@ -61,7 +61,7 @@ struct _TAPDataHandler { * @_id_ Entry in JEDEC table */ #define JEDEC_MANUFACTURER_ID(_tbl_, _id_) \ - (((((_tbl_)-1u) & 0xfu) << 7u) | ((_id_) & 0x7fu)) + (((((_tbl_) - 1u) & 0xfu) << 7u) | ((_id_) & 0x7fu)) /* * Start the JTAG server diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index c9b0fbc590dfe..c414e565161e8 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -194,7 +194,7 @@ typedef struct { */ #define IBEX_MEMMAP_REGIDX_COUNT 4u #define IBEX_MEMMAP_REGIDX_MASK \ - ((IBEX_MEMMAP_REGIDX_COUNT)-1u) /* address are always word-aligned */ + ((IBEX_MEMMAP_REGIDX_COUNT) - 1u) /* address are always word-aligned */ #define IBEX_MEMMAP_MAKE_REG(_addr_, _flag_) \ ((_addr_) | (((uint32_t)_flag_) & IBEX_MEMMAP_REGIDX_MASK)) #define IBEX_MEMMAP_MAKE_REG_MASK(_flag_) (1u << (_flag_)) @@ -205,7 +205,7 @@ typedef struct { #define IBEX_GPIO_GRP_BITS 5u #define IBEX_GPIO_GRP_COUNT (1u << (IBEX_GPIO_GRP_BITS)) #define IBEX_GPIO_GRP_SHIFT (32u - IBEX_GPIO_GRP_BITS) -#define IBEX_GPIO_GRP_MASK ((IBEX_GPIO_GRP_COUNT)-1u) << (IBEX_GPIO_GRP_SHIFT) +#define IBEX_GPIO_GRP_MASK ((IBEX_GPIO_GRP_COUNT) - 1u) << (IBEX_GPIO_GRP_SHIFT) #define IBEX_GPIO_IDX_MASK (~(IBEX_GPIO_GRP_MASK)) #define IBEX_GPIO_GET_IDX(_idx_) ((_idx_) & IBEX_GPIO_IDX_MASK) #define IBEX_GPIO_GET_GRP(_idx_) \ @@ -217,7 +217,7 @@ typedef struct { #define IBEX_DEVLINK_RMT_COUNT (1u << (IBEX_DEVLINK_RMT_BITS)) #define IBEX_DEVLINK_RMT_SHIFT (32u - IBEX_DEVLINK_RMT_BITS) #define IBEX_DEVLINK_RMT_MASK \ - ((IBEX_DEVLINK_RMT_COUNT)-1u) << (IBEX_DEVLINK_RMT_SHIFT) + ((IBEX_DEVLINK_RMT_COUNT) - 1u) << (IBEX_DEVLINK_RMT_SHIFT) #define IBEX_DEVLINK_IDX_MASK (~(IBEX_DEVLINK_RMT_MASK)) #define IBEX_DEVLINK_DEVICE(_idx_) ((_idx_) & IBEX_DEVLINK_IDX_MASK) #define IBEX_DEVLINK_REMOTE(_idx_) \ @@ -226,10 +226,7 @@ typedef struct { (((_par_) << IBEX_DEVLINK_RMT_SHIFT) | ((_ix_) & IBEX_DEVLINK_IDX_MASK)) /* MemMapEntry that should be ignored (i.e. skipped, not mapped) */ -#define MEMMAPSKIP \ - { \ - .base = HWADDR_MAX, .size = HWADDR_MAX \ - } +#define MEMMAPSKIP { .base = HWADDR_MAX, .size = HWADDR_MAX } #define SKIP_MEMMAP(_mmap_) ((_mmap_)->size == HWADDR_MAX) /** @@ -339,7 +336,8 @@ typedef struct { */ #define IBEX_DEVLINK(_pname_, _idx_) \ { \ - .propname = (_pname_), .index = (_idx_), \ + .propname = (_pname_), \ + .index = (_idx_), \ } /** @@ -368,7 +366,9 @@ typedef struct { */ #define IBEX_DEV_BOOL_PROP(_pname_, _b_) \ { \ - .propname = (_pname_), .type = IBEX_PROP_TYPE_BOOL, .b = (_b_), \ + .propname = (_pname_), \ + .type = IBEX_PROP_TYPE_BOOL, \ + .b = (_b_), \ } /** @@ -376,7 +376,9 @@ typedef struct { */ #define IBEX_DEV_INT_PROP(_pname_, _i_) \ { \ - .propname = (_pname_), .type = IBEX_PROP_TYPE_INT, .i = (_i_), \ + .propname = (_pname_), \ + .type = IBEX_PROP_TYPE_INT, \ + .i = (_i_), \ } /** @@ -384,7 +386,9 @@ typedef struct { */ #define IBEX_DEV_UINT_PROP(_pname_, _u_) \ { \ - .propname = (_pname_), .type = IBEX_PROP_TYPE_UINT, .u = (_u_), \ + .propname = (_pname_), \ + .type = IBEX_PROP_TYPE_UINT, \ + .u = (_u_), \ } /** @@ -392,7 +396,9 @@ typedef struct { */ #define IBEX_DEV_STRING_PROP(_pname_, _s_) \ { \ - .propname = (_pname_), .type = IBEX_PROP_TYPE_STR, .s = (_s_), \ + .propname = (_pname_), \ + .type = IBEX_PROP_TYPE_STR, \ + .s = (_s_), \ } DeviceState **ibex_create_devices(const IbexDeviceDef *defs, unsigned count, From a2eb7deb151be4c09aef3d94d68f5733cd3b7c26 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 14:45:28 +0200 Subject: [PATCH 09/65] [ot] scripts/opentitan: clang-tidy.yml: update definitions for v18 - discard casting-through-void, as QEMU heavily relies on ptr casting - discard multi-level-implicit-pointer-conversion, as Glib likes casts - discard macro-to-enum which is not fitted for C language - discard redundant-casting which consider bool and int as strictly eq. - discard avoid-nested-conditional-operator, useful for ?: operator --- scripts/opentitan/clang-tidy.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/opentitan/clang-tidy.yml b/scripts/opentitan/clang-tidy.yml index 204f0e2636b4a..8041283eafc3c 100644 --- a/scripts/opentitan/clang-tidy.yml +++ b/scripts/opentitan/clang-tidy.yml @@ -6,7 +6,9 @@ Checks: '*, -altera-*, -android-cloexec-fopen, -bugprone-assignment-in-if-condition, + -bugprone-casting-through-void, -bugprone-easily-swappable-parameters, + -bugprone-multi-level-implicit-pointer-conversion, -bugprone-reserved-identifier, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -clang-diagnostic-error, @@ -15,6 +17,7 @@ Checks: '*, -concurrency-mt-unsafe, -cppcoreguidelines-init-variables, -cppcoreguidelines-interfaces-global-init, + -cppcoreguidelines-macro-to-enum, -google-readability-*, -hicpp-no-assembler, -hicpp-signed-bitwise, @@ -23,9 +26,11 @@ Checks: '*, -misc-include-cleaner, -modernize-macro-to-enum, -performance-no-int-to-ptr, + -readability-avoid-nested-conditional-operator, -readability-function-cognitive-complexity, -readability-identifier-length, - -readability-isolate-declaration' + -readability-isolate-declaration, + -readability-redundant-casting' UseColor: true WarningsAsErrors: '*' ... From 5c59f17a95e76ab016b4a79b3f3f5f02a2288e7e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 15:13:47 +0200 Subject: [PATCH 10/65] [ot] hw/opentitan: disable clang-tidy warnings on broken QEMU APIs Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 1 + hw/opentitan/ot_flash.c | 4 ++++ hw/opentitan/ot_gpio_dj.c | 1 + hw/opentitan/ot_gpio_eg.c | 1 + hw/opentitan/ot_otp_dj.c | 2 ++ hw/opentitan/ot_spi_device.c | 1 + hw/opentitan/ot_uart.c | 1 + 7 files changed, 11 insertions(+) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index 40ffa0c39c155..6c3a7e8fcc05e 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -1411,6 +1411,7 @@ static int ot_dev_proxy_be_change(void *opaque) if (s->watch_tag > 0) { g_source_remove(s->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, &ot_dev_proxy_watch_cb, s); } diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index a4ef20cf44920..f17927c5d2933 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -1596,6 +1596,7 @@ static void ot_flash_load(OtFlashState *s, Error **errp) blk_blockalign(s->blk, sizeof(OtFlashBackendHeader)); int rc; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) rc = blk_pread(s->blk, 0, sizeof(*header), header, 0); if (rc < 0) { error_setg(errp, "failed to read the flash header content: %d", rc); @@ -1649,6 +1650,7 @@ static void ot_flash_load(OtFlashState *s, Error **errp) unsigned offset = offsetof(OtFlashBackendHeader, hlength) + sizeof(header->hlength) + header->hlength; + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) rc = blk_pread(s->blk, (int64_t)offset, flash_size, flash->storage, 0); if (rc < 0) { error_setg(errp, "failed to read the initial flash content: %d", @@ -1663,8 +1665,10 @@ static void ot_flash_load(OtFlashState *s, Error **errp) size_t debug_trailer_size = (size_t)(flash->bank_count) * ELFNAME_SIZE * BIN_APP_COUNT; uint8_t *elfnames = blk_blockalign(s->blk, debug_trailer_size); + // NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) rc = blk_pread(s->blk, (int64_t)offset + flash_size, (int64_t)debug_trailer_size, elfnames, 0); + // NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) if (!rc) { const char *elfname = (const char *)elfnames; for (unsigned ix = 0; ix < BIN_APP_COUNT; ix++) { diff --git a/hw/opentitan/ot_gpio_dj.c b/hw/opentitan/ot_gpio_dj.c index 0959bff97d680..972e68c90f68d 100644 --- a/hw/opentitan/ot_gpio_dj.c +++ b/hw/opentitan/ot_gpio_dj.c @@ -669,6 +669,7 @@ static int ot_gpio_dj_chr_be_change(void *opaque) if (s->watch_tag > 0) { g_source_remove(s->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, &ot_gpio_dj_chr_watch_cb, s); } diff --git a/hw/opentitan/ot_gpio_eg.c b/hw/opentitan/ot_gpio_eg.c index d8e3cf99cd94c..13a12e828fa33 100644 --- a/hw/opentitan/ot_gpio_eg.c +++ b/hw/opentitan/ot_gpio_eg.c @@ -646,6 +646,7 @@ static int ot_gpio_eg_chr_be_change(void *opaque) if (s->watch_tag > 0) { g_source_remove(s->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, &ot_gpio_eg_chr_watch_cb, s); } diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 09428d4239ffa..a03f4b7360a0f 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -3086,9 +3086,11 @@ static void ot_otp_dj_lci_write_complete(OtOTPDjState *s, bool success) const OtOTPPartDesc *lcdesc = &OtOTPPartDescs[OTP_PART_LIFE_CYCLE]; unsigned lc_data_off = lcdesc->offset / sizeof(uint32_t); uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + // NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) if (blk_pwrite(s->blk, (int64_t)(intptr_t)(offset + lcdesc->offset), lcdesc->size, &s->otp->data[lc_data_off], (BdrvRequestFlags)0)) { + // NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) error_report("%s: cannot update OTP backend", __func__); if (lci->error == OTP_NO_ERROR) { lci->error = OTP_MACRO_ERROR; diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 0c51ef63d4787..d1cc5efb25bf0 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -2531,6 +2531,7 @@ static int ot_spi_device_chr_be_change(void *opaque) if (s->watch_tag > 0) { g_source_remove(s->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, &ot_spi_device_chr_watch_cb, s); } diff --git a/hw/opentitan/ot_uart.c b/hw/opentitan/ot_uart.c index 1c83007b0a880..f73ad34eadb03 100644 --- a/hw/opentitan/ot_uart.c +++ b/hw/opentitan/ot_uart.c @@ -562,6 +562,7 @@ static int ot_uart_be_change(void *opaque) if (s->watch_tag > 0) { g_source_remove(s->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, ot_uart_watch_cb, s); } From 86c74a3004876bfe8684925655aa46ac0cc6d385 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Sat, 18 May 2024 15:51:20 +0200 Subject: [PATCH 11/65] [ot] hw/riscv: dm: address clang-tidy warning Signed-off-by: Emmanuel Blot --- hw/riscv/dm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/dm.c b/hw/riscv/dm.c index 184c43b4d6bbe..f8072989712b2 100644 --- a/hw/riscv/dm.c +++ b/hw/riscv/dm.c @@ -605,7 +605,7 @@ riscv_dm_write_rq(RISCVDebugDeviceState *dev, uint32_t addr, uint32_t value) dm->cmd_err = ret; } - if (!(RISCVDM_REG_IGNORE_TRACES & REG_BIT(addr))) { + if ((addr >= A_FIRST) && !(RISCVDM_REG_IGNORE_TRACES & REG_BIT(addr))) { trace_riscv_dm_reg_update(dm->soc, riscv_dm_get_reg_name(addr), addr, value, "write", ret); } @@ -655,7 +655,7 @@ riscv_dm_read_rq(RISCVDebugDeviceState *dev, uint32_t addr) ret = riscv_dm_exec_command(dm, dm->regs[A_COMMAND]); } - if (!(RISCVDM_REG_IGNORE_TRACES & REG_BIT(addr))) { + if ((addr >= A_FIRST) && !(RISCVDM_REG_IGNORE_TRACES & REG_BIT(addr))) { trace_riscv_dm_reg_update(dm->soc, riscv_dm_get_reg_name(addr), addr, value, "read", ret); } From 5bb0434bd001845d89c47828990bf478b26e2337 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 21 May 2024 10:45:35 +0200 Subject: [PATCH 12/65] [ot] .gitlab-ci.d: build with clang v18 Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/build.yml b/.gitlab-ci.d/opentitan/build.yml index fdab2b1104157..b5ebeaf4ce8a3 100644 --- a/.gitlab-ci.d/opentitan/build.yml +++ b/.gitlab-ci.d/opentitan/build.yml @@ -19,7 +19,7 @@ build-clang: - git clean -dffx subprojects - mkdir build - cd build - - ../configure --cc=clang-17 --disable-werror $QEMU_BUILD_OPTS + - ../configure --cc=clang-18 --disable-werror $QEMU_BUILD_OPTS --target-list=riscv32-softmmu,riscv64-softmmu,x86_64-linux-user - ninja - ninja qemu-img From 63742775753f1f6158ce6e23a8edf155c1590f78 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 26 Apr 2024 18:06:03 +0200 Subject: [PATCH 13/65] [ot] hw/opentitan: alerts: update alert test management in OT devices Signed-off-by: Emmanuel Blot --- hw/misc/pulp_rv_dm.c | 11 +++-- hw/opentitan/ot_clkmgr.c | 6 +-- hw/opentitan/ot_csrng.c | 34 +++++++++------- hw/opentitan/ot_dma.c | 6 +-- hw/opentitan/ot_edn.c | 66 ++++++++++++++++-------------- hw/opentitan/ot_entropy_src.c | 19 +++++---- hw/opentitan/ot_flash.c | 20 +++++---- hw/opentitan/ot_i2c_dj.c | 7 ++-- hw/opentitan/ot_ibex_wrapper_dj.c | 24 +++++++---- hw/opentitan/ot_ibex_wrapper_eg.c | 24 +++++++---- hw/opentitan/ot_lc_ctrl.c | 20 +++++---- hw/opentitan/ot_mbx.c | 6 +-- hw/opentitan/ot_otbn.c | 23 ++++++++--- hw/opentitan/ot_otp_dj.c | 31 +++++++++----- hw/opentitan/ot_otp_eg.c | 68 +++++++++++++++++++------------ hw/opentitan/ot_pinmux_dj.c | 5 +-- hw/opentitan/ot_pwrmgr.c | 5 +-- hw/opentitan/ot_rom_ctrl.c | 5 +-- hw/opentitan/ot_rstmgr.c | 20 ++++++--- hw/opentitan/ot_sensor.c | 24 ++++++++--- hw/opentitan/ot_soc_proxy.c | 20 +++++---- hw/opentitan/ot_spi_device.c | 20 +++++---- hw/opentitan/ot_spi_host.c | 1 - hw/opentitan/ot_sram_ctrl.c | 6 +-- hw/opentitan/ot_timer.c | 8 ++-- 25 files changed, 288 insertions(+), 191 deletions(-) diff --git a/hw/misc/pulp_rv_dm.c b/hw/misc/pulp_rv_dm.c index 7c7d17c720382..7445cda044608 100644 --- a/hw/misc/pulp_rv_dm.c +++ b/hw/misc/pulp_rv_dm.c @@ -132,13 +132,13 @@ struct PulpRVDMState { MemoryRegion dmflag; /* MMIO */ MemoryRegion rom; /* ROM */ + qemu_irq *ack_out; + IbexIRQ alert; + uint32_t dmflag_regs[PULP_RV_DM_DMFLAG_SIZE / sizeof(uint32_t)]; unsigned hart_count; uint64_t idle_bm; - - qemu_irq *ack_out; - IbexIRQ alert; }; #ifdef DISCARD_REPEATED_IO_TRACES @@ -258,9 +258,8 @@ static void pulp_rv_dm_regs_write(void *opaque, hwaddr addr, uint64_t val64, switch (R32_OFF(addr)) { case R_ALERT_TEST: - if (val32 & R_ALERT_TEST) { - ibex_irq_set(&s->alert, true); - } + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + ibex_irq_set(&s->alert, (int)(bool)val32); break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", diff --git a/hw/opentitan/ot_clkmgr.c b/hw/opentitan/ot_clkmgr.c index c3386b5954687..067123d5e242f 100644 --- a/hw/opentitan/ot_clkmgr.c +++ b/hw/opentitan/ot_clkmgr.c @@ -319,10 +319,8 @@ static void ot_clkmgr_write(void *opaque, hwaddr addr, uint64_t val64, switch (reg) { case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); } break; case R_EXTCLK_CTRL_REGWEN: diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index f25c9b748d1df..bbf417de3d2ee 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -212,8 +212,11 @@ static_assert(OT_CSRNG_AES_BLOCK_SIZE + OT_CSRNG_AES_KEY_SIZE == enum { ALERT_RECOVERABLE, ALERT_FATAL, + ALERT_COUNT, }; +static_assert(ALERT_COUNT == PARAM_NUM_ALERTS, "Invalid alert count"); + typedef enum { CSRNG_CMD_STALLED = -2, /* entropy stack is stalled */ CSRNG_CMD_ERROR = -1, /* command error, not recoverable */ @@ -739,14 +742,20 @@ static void ot_csrng_update_irqs(OtCSRNGState *s) } } -static void ot_csrng_report_alert(OtCSRNGState *s) +static void ot_csrng_update_alerts(OtCSRNGState *s) { + uint32_t level = s->regs[R_ALERT_TEST]; + if (__builtin_popcount(s->regs[R_RECOV_ALERT_STS])) { - ibex_irq_set(&s->alerts[ALERT_RECOVERABLE], 1); + level |= 1u << ALERT_RECOVERABLE; } if (s->state == CSRNG_ERROR) { - ibex_irq_set(&s->alerts[ALERT_FATAL], 1); + level |= 1u << ALERT_FATAL; + } + + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); } } @@ -759,7 +768,7 @@ static void ot_csrng_change_state_line(OtCSRNGState *s, OtCSRNGFsmState state, s->state = state; if (s->state == CSRNG_ERROR) { - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); } } @@ -779,7 +788,7 @@ ot_csrng_check_multibitboot(OtCSRNGState *s, uint8_t mbbool, uint32_t alert_bit) (unsigned)mbbool); s->regs[R_RECOV_ALERT_STS] |= 1u << alert_bit; - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); /* for CSRNG, default to false for invalid multibit boolean */ return false; @@ -1006,7 +1015,7 @@ static void ot_csrng_complete_command(OtCSRNGInstance *inst, int res) CHANGE_STATE(s, CSRNG_IDLE); } else { CHANGE_STATE(s, CSRNG_ERROR); - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); } } @@ -1071,7 +1080,7 @@ static int ot_csrng_handle_generate(OtCSRNGState *s, unsigned slot) if (!packet_count) { xtrace_ot_csrng_error("generation for no packet"); CHANGE_STATE(s, CSRNG_ERROR); - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); return -1; } @@ -1307,7 +1316,7 @@ static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) qemu_log_mask(LOG_GUEST_ERROR, "Unknown command: %u\n", acmd); s->regs[R_RECOV_ALERT_STS] |= R_RECOV_ALERT_STS_CS_MAIN_SM_ALERT_MASK; CHANGE_STATE(s, CSRNG_ERROR); - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); return -1; } @@ -1331,7 +1340,7 @@ static int ot_csrng_handle_command(OtCSRNGState *s, unsigned slot) qemu_log_mask(LOG_GUEST_ERROR, "%s: instance %u not instantiated\n", __func__, slot); CHANGE_STATE(s, CSRNG_ERROR); - ot_csrng_report_alert(s); + ot_csrng_update_alerts(s); return -1; } } @@ -1656,11 +1665,8 @@ static void ot_csrng_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_csrng_update_alerts(s); break; case R_REGWEN: val32 &= R_REGWEN_EN_MASK; diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 56bc6077ee394..ef8af951f08dc 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -1151,10 +1151,8 @@ static void ot_dma_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); } break; case R_SRC_ADDR_LO ... R_SRC_ADDR_HI: diff --git a/hw/opentitan/ot_edn.c b/hw/opentitan/ot_edn.c index c15f2ef843bed..d76d75c77b38a 100644 --- a/hw/opentitan/ot_edn.c +++ b/hw/opentitan/ot_edn.c @@ -165,8 +165,12 @@ static const char *REG_NAMES[REGS_COUNT] = { enum { ALERT_RECOVERABLE, ALERT_FATAL, + ALERT_COUNT, + ALERT_FATAL_STICKY = ALERT_COUNT, }; +static_assert(ALERT_COUNT == PARAM_NUM_ALERTS, "Invalid alert count"); + typedef enum { EDN_IDLE, /* idle */ EDN_BOOT_LOAD_INS, /* boot: load the instantiate command */ @@ -347,19 +351,21 @@ static void ot_edn_update_irqs(OtEDNState *s) } } -static void ot_edn_report_alert(OtEDNState *s) +static void ot_edn_update_alerts(OtEDNState *s) { - if (s->regs[R_RECOV_ALERT_STS]) { - ibex_irq_set(&s->alerts[ALERT_RECOVERABLE], 1); + uint32_t level = s->regs[R_ALERT_TEST]; + if (s->regs[R_ERR_CODE] & ERR_CODE_MASK) { + /* ERR_CODE is sticky */ + level |= 1u << ALERT_FATAL; } -} - -static void ot_edn_signal_error(OtEDNState *s, unsigned error) -{ - uint32_t bit = (1u << error) & ERR_CODE_MASK; - if (bit) { - s->regs[R_ERR_CODE] |= bit; - ibex_irq_set(&s->alerts[ALERT_FATAL], 1); + if (s->regs[R_ERR_CODE_TEST] & ERR_CODE_MASK) { + level |= 1u << ALERT_FATAL; + } + if (s->regs[R_RECOV_ALERT_STS] & RECOV_ALERT_STS_MASK) { + level |= 1u << ALERT_RECOVERABLE; + } + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); } } @@ -375,7 +381,7 @@ static void ot_edn_check_multibitboot(OtEDNState *s, uint8_t mbbool, } s->regs[R_RECOV_ALERT_STS] |= 1u << alert_bit; - ot_edn_report_alert(s); + ot_edn_update_alerts(s); } static bool ot_edn_is_enabled(OtEDNState *s) @@ -462,7 +468,7 @@ static void ot_edn_change_state_line(OtEDNState *s, OtEDNFsmState state, if (s->state == EDN_ERROR) { s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_MAIN_SM_ERR_MASK; - ot_edn_report_alert(s); + ot_edn_update_alerts(s); } } @@ -595,7 +601,7 @@ static void ot_edn_send_reseed_cmd(OtEDNState *s) R_ERR_CODE_SFIFO_RESCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; ot_edn_update_irqs(s); - ot_edn_report_alert(s); + ot_edn_update_alerts(s); return; } @@ -607,7 +613,7 @@ static void ot_edn_send_reseed_cmd(OtEDNState *s) s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; ot_edn_update_irqs(s); - ot_edn_report_alert(s); + ot_edn_update_alerts(s); return; } @@ -636,7 +642,7 @@ static void ot_edn_send_generate_cmd(OtEDNState *s) R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; ot_edn_update_irqs(s); - ot_edn_report_alert(s); + ot_edn_update_alerts(s); return; } @@ -648,7 +654,7 @@ static void ot_edn_send_generate_cmd(OtEDNState *s) R_ERR_CODE_SFIFO_GENCMD_ERR_MASK | R_ERR_CODE_FIFO_READ_ERR_MASK; s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; ot_edn_update_irqs(s); - ot_edn_report_alert(s); + ot_edn_update_alerts(s); return; } @@ -830,9 +836,7 @@ static void ot_edn_clean_up(OtEDNState *s, bool discard_requests) ot_fifo32_reset(&c->bits_fifo); ot_fifo32_reset(&c->sw_cmd_fifo); ot_edn_update_irqs(s); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_edn_update_alerts(s); if (discard_requests) { /* clear all pending end point requests */ @@ -881,7 +885,7 @@ static void ot_edn_fill_bits(void *opaque, const uint32_t *bits, bool fips) R_ERR_CODE_FIFO_WRITE_ERR_MASK; s->regs[R_INTR_STATE] |= INTR_EDN_FATAL_ERR_BIT_MASK; ot_edn_update_irqs(s); - ot_edn_report_alert(s); + ot_edn_update_alerts(s); return; } ot_fifo32_push(&c->bits_fifo, bits[ix]); @@ -1034,7 +1038,7 @@ static void ot_edn_csrng_ack_irq(void *opaque, int n, int level) trace_ot_edn_invalid_state(c->appid, __func__, STATE_NAME(s->state), s->state); s->regs[R_ERR_CODE] |= R_ERR_CODE_EDN_ACK_SM_ERR_MASK; - ot_edn_report_alert(s); + ot_edn_update_alerts(s); g_assert_not_reached(); } } @@ -1135,11 +1139,7 @@ static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[R_ALERT_TEST] = val32; break; case R_REGWEN: val32 &= R_REGWEN_EN_MASK; @@ -1211,20 +1211,23 @@ static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; } } else { - ot_edn_signal_error(s, R_ERR_CODE_FIFO_WRITE_ERR_SHIFT); + s->regs[R_ERR_CODE] |= R_ERR_CODE_FIFO_WRITE_ERR_MASK; + ot_edn_update_alerts(s); xtrace_ot_edn_error(c->appid, "sw fifo full"); } break; case R_RESEED_CMD: if (ot_fifo32_is_full(&c->cmd_reseed_fifo)) { - ot_edn_signal_error(s, R_ERR_CODE_SFIFO_RESCMD_ERR_SHIFT); + s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_RESCMD_ERR_MASK; + ot_edn_update_alerts(s); } else { ot_fifo32_push(&c->cmd_reseed_fifo, val32); } break; case R_GENERATE_CMD: if (ot_fifo32_is_full(&c->cmd_gen_fifo)) { - ot_edn_signal_error(s, R_ERR_CODE_SFIFO_GENCMD_ERR_SHIFT); + s->regs[R_ERR_CODE] |= R_ERR_CODE_SFIFO_GENCMD_ERR_MASK; + ot_edn_update_alerts(s); } else { xtrace_ot_edn_xinfo(c->appid, "PUSH GEN CMD", val32); ot_fifo32_push(&c->cmd_gen_fifo, val32); @@ -1240,7 +1243,8 @@ static void ot_edn_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ERR_CODE_TEST: val32 &= R_ERR_CODE_TEST_VAL_MASK; - ot_edn_signal_error(s, (unsigned)val32); + s->regs[reg] = val32; + ot_edn_update_alerts(s); break; case R_SW_CMD_STS: case R_ERR_CODE: diff --git a/hw/opentitan/ot_entropy_src.c b/hw/opentitan/ot_entropy_src.c index ad7384790fe69..b93de78fb31bb 100644 --- a/hw/opentitan/ot_entropy_src.c +++ b/hw/opentitan/ot_entropy_src.c @@ -331,8 +331,11 @@ static const char *REG_NAMES[REGS_COUNT] = { enum { ALERT_RECOVERABLE, ALERT_FATAL, + ALERT_COUNT, }; +static_assert(ALERT_COUNT == PARAM_NUM_ALERTS, "Invalid alert count"); + typedef enum { ENTROPY_SRC_IDLE, ENTROPY_SRC_BOOT_HT_RUNNING, @@ -716,13 +719,18 @@ static void ot_entropy_src_update_alerts(OtEntropySrcState *s) ALERT_THRESHOLD, ALERT_THRESHOLD); unsigned alert_count = ot_alert_get_alert_fail_count(s); bool recoverable = (bool)s->regs[R_RECOV_ALERT_STS]; + uint32_t level = s->regs[R_ALERT_TEST]; if (alert_count >= alert_threshold || recoverable) { - ibex_irq_set(&s->alerts[ALERT_RECOVERABLE], 1); + level |= 1u << ALERT_RECOVERABLE; } uint32_t fatal_alert = s->regs[R_ERR_CODE] & ERR_CODE_FATAL_ERROR_MASK; fatal_alert |= (1u << s->regs[R_ERR_CODE_TEST]) & ERR_CODE_FATAL_ERROR_MASK; if (fatal_alert) { - ibex_irq_set(&s->alerts[ALERT_FATAL], 1); + level |= 1u << ALERT_FATAL; + } + + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); } } @@ -1289,11 +1297,8 @@ static void ot_entropy_src_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_entropy_src_update_alerts(s); break; case R_ME_REGWEN: val32 &= R_ME_REGWEN_EN_MASK; diff --git a/hw/opentitan/ot_flash.c b/hw/opentitan/ot_flash.c index f17927c5d2933..7d1c084a9847a 100644 --- a/hw/opentitan/ot_flash.c +++ b/hw/opentitan/ot_flash.c @@ -672,6 +672,15 @@ static void ot_flash_update_irqs(OtFlashState *s) } } +static void ot_flash_update_alerts(OtFlashState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static bool ot_flash_is_disabled(OtFlashState *s) { return s->regs[R_DIS] != OT_MULTIBITBOOL4_FALSE; @@ -1043,11 +1052,8 @@ static void ot_flash_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_flash_update_alerts(s); break; case R_DIS: val32 &= R_DIS_VAL_MASK; @@ -1539,9 +1545,7 @@ static void ot_flash_reset(DeviceState *dev) s->csrs[R_CSR0_REGWEN] = 0x1u; ot_flash_update_irqs(s); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_flash_update_alerts(s); ot_fifo32_reset(&s->rd_fifo); } diff --git a/hw/opentitan/ot_i2c_dj.c b/hw/opentitan/ot_i2c_dj.c index 0d3a4424b7d23..d18b05cb27450 100644 --- a/hw/opentitan/ot_i2c_dj.c +++ b/hw/opentitan/ot_i2c_dj.c @@ -706,10 +706,9 @@ static void ot_i2c_dj_write(void *opaque, hwaddr addr, uint64_t val64, ot_i2c_dj_update_irqs(s); break; case R_ALERT_TEST: - if (FIELD_EX32(val32, ALERT_TEST, FATAL_FAULT)) { - ARRAY_FIELD_DP32(s->regs, ALERT_TEST, FATAL_FAULT, 1u); - ibex_irq_set(&s->alert, 1u); - } + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + s->regs[reg] = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); break; case R_TIMEOUT_CTRL: case R_HOST_TIMEOUT_CTRL: diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c index bde26ce570ab3..6b821881da3d8 100644 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ b/hw/opentitan/ot_ibex_wrapper_dj.c @@ -726,6 +726,19 @@ struct OtIbexWrapperDjState { 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) { @@ -1350,11 +1363,8 @@ static void ot_ibex_wrapper_dj_regs_write(void *opaque, hwaddr addr, switch (reg) { case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_ibex_wrapper_dj_update_alerts(s); break; case R_SW_FATAL_ERR: if ((val32 >> 16u) == 0xC0DEu) { @@ -1375,9 +1385,7 @@ static void ot_ibex_wrapper_dj_regs_write(void *opaque, hwaddr addr, } val32 &= R_SW_FATAL_ERR_VAL_MASK; s->regs[reg] = ot_multibitbool_w1s_write(s->regs[reg], val32, 4u); - if (s->regs[reg] != OT_MULTIBITBOOL4_FALSE) { - ibex_irq_set(&s->alerts[R_SW_FATAL_ERR_VAL_SHIFT], 1); - } + 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): diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c index 3de8d68cad788..76a444dd25d42 100644 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ b/hw/opentitan/ot_ibex_wrapper_eg.c @@ -243,6 +243,19 @@ struct OtIbexWrapperEgState { 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) { @@ -797,11 +810,8 @@ static void ot_ibex_wrapper_eg_regs_write(void *opaque, hwaddr addr, switch (reg) { case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_ibex_wrapper_eg_update_alerts(s); break; case R_SW_FATAL_ERR: if ((val32 >> 16u) == 0xC0DEu) { @@ -822,9 +832,7 @@ static void ot_ibex_wrapper_eg_regs_write(void *opaque, hwaddr addr, } val32 &= R_SW_FATAL_ERR_VAL_MASK; s->regs[reg] = ot_multibitbool_w1s_write(s->regs[reg], val32, 4u); - if (s->regs[reg] != OT_MULTIBITBOOL4_FALSE) { - ibex_irq_set(&s->alerts[R_SW_FATAL_ERR_VAL_SHIFT], 1); - } + ot_ibex_wrapper_eg_update_alerts(s); break; case R_IBUS_REGWEN_0: case R_IBUS_REGWEN_1: diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index e1c374e19adba..4ab52abae17d9 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -484,6 +484,15 @@ ot_lc_ctrl_change_state_line(OtLcCtrlState *s, OtLcCtrlFsmState state, int line) s->state = state; } +static void ot_lc_ctrl_update_alerts(OtLcCtrlState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < NUM_ALERTS; ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static void ot_lc_ctrl_update_broadcast(OtLcCtrlState *s) { uint32_t sigbm = 0; @@ -1380,11 +1389,8 @@ static void ot_lc_ctrl_regs_write(OtLcCtrlState *s, hwaddr addr, uint32_t val32, switch (reg) { case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - if (val32 && (1u << ix)) { - ibex_irq_set(&s->alerts[ix], 1); - } - } + s->regs[R_ALERT_TEST] = val32; + ot_lc_ctrl_update_alerts(s); break; case R_CLAIM_TRANSITION_IF_REGWEN: val32 &= R_CLAIM_TRANSITION_IF_REGWEN_EN_MASK; @@ -1621,9 +1627,7 @@ static void ot_lc_ctrl_reset(DeviceState *dev) memset(&s->status_cache, 0, sizeof(s->status_cache)); - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_lc_ctrl_update_alerts(s); for (unsigned ix = 0; ix < ARRAY_SIZE(s->broadcasts); ix++) { ibex_irq_set(&s->broadcasts[ix], 0); diff --git a/hw/opentitan/ot_mbx.c b/hw/opentitan/ot_mbx.c index 1c9748a93d3f4..2636d684fc140 100644 --- a/hw/opentitan/ot_mbx.c +++ b/hw/opentitan/ot_mbx.c @@ -368,10 +368,8 @@ static void ot_mbx_host_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_HOST_ALERT_TEST: val32 &= HOST_ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&host->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } + for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { + ibex_irq_set(&host->alerts[ix], (int)((val32 >> ix) & 0x1u)); } break; case R_HOST_CONTROL: diff --git a/hw/opentitan/ot_otbn.c b/hw/opentitan/ot_otbn.c index 17744c1cab1ce..06466827635bb 100644 --- a/hw/opentitan/ot_otbn.c +++ b/hw/opentitan/ot_otbn.c @@ -130,6 +130,12 @@ typedef struct { bool entropy_requested; /* EDN request on-going */ } OtOTBNRandom; +typedef enum { + ALERT_FATAL, + ALERT_RECOVERABLE, + ALERT_COUNT, +} OtOtbnAlert; + struct OtOTBNState { /* */ SysBusDevice parent_obj; @@ -141,7 +147,7 @@ struct OtOTBNState { MemoryRegion dmem; IbexIRQ irq_done; - IbexIRQ alert; + IbexIRQ alerts[ALERT_COUNT]; IbexIRQ clkmgr; QEMUBH *proxy_completion_bh; @@ -182,8 +188,13 @@ static void ot_otbn_update_irq(OtOTBNState *s) static void ot_otbn_update_alert(OtOTBNState *s) { - bool level = s->alert_test || s->fatal_alert_cause; - ibex_irq_set(&s->alert, level); + for (unsigned ix = 0; ix < ALERT_COUNT; ix++) { + bool level = (bool)(s->alert_test & (1u << ix)); + if (ix == ALERT_FATAL) { + level |= (bool)s->fatal_alert_cause; + } + ibex_irq_set(&s->alerts[ix], level); + } } static void ot_otbn_post_execute(void *opaque) @@ -622,7 +633,9 @@ static void ot_otbn_reset(DeviceState *dev) s->last_cmd = OT_OTBN_CMD_NONE; ibex_irq_set(&s->irq_done, 0); - ibex_irq_set(&s->alert, 0); + for (unsigned ix = 0; ix < ALERT_COUNT; ix++) { + ibex_irq_set(&s->alerts[ix], 0); + } for (unsigned rix = 0; rix < (unsigned)OT_OTBN_RND_COUNT; rix++) { OtOTBNRandom *rnd = &s->rnds[rix]; @@ -664,7 +677,7 @@ static void ot_otbn_init(Object *obj) memory_region_add_subregion(&s->mmio, OT_OTBN_DMEM_BASE, &s->dmem); ibex_sysbus_init_irq(obj, &s->irq_done); - ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); + ibex_qdev_init_irqs(obj, s->alerts, OT_DEVICE_ALERT, ALERT_COUNT); ibex_qdev_init_irq(obj, &s->clkmgr, OT_CLOCK_ACTIVE); for (unsigned rix = 0; rix < (unsigned)OT_OTBN_RND_COUNT; rix++) { diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index a03f4b7360a0f..477093b861ce8 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -834,6 +834,7 @@ struct OtOTPDjState { IbexIRQ pwc_otp_rsp; uint32_t *regs; + uint32_t alert_bm; OtOTPLcBroadcast lc_broadcast; OtOTPDAIController *dai; @@ -1151,11 +1152,22 @@ static void ot_otp_dj_update_irqs(OtOTPDjState *s) { uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; + level |= s->alert_bm; + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); } } +static void ot_otp_dj_update_alerts(OtOTPDjState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static bool ot_otp_dj_is_wide_granule(int partition, unsigned address) { if ((unsigned)partition < OTP_PART_COUNT) { @@ -1192,7 +1204,8 @@ static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part, OtOTPError err) switch (err) { case OTP_MACRO_ERROR: case OTP_MACRO_ECC_UNCORR_ERROR: - ibex_irq_set(&s->alerts[ALERT_FATAL_MACRO_ERROR_SHIFT], 1); + s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; + ot_otp_dj_update_alerts(s); break; /* NOLINTNEXTLINE */ case OTP_MACRO_ECC_CORR_ERROR: @@ -1208,7 +1221,8 @@ static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part, OtOTPError err) break; case OTP_CHECK_FAIL_ERROR: case OTP_FSM_STATE_ERROR: - ibex_irq_set(&s->alerts[ALERT_FATAL_CHECK_ERROR_SHIFT], 1); + s->alert_bm |= ALERT_FATAL_CHECK_ERROR_MASK; + ot_otp_dj_update_alerts(s); break; default: break; @@ -2419,11 +2433,8 @@ static void ot_otp_dj_regs_write(void *opaque, hwaddr addr, uint64_t value, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - if (val32 && (1u << ix)) { - ibex_irq_set(&s->alerts[ix], 1); - } - } + s->regs[reg] = val32; + ot_otp_dj_update_alerts(s); break; case R_DIRECT_ACCESS_CMD: if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { @@ -3390,13 +3401,13 @@ static void ot_otp_dj_reset(DeviceState *dev) s->regs[R_EXT_NVM_READ_LOCK] = 0x1u; s->regs[R_ROM_PATCH_READ_LOCK] = 0x1u; + s->alert_bm = 0u; + s->lc_broadcast.level = 0u; s->lc_broadcast.signal = 0u; ot_otp_dj_update_irqs(s); - for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_otp_dj_update_alerts(s); ibex_irq_set(&s->pwc_otp_rsp, 0); for (unsigned ix = 0; ix < OTP_PART_COUNT; ix++) { diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 2bcf09a505879..da5b58e861a37 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -44,6 +44,7 @@ #include "sysemu/block-backend.h" #include "trace.h" +#define NUM_ALERTS 5u /* clang-format off */ /* Core registers */ @@ -53,11 +54,11 @@ REG32(INTR_STATE, 0x0u) REG32(INTR_ENABLE, 0x4u) REG32(INTR_TEST, 0x8u) REG32(ALERT_TEST, 0xcu) - FIELD(ALERT_TEST, FATAL_MACRO_ERROR, 0u, 1u) - FIELD(ALERT_TEST, FATAL_CHECK_ERROR, 1u, 1u) - FIELD(ALERT_TEST, FATAL_BUS_INTEG_ERROR, 2u, 1u) - FIELD(ALERT_TEST, FATAL_PRIM_OTP_ALERT, 3u, 1u) - FIELD(ALERT_TEST, RECOV_PRIM_OTP_ALERT, 4u, 1u) + SHARED_FIELD(ALERT_FATAL_MACRO_ERROR, 0u, 1u) + SHARED_FIELD(ALERT_FATAL_CHECK_ERROR, 1u, 1u) + SHARED_FIELD(ALERT_FATAL_BUS_INTEG_ERROR, 2u, 1u) + SHARED_FIELD(ALERT_FATAL_PRIM_OTP_ALERT, 3u, 1u) + SHARED_FIELD(ALERT_RECOV_PRIM_OTP_ALERT, 4u, 1u) REG32(STATUS, 0x10u) FIELD(STATUS, VENDOR_TEST_ERROR, 0u, 1u) FIELD(STATUS, CREATOR_SW_CFG_ERROR, 1u, 1u) @@ -233,11 +234,9 @@ REG32(LC_STATE, 2008u) #define INTR_MASK (INTR_OTP_OPERATION_DONE_MASK | INTR_OTP_ERROR_MASK) #define ALERT_TEST_MASK \ - (R_ALERT_TEST_FATAL_MACRO_ERROR_MASK | \ - R_ALERT_TEST_FATAL_CHECK_ERROR_MASK | \ - R_ALERT_TEST_FATAL_BUS_INTEG_ERROR_MASK | \ - R_ALERT_TEST_FATAL_PRIM_OTP_ALERT_MASK | \ - R_ALERT_TEST_RECOV_PRIM_OTP_ALERT_MASK) + (ALERT_FATAL_MACRO_ERROR_MASK | ALERT_FATAL_CHECK_ERROR_MASK | \ + ALERT_FATAL_BUS_INTEG_ERROR_MASK | ALERT_FATAL_PRIM_OTP_ALERT_MASK | \ + ALERT_RECOV_PRIM_OTP_ALERT_MASK) /* clang-format off */ REG32(CSR0, 0x0u) @@ -441,11 +440,13 @@ struct OtOTPEgState { unsigned tcount; } lc; IbexIRQ irqs[2u]; - IbexIRQ alert; + IbexIRQ alerts[NUM_ALERTS]; QEMUTimer *dai_delay; /**< Simulate delayed access completion */ uint32_t regs[REGS_COUNT]; + uint32_t alert_bm; + bool dai_busy; OtOTPStorage otp; @@ -475,6 +476,24 @@ static const OtOTPTokens OT_OTP_EG_TOKENS; (((_x_) << 0u) | ((_x_) << 5u) | ((_x_) << 10u) | ((_x_) << 15u) | \ ((_x_) << 20u) | ((_x_) << 25u)) +static void ot_otp_eg_update_irqs(OtOTPEgState *s) +{ + uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { + ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); + } +} + +static void ot_otp_eg_update_alerts(OtOTPEgState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static void ot_otp_eg_set_error(OtOTPEgState *s, int part, OtOTPError err) { unsigned err_off = (unsigned)part * 3u; @@ -485,9 +504,13 @@ static void ot_otp_eg_set_error(OtOTPEgState *s, int part, OtOTPError err) switch (err) { case OTP_MACRO_ERROR: case OTP_MACRO_ECC_UNCORR_ERROR: + s->alert_bm |= ALERT_FATAL_MACRO_ERROR_MASK; + ot_otp_eg_update_alerts(s); + break; case OTP_CHECK_FAIL_ERROR: case OTP_FSM_STATE_ERROR: - ibex_irq_set(&s->alert, 1u); + s->alert_bm |= ALERT_FATAL_CHECK_ERROR_MASK; + ot_otp_eg_update_alerts(s); break; default: break; @@ -565,15 +588,6 @@ static bool ot_otp_eg_swcfg_is_part_digest_offset(int part, hwaddr addr) return (addr == offset) || ((addr + (sizeof(uint32_t))) == offset); } -static void ot_otp_eg_update_irqs(OtOTPEgState *s) -{ - uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); - } -} - static bool ot_otp_eg_is_readable(OtOTPEgState *s, int partition, unsigned addr) { /* "in all partitions, the digest itself is ALWAYS readable." */ @@ -847,9 +861,8 @@ static void ot_otp_eg_regs_write(void *opaque, hwaddr addr, uint64_t value, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + s->regs[reg] = val32; + ot_otp_eg_update_alerts(s); break; case R_DIRECT_ACCESS_CMD: if (FIELD_EX32(val32, DIRECT_ACCESS_CMD, RD)) { @@ -1341,9 +1354,10 @@ static void ot_otp_eg_reset(DeviceState *dev) s->regs[R_CREATOR_SW_CFG_READ_LOCK] = 0x1u; s->regs[R_OWNER_SW_CFG_READ_LOCK] = 0x1u; s->dai_busy = false; + s->alert_bm = 0; ot_otp_eg_update_irqs(s); - ibex_irq_set(&s->alert, 0); + ot_otp_eg_update_alerts(s); } static void ot_otp_eg_realize(DeviceState *dev, Error **errp) @@ -1378,7 +1392,9 @@ static void ot_otp_eg_init(Object *obj) for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { ibex_sysbus_init_irq(obj, &s->irqs[ix]); } - ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); + } s->hw_cfg = g_new0(OtOTPHWCfg, 1u); s->entropy_cfg = g_new0(OtOTPEntropyCfg, 1u); diff --git a/hw/opentitan/ot_pinmux_dj.c b/hw/opentitan/ot_pinmux_dj.c index a190f4632af0b..175d11d5e93e1 100644 --- a/hw/opentitan/ot_pinmux_dj.c +++ b/hw/opentitan/ot_pinmux_dj.c @@ -317,9 +317,8 @@ static void ot_pinmux_dj_regs_write(void *opaque, hwaddr addr, uint64_t val64, switch (reg) { case R_ALERT_TEST: val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + regs->alert_test = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); break; case CASE_RANGE(MIO_PERIPH_INSEL_REGWEN, PARAM_N_MIO_PERIPH_IN): val32 &= R_MIO_PERIPH_INSEL_REGWEN_EN_MASK; diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index 5bf9b4d09a007..eed1f4f056b76 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -782,9 +782,8 @@ static void ot_pwrmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + s->regs[reg] = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); break; case R_CONTROL: /* TODO: clear LOW_POWER_HINT on next WFI? */ diff --git a/hw/opentitan/ot_rom_ctrl.c b/hw/opentitan/ot_rom_ctrl.c index 56a2851613462..982522f246f6d 100644 --- a/hw/opentitan/ot_rom_ctrl.c +++ b/hw/opentitan/ot_rom_ctrl.c @@ -356,9 +356,8 @@ static void ot_rom_ctrl_regs_write(void *opaque, hwaddr addr, uint64_t val64, switch (reg) { case R_ALERT_TEST: val32 &= R_ALERT_TEST_FATAL_ERROR_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + s->regs[reg] = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); break; case R_FATAL_ALERT_CAUSE: case R_DIGEST_0: diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 4ad3345b073c3..687f5763cb3f7 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -147,7 +147,7 @@ struct OtRstMgrState { MemoryRegion mmio; IbexIRQ sw_reset; - IbexIRQ alert; + IbexIRQ alerts[PARAM_NUM_ALERTS]; QEMUBH *bus_reset_bh; CPUState *cpu; @@ -215,6 +215,15 @@ void ot_rstmgr_reset_req(OtRstMgrState *s, bool fastclk, OtRstMgrResetReq req) /* Private implementation */ /* -------------------------------------------------------------------------- */ +static void ot_rstmgr_update_alerts(OtRstMgrState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static void ot_rstmgr_reset_bus(void *opaque) { OtRstMgrState *s = opaque; @@ -438,9 +447,8 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + s->regs[reg] = val32; + ot_rstmgr_update_alerts(s); break; case R_ALERT_INFO_ATTR: case R_ALERT_INFO: @@ -515,7 +523,7 @@ static void ot_rstmgr_reset(DeviceState *dev) } ibex_irq_set(&s->sw_reset, 0); - ibex_irq_set(&s->alert, 0); + ot_rstmgr_update_alerts(s); } static void ot_rstmgr_init(Object *obj) @@ -529,7 +537,7 @@ static void ot_rstmgr_init(Object *obj) s->regs = g_new0(uint32_t, REGS_COUNT); ibex_qdev_init_irq(obj, &s->sw_reset, OT_RSTMGR_SW_RST); - ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); + ibex_qdev_init_irqs(obj, s->alerts, OT_DEVICE_ALERT, PARAM_NUM_ALERTS); s->bus_reset_bh = qemu_bh_new(&ot_rstmgr_reset_bus, s); } diff --git a/hw/opentitan/ot_sensor.c b/hw/opentitan/ot_sensor.c index 2c1d10dd67a2f..16d5ca6dcc164 100644 --- a/hw/opentitan/ot_sensor.c +++ b/hw/opentitan/ot_sensor.c @@ -40,6 +40,8 @@ #include "hw/sysbus.h" #include "trace.h" +#define NUM_ALERTS 2u + /* clang-format off */ REG32(INTR_STATE, 0x0u) SHARED_FIELD(INTR_IO_STATUS_CHANGE, 0u, 1u) @@ -134,7 +136,7 @@ struct OtSensorState { MemoryRegion mmio; IbexIRQ irqs[2u]; - IbexIRQ alert; + IbexIRQ alerts[NUM_ALERTS]; uint32_t *regs; }; @@ -149,6 +151,15 @@ static void ot_sensor_update_irqs(OtSensorState *s) } } +static void ot_sensor_update_alerts(OtSensorState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static uint64_t ot_sensor_regs_read(void *opaque, hwaddr addr, unsigned size) { OtSensorState *s = opaque; @@ -224,9 +235,8 @@ static void ot_sensor_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + s->regs[reg] = val32; + ot_sensor_update_alerts(s); break; case R_CFG_REGWEN: case R_ALERT_TRIG: @@ -270,7 +280,7 @@ static void ot_sensor_reset(DeviceState *dev) s->regs[R_CFG_REGWEN] = 0x1u; ot_sensor_update_irqs(s); - ibex_irq_set(&s->alert, 0); + ot_sensor_update_alerts(s); } static void ot_sensor_init(Object *obj) @@ -285,7 +295,9 @@ static void ot_sensor_init(Object *obj) for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { ibex_sysbus_init_irq(obj, &s->irqs[ix]); } - ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_qdev_init_irq(obj, &s->alerts[ix], OT_DEVICE_ALERT); + } } static void ot_sensor_class_init(ObjectClass *klass, void *data) diff --git a/hw/opentitan/ot_soc_proxy.c b/hw/opentitan/ot_soc_proxy.c index db4efa5f3a95f..70cfd22095b5b 100644 --- a/hw/opentitan/ot_soc_proxy.c +++ b/hw/opentitan/ot_soc_proxy.c @@ -125,6 +125,15 @@ static void ot_soc_proxy_update_irqs(OtSoCProxyState *s) } } +static void ot_soc_proxy_update_alerts(OtSoCProxyState *s) +{ + uint32_t level = s->regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static void ot_soc_proxy_ingress_irq(void *opaque, int n, int level) { OtSoCProxyState *s = opaque; @@ -202,11 +211,8 @@ static void ot_soc_proxy_regs_write(void *opaque, hwaddr addr, uint64_t val64, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->regs[reg] = val32; + ot_soc_proxy_update_alerts(s); break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", @@ -237,9 +243,7 @@ static void ot_soc_proxy_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); ot_soc_proxy_update_irqs(s); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_soc_proxy_update_alerts(s); } static void ot_soc_proxy_init(Object *obj) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index d1cc5efb25bf0..508ee92a423aa 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -985,6 +985,15 @@ static void ot_spi_device_update_irqs(OtSPIDeviceState *s) } } +static void ot_spi_device_update_alerts(OtSPIDeviceState *s) +{ + uint32_t level = s->spi_regs[R_ALERT_TEST]; + + for (unsigned ix = 0; ix < ARRAY_SIZE(s->alerts); ix++) { + ibex_irq_set(&s->alerts[ix], (int)((level >> ix) & 0x1u)); + } +} + static OtSpiDeviceMode ot_spi_device_get_mode(const OtSPIDeviceState *s) { return (OtSpiDeviceMode)FIELD_EX32(s->spi_regs[R_CONTROL], CONTROL, MODE); @@ -1815,11 +1824,8 @@ static void ot_spi_device_spi_regs_write(void *opaque, hwaddr addr, break; case R_ALERT_TEST: val32 &= ALERT_TEST_MASK; - if (val32) { - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], (int)((val32 >> ix) & 0x1u)); - } - } + s->spi_regs[reg] = val32; + ot_spi_device_update_alerts(s); break; case R_CONTROL: val32 &= CONTROL_MASK; @@ -2604,9 +2610,7 @@ static void ot_spi_device_reset(DeviceState *dev) s->tpm_regs[R_TPM_CAP] = 0x660100u; ot_spi_device_update_irqs(s); - for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { - ibex_irq_set(&s->alerts[ix], 0); - } + ot_spi_device_update_alerts(s); } static void ot_spi_device_realize(DeviceState *dev, Error **errp) diff --git a/hw/opentitan/ot_spi_host.c b/hw/opentitan/ot_spi_host.c index 6283e752639ce..dcc977f01daa4 100644 --- a/hw/opentitan/ot_spi_host.c +++ b/hw/opentitan/ot_spi_host.c @@ -683,7 +683,6 @@ static void ot_spi_host_update_alert(OtSPIHostState *s) * register in QEMU */ bool alert = (bool)s->regs[R_ALERT_TEST]; - s->regs[R_ALERT_TEST] = 0u; ibex_irq_set(&s->alert, alert); } diff --git a/hw/opentitan/ot_sram_ctrl.c b/hw/opentitan/ot_sram_ctrl.c index 9d14ebcba7952..fe17457747ca8 100644 --- a/hw/opentitan/ot_sram_ctrl.c +++ b/hw/opentitan/ot_sram_ctrl.c @@ -381,9 +381,7 @@ static void ot_sram_ctrl_regs_write(void *opaque, hwaddr addr, uint64_t val64, switch (reg) { case R_ALERT_TEST: val32 &= R_ALERT_TEST_FATAL_ERROR_MASK; - if (val32) { - ibex_irq_set(&s->alert, (int)val32); - } + ibex_irq_set(&s->alert, (int)(bool)val32); break; case R_EXEC_REGWEN: val32 &= R_EXEC_REGWEN_EN_MASK; @@ -660,6 +658,8 @@ static void ot_sram_ctrl_reset(DeviceState *dev) } 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); } diff --git a/hw/opentitan/ot_timer.c b/hw/opentitan/ot_timer.c index 76ab0242642ab..d0699b8468567 100644 --- a/hw/opentitan/ot_timer.c +++ b/hw/opentitan/ot_timer.c @@ -146,8 +146,8 @@ static inline bool ot_timer_is_active(OtTimerState *s) static void ot_timer_update_alert(OtTimerState *s) { - bool level = s->regs[R_ALERT_TEST] & R_ALERT_TEST_FATAL_FAULT_MASK; - ibex_irq_set(&s->irq, level); + bool level = (bool)(s->regs[R_ALERT_TEST] & R_ALERT_TEST_FATAL_FAULT_MASK); + ibex_irq_set(&s->alert, level); } static void ot_timer_update_irqs(OtTimerState *s) @@ -261,7 +261,8 @@ static void ot_timer_write(void *opaque, hwaddr addr, uint64_t value, switch (reg) { case R_ALERT_TEST: - s->regs[R_ALERT_TEST] |= val32 & R_ALERT_TEST_FATAL_FAULT_MASK; + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + s->regs[reg] = val32; ot_timer_update_alert(s); break; case R_CTRL: { @@ -364,6 +365,7 @@ static void ot_timer_reset(DeviceState *dev) s->regs[R_COMPARE_UPPER0_0] = UINT32_MAX; ot_timer_update_irqs(s); + ot_timer_update_alert(s); } static void ot_timer_init(Object *obj) From 57d1403f97f0149c0249f9c9060710370a0e92e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Tue, 28 May 2024 19:11:44 +0200 Subject: [PATCH 14/65] [ot] hw/riscv: ot_earlgrey,ot_darjeeling: temporarily disable alert handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EarlGrey/Darjeeling alert handlers will be replaced by a new shared implementation in a later commit. Disable instantiation of current versions to avoid breaking bisect during the transition. Signed-off-by: Loïc Lefort --- hw/riscv/Kconfig | 2 -- hw/riscv/ot_darjeeling.c | 3 ++- hw/riscv/ot_earlgrey.c | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 718a169fb35b9..a38d720f3ed9d 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,7 +15,6 @@ config OT_DARJEELING select IBEX_COMMON select OT_ADDRESS_SPACE select OT_AES - select OT_ALERT_DJ select OT_AON_TIMER select OT_AST_DJ select OT_CLKMGR @@ -56,7 +55,6 @@ config OT_EARLGREY select IBEX select IBEX_COMMON select OT_AES - select OT_ALERT_EG select OT_AON_TIMER select OT_AST_EG select OT_CLKMGR diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 1a87f52f7983e..60ecf4e12d054 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -37,7 +37,6 @@ #include "hw/misc/unimp.h" #include "hw/opentitan/ot_address_space.h" #include "hw/opentitan/ot_aes.h" -#include "hw/opentitan/ot_alert_dj.h" #include "hw/opentitan/ot_aon_timer.h" #include "hw/opentitan/ot_ast_dj.h" #include "hw/opentitan/ot_clkmgr.h" @@ -1043,6 +1042,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("kmac-app", 1u) ) }, +#if 0 [OT_DJ_SOC_DEV_ALERT_HANDLER] = { .type = TYPE_OT_ALERT_DJ, .memmap = MEMMAPENTRIES( @@ -1061,6 +1061,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("edn-ep", 4u) ), }, +#endif [OT_DJ_SOC_DEV_SPI_HOST0] = { .type = TYPE_OT_SPI_HOST, .instance = 0, diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index ebd13065544e8..3b99007b4b831 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -34,7 +34,6 @@ #include "hw/misc/pulp_rv_dm.h" #include "hw/misc/unimp.h" #include "hw/opentitan/ot_aes.h" -#include "hw/opentitan/ot_alert_eg.h" #include "hw/opentitan/ot_aon_timer.h" #include "hw/opentitan/ot_ast_eg.h" #include "hw/opentitan/ot_clkmgr.h" @@ -536,6 +535,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_UINT_PROP("kmac-app", 1u) ) }, +#if 0 [OT_EG_SOC_DEV_ALERT_HANDLER] = { .type = TYPE_OT_ALERT_EG, .memmap = MEMMAPENTRIES( @@ -554,6 +554,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_UINT_PROP("edn-ep", 4u) ), }, +#endif [OT_EG_SOC_DEV_SPI_HOST0] = { .type = TYPE_OT_SPI_HOST, .instance = 0, From ee6f1f3a66aa7d01fe61592d825186bc1f569883 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 May 2024 18:33:49 +0200 Subject: [PATCH 15/65] [ot] hw/opentitan: ot_alert: implement alert handler Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 8 - hw/opentitan/meson.build | 2 - hw/opentitan/ot_alert.c | 1150 +++++++++++++++++++++++++++- hw/opentitan/ot_alert_dj.c | 683 ----------------- hw/opentitan/ot_alert_eg.c | 684 ----------------- hw/opentitan/trace-events | 16 +- include/hw/opentitan/ot_alert.h | 11 +- include/hw/opentitan/ot_alert_dj.h | 37 - include/hw/opentitan/ot_alert_eg.h | 38 - 9 files changed, 1159 insertions(+), 1470 deletions(-) delete mode 100644 hw/opentitan/ot_alert_dj.c delete mode 100644 hw/opentitan/ot_alert_eg.c delete mode 100644 include/hw/opentitan/ot_alert_dj.h delete mode 100644 include/hw/opentitan/ot_alert_eg.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 68cf434a31fee..94d0dba3c695a 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -10,14 +10,6 @@ config OT_AES config OT_ALERT bool -config OT_ALERT_DJ - select OT_ALERT - bool - -config OT_ALERT_EG - select OT_ALERT - bool - config OT_AON_TIMER bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 6b94e0a144066..373f17cb0d9f2 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -7,8 +7,6 @@ libtomcrypt_dep = subproject('libtomcrypt').get_variable('libtomcrypt_dep') system_ss.add(when: 'CONFIG_OT_ADDRESS_SPACE', if_true: files('ot_address_space.c')) system_ss.add(when: 'CONFIG_OT_AES', if_true: [files('ot_aes.c'), libtomcrypt_dep]) system_ss.add(when: 'CONFIG_OT_ALERT', if_true: files('ot_alert.c')) -system_ss.add(when: 'CONFIG_OT_ALERT_DJ', if_true: files('ot_alert_dj.c')) -system_ss.add(when: 'CONFIG_OT_ALERT_EG', if_true: files('ot_alert_eg.c')) system_ss.add(when: 'CONFIG_OT_AON_TIMER', if_true: files('ot_aon_timer.c')) system_ss.add(when: 'CONFIG_OT_AST_DJ', if_true: files('ot_ast_dj.c')) system_ss.add(when: 'CONFIG_OT_AST_EG', if_true: files('ot_ast_eg.c')) diff --git a/hw/opentitan/ot_alert.c b/hw/opentitan/ot_alert.c index 791290edeb287..33849571ed0a0 100644 --- a/hw/opentitan/ot_alert.c +++ b/hw/opentitan/ot_alert.c @@ -1,10 +1,10 @@ /* - * QEMU OpenTitan Alert handler + * QEMU OpenTitan Alert handler device * - * Copyright (c) 2023 Rivos, Inc. + * Copyright (c) 2023-2024 Rivos, Inc. * * Author(s): - * Loïc Lefort + * 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 @@ -23,15 +23,1151 @@ * 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: for now, only a minimalist subset of Alert Handler device is + * implemented in order to enable OpenTitan's ROM boot to progress */ #include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/typedefs.h" #include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_edn.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 "trace.h" + +#define PARAM_ESC_CNT_DW 32u +#define PARAM_ACCU_CNT_DW 16u +#define PARAM_N_ESC_SEV 4u +#define PARAM_PING_CNT_DW 16u +#define PARAM_PHASE_DW 2u +#define PARAM_CLASS_DW 2u + +/* clang-format off */ +REG32(INTR_STATE, 0x0u) + SHARED_FIELD(INTR_STATE_CLASSA, 0u, 1u) + SHARED_FIELD(INTR_STATE_CLASSB, 1u, 1u) + SHARED_FIELD(INTR_STATE_CLASSC, 2u, 1u) + SHARED_FIELD(INTR_STATE_CLASSD, 3u, 1u) +REG32(INTR_ENABLE, 0x4u) +REG32(INTR_TEST, 0x8u) +REG32(PING_TIMER_REGWEN, 0xcu) + FIELD(PING_TIMER_REGWEN, EN, 0u, 1u) +REG32(PING_TIMEOUT_CYC_SHADOWED, 0x10u) + FIELD(PING_TIMEOUT_CYC_SHADOWED, VAL, 0u, 16u) +REG32(PING_TIMER_EN_SHADOWED, 0x14u) + FIELD(PING_TIMER_EN_SHADOWED, EN, 0u, 1u) +SHARED_FIELD(ALERT_REGWEN_EN, 0u, 1u) +SHARED_FIELD(ALERT_EN_SHADOWED_EN, 0u, 1u) +SHARED_FIELD(ALERT_CLASS_SHADOWED_EN, 0u, 2u) +SHARED_FIELD(ALERT_CAUSE_EN, 0u, 1u) +SHARED_FIELD(LOC_ALERT_REGWEN_EN, 0u, 1u) +SHARED_FIELD(LOC_ALERT_EN_SHADOWED_EN, 0u, 1u) +SHARED_FIELD(LOC_ALERT_CLASS_SHADOWED_EN, 0u, 2u) +SHARED_FIELD(LOC_ALERT_CAUSE_EN, 0u, 1u) +SHARED_FIELD(CLASS_REGWEN_EN, 0u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_EN, 0u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_LOCK, 1u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E0, 2u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E1, 3u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E2, 4u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E3, 5u, 1u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E0, 6u, 2u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E1, 8u, 2u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E2, 10u, 2u) +SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E3, 12u, 2u) +SHARED_FIELD(CLASS_CLR_REGWEN_EN, 0u, 1u) +SHARED_FIELD(CLASS_CLR_SHADOWED_EN, 0u, 1u) +SHARED_FIELD(CLASS_ACCUM_CNT, 0u, 16u) +SHARED_FIELD(CLASS_ACCUM_THRESH_SHADOWED, 0u, 16u) +SHARED_FIELD(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0u, 2u) +SHARED_FIELD(CLASS_STATE, 0u, 3u) +/* clang-format on */ + +#define INTR_MASK ((1u << PARAM_N_CLASSES) - 1u) +#define CLASS_CTRL_SHADOWED_MASK \ + (CLASS_CTRL_SHADOWED_EN_MASK | CLASS_CTRL_SHADOWED_LOCK_MASK | \ + CLASS_CTRL_SHADOWED_EN_E0_MASK | CLASS_CTRL_SHADOWED_EN_E1_MASK | \ + CLASS_CTRL_SHADOWED_EN_E2_MASK | CLASS_CTRL_SHADOWED_EN_E3_MASK | \ + CLASS_CTRL_SHADOWED_MAP_E0_MASK | CLASS_CTRL_SHADOWED_MAP_E1_MASK | \ + CLASS_CTRL_SHADOWED_MAP_E2_MASK | CLASS_CTRL_SHADOWED_MAP_E3_MASK) + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) +#define REG_COUNT(_s_) (sizeof(_s_) / sizeof(OtShadowReg)) + +/* + * as many registers are shadowed, it is easier to use shadow registers + * for all registers, and only use the shadow 'committed' attribute for + * the rest of them (the non-shadow registers) + */ + +/* direct value of a 'fake' shadow register */ +#define DVAL(_shadow_) ((_shadow_).committed) + +#define ACLASS(_cls_) ((char)('A' + (_cls_))) + +typedef struct { + OtShadowReg state; + OtShadowReg enable; + OtShadowReg test; +} OtAlertIntr; + +typedef struct { + OtShadowReg timer_regwen; + OtShadowReg timeout_cyc_shadowed; + OtShadowReg timer_en_shadowed; +} OtAlertPing; + +/* not a real structure, only used to compute register spacing */ +typedef struct { + OtShadowReg regwen; + OtShadowReg en_shadowed; + OtShadowReg class_shadowed; + OtShadowReg cause; +} OtAlertTemplate; + +typedef struct { + OtShadowReg *regwen; + OtShadowReg *en_shadowed; + OtShadowReg *class_shadowed; + OtShadowReg *cause; +} OtAlertArrays; + +typedef struct { + OtShadowReg regwen; + OtShadowReg ctrl_shadowed; + OtShadowReg clr_regwen; + OtShadowReg clr_shadowed; + OtShadowReg accum_cnt; + OtShadowReg accum_thresh_shadowed; + OtShadowReg timeout_cyc_shadowed; + OtShadowReg crashdump_trigger_shadowed; + OtShadowReg phase_cyc_shadowed[4u]; + OtShadowReg esc_cnt; + OtShadowReg state; +} OtAlertAClass; + +typedef struct OtAlertRegs { + OtShadowReg *shadow; + /* shortcuts to the shadow entries */ + OtAlertIntr *intr; + OtAlertPing *ping; + OtAlertArrays alerts; + OtAlertArrays loc_alerts; + OtAlertAClass *classes; +} OtAlertRegs; + +typedef uint32_t (*ot_alert_reg_read_fn)(OtAlertState *s, unsigned reg); +typedef void (*ot_alert_reg_write_fn)(OtAlertState *s, unsigned reg, + uint32_t value); + +typedef enum { + PWA_UPDATE_IRQ, + PWA_CLEAR_ALERT, +} OtAlertPostWriteAction; + +typedef struct { + ot_alert_reg_read_fn read; + ot_alert_reg_write_fn write; + uint32_t mask; /* the mask to apply to the written value */ + uint16_t protect; /* not 0 if write protected by another register */ + uint8_t wpost; /* whether to perform post-write action */ +} OtAlertAccess; + +typedef struct { + /* count cycles: either timeout cycles or phase escalation cycles */ + QEMUTimer timer; + QEMUBH *esc_releaser; + OtAlertState *parent; + IbexIRQ *esc_tx_release; /* Escalate signal to release */ + unsigned nclass; +} OtAlertScheduler; + +enum { + LOCAL_ALERT_ALERT_PINGFAIL, + LOCAL_ALERT_ESC_PINGFAIL, + LOCAL_ALERT_ALERT_INTEGFAIL, + LOCAL_ALERT_ESC_INTEGFAIL, + LOCAL_ALERT_BUS_INTEGFAIL, + LOCAL_ALERT_SHADOW_REG_UPDATE_ERROR, + LOCAL_ALERT_SHADOW_REG_STORAGE_ERROR, + LOCAL_ALERT_COUNT, +}; + +typedef enum { + STATE_IDLE, + STATE_TIMEOUT, + STATE_FSMERROR, + STATE_TERMINAL, + STATE_PHASE0, + STATE_PHASE1, + STATE_PHASE2, + STATE_PHASE3, + STATE_COUNT, +} OtAlertAClassState; + +struct OtAlertState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + IbexIRQ *irqs; + IbexIRQ *esc_txs; + OtAlertScheduler *schedulers; + + OtAlertRegs regs; /* not ordered by register index */ + OtAlertAccess *access_table; /* ordered by register index */ + char **reg_names; /* ordered by register index */ + unsigned reg_count; /* total count of registers */ + unsigned reg_aclass_pos; /* index of the first register of OtAlertAClass */ + + char *ot_id; + OtEDNState *edn; + uint32_t pclk; + uint16_t n_alerts; + uint8_t edn_ep; + uint8_t n_low_power_groups; + uint8_t n_classes; +}; + +/* clang-format off */ +#define ST_NAME_ENTRY(_name_) [STATE_##_name_] = stringify(_name_) +#define ST_NAME(_st_) ((_st_) < ARRAY_SIZE(ST_NAMES) ? ST_NAMES[(_st_)] : "?") + +static const char *ST_NAMES[] = { + ST_NAME_ENTRY(IDLE), + ST_NAME_ENTRY(TIMEOUT), + ST_NAME_ENTRY(FSMERROR), + ST_NAME_ENTRY(TERMINAL), + ST_NAME_ENTRY(PHASE0), + ST_NAME_ENTRY(PHASE1), + ST_NAME_ENTRY(PHASE2), + ST_NAME_ENTRY(PHASE3), +}; +#undef ST_NAME_ENTRY + +#define R_ACC_MPA(_r_, _w_, _m_, _p_, _u_) (OtAlertAccess) { \ + .read = &ot_alert_reg_ ## _r_, \ + .write = &ot_alert_reg_ ## _w_, \ + .mask = (_m_), \ + .protect = (_p_), \ + .wpost = (_u_) \ +} +/* clang-format on */ +#define R_ACC_MP(_r_, _w_, _m_, _p_) R_ACC_MPA(_r_, _w_, _m_, _p_, 0) +#define R_ACC_M_IRQ(_r_, _w_, _m_) \ + R_ACC_MPA(_r_, _w_, _m_, 0, BIT(PWA_UPDATE_IRQ)) +#define R_ACC_P(_r_, _w_, _p_) R_ACC_MP(_r_, _w_, UINT32_MAX, _p_) +#define R_ACC_M(_r_, _w_, _m_) R_ACC_MP(_r_, _w_, _m_, 0) +#define R_ACC_IRQ(_r_, _w_) R_ACC_M_IRQ(_r_, _w_, UINT32_MAX) +#define R_ACC(_r_, _w_) R_ACC_M(_r_, _w_, UINT32_MAX) + +#define REG_NAME_LENGTH 36u /* > "CLASS_X_CRASHDUMP_TRIGGER_SHADOWED" */ + +#define REG_NAME(_s_, _reg_) \ + ((_reg_) < (_s_)->reg_count ? (_s_)->reg_names[(_reg_)] : "?") +#define CREATE_NAME_REGISTER(_s_, _reg_) \ + strncpy((_s_)->reg_names[R_##_reg_], stringify(_reg_), REG_NAME_LENGTH - 1) +#define CREATE_NAME_REG_IX_AT(_s_, _off_, _ix_, _reg_) \ + do { \ + int l = snprintf((_s_)->reg_names[(_off_)], REG_NAME_LENGTH, \ + stringify(_reg_) "_%02u", (_ix_)); \ + g_assert((unsigned)l < REG_NAME_LENGTH); \ + } while (0) +#define CREATE_NAME_REG_CLS_AT(_s_, _off_, _ix_, _reg_) \ + do { \ + int l = snprintf((_s_)->reg_names[(_off_)], REG_NAME_LENGTH, \ + "CLASS_%c_" stringify(_reg_), 'A' + (_ix_)); \ + g_assert((unsigned)l < REG_NAME_LENGTH); \ + } while (0) +#undef ALERT_SHOW_OT_ID_REG_NAME /* define as ot_id string here */ + +static unsigned +ot_alert_get_nclass_from_reg(const OtAlertState *s, unsigned reg) +{ + g_assert(reg >= s->reg_aclass_pos && reg < s->reg_count); + unsigned nclass = (reg - s->reg_aclass_pos) / REG_COUNT(OtAlertAClass); + g_assert(nclass < s->n_classes); + return nclass; +} + +static OtAlertAClassState +ot_alert_get_class_state(const OtAlertState *s, unsigned nclass) +{ + const OtAlertAClass *aclass = &s->regs.classes[nclass]; + + return DVAL(aclass->state); +} + +static void ot_alert_set_class_state(OtAlertState *s, unsigned nclass, + OtAlertAClassState state) +{ + trace_ot_alert_set_class_state(s->ot_id, ACLASS(nclass), + ST_NAME(DVAL(s->regs.classes[nclass].state)), + ST_NAME(state)); + + g_assert(state >= 0 && state < STATE_COUNT); + + DVAL(s->regs.classes[nclass].state) = state; +} + +static void ot_alert_update_irqs(OtAlertState *s) +{ + uint32_t level = DVAL(s->regs.intr->state) & DVAL(s->regs.intr->enable); + + trace_ot_alert_irqs(s->ot_id, DVAL(s->regs.intr->state), + DVAL(s->regs.intr->enable), level); + for (unsigned ix = 0; ix < s->n_classes; ix++) { + ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); + } +} + +static uint32_t ot_alert_reg_write_only(OtAlertState *s, unsigned reg) +{ + (void)s; + qemu_log_mask(LOG_GUEST_ERROR, "%s: W/O register 0x%03x\n", __func__, + (unsigned)(reg * sizeof(uint32_t))); + return 0; +} + +static void ot_alert_reg_read_only(OtAlertState *s, unsigned reg, + uint32_t value) +{ + (void)s; + (void)value; + qemu_log_mask(LOG_GUEST_ERROR, "%s: R/O register 0x%03x\n", __func__, + (unsigned)(reg * sizeof(uint32_t))); +} + +static uint32_t ot_alert_reg_direct_read(OtAlertState *s, unsigned reg) +{ + return DVAL(s->regs.shadow[reg]); +} + +static uint32_t ot_alert_reg_shadow_read(OtAlertState *s, unsigned reg) +{ + return ot_shadow_reg_read(&s->regs.shadow[reg]); +} + +static uint32_t ot_alert_reg_esc_count_read(OtAlertState *s, unsigned reg) +{ + unsigned nclass = ot_alert_get_nclass_from_reg(s, reg); + OtAlertAClass *aclass = &s->regs.classes[nclass]; + unsigned state = DVAL(aclass->state); + + QEMUTimer *timer = &s->schedulers[nclass].timer; + + uint64_t now = qemu_clock_get_ns(OT_VIRTUAL_CLOCK); + uint64_t expire = timer_expire_time_ns(timer); + if (expire == UINT64_MAX) { + trace_ot_alert_esc_count(s->ot_id, ACLASS(nclass), ST_NAME(state), 0); + return 0; + } + + uint32_t cycles; + + switch (state) { + case STATE_TIMEOUT: + cycles = ot_shadow_reg_peek(&aclass->timeout_cyc_shadowed); + break; + case STATE_PHASE0: + case STATE_PHASE1: + case STATE_PHASE2: + case STATE_PHASE3: + cycles = ot_shadow_reg_peek( + &aclass->phase_cyc_shadowed[state - STATE_PHASE0]); + break; + default: + trace_ot_alert_esc_count(s->ot_id, ACLASS(nclass), ST_NAME(state), 0); + return 0; + } + + uint32_t cnt; + if (expire >= now) { + uint64_t rem64 = + muldiv64(expire - now, s->pclk, NANOSECONDS_PER_SECOND); + uint32_t rem32 = (uint32_t)MIN(rem64, (uint64_t)UINT32_MAX); + cnt = (rem32 < cycles) ? cycles - rem32 : 0; + } else { + cnt = cycles; + } + + trace_ot_alert_esc_count(s->ot_id, ACLASS(nclass), ST_NAME(state), cnt); + + return cnt; +} + +static void ot_alert_reg_direct_write(OtAlertState *s, unsigned reg, + uint32_t value) +{ + if ((!s->access_table[reg].protect) || + DVAL(s->regs.shadow[s->access_table[reg].protect]) & 0x1) { + value &= s->access_table[reg].mask; + DVAL(s->regs.shadow[reg]) = value; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Register 0x%03x write protected by 0x%03x\n", + __func__, (unsigned)(reg * sizeof(uint32_t)), + (unsigned)(s->access_table[reg].protect * + sizeof(uint32_t))); + } +} + +static void ot_alert_reg_shadow_write(OtAlertState *s, unsigned reg, + uint32_t value) +{ + value &= s->access_table[reg].mask; + ot_shadow_reg_write(&s->regs.shadow[reg], value); +} + +static void +ot_alert_reg_direct_rw0c_write(OtAlertState *s, unsigned reg, uint32_t value) +{ + value &= s->access_table[reg].mask; + DVAL(s->regs.shadow[reg]) &= value; +} + +static void +ot_alert_reg_direct_rw1c_write(OtAlertState *s, unsigned reg, uint32_t value) +{ + value &= s->access_table[reg].mask; + DVAL(s->regs.shadow[reg]) &= ~value; +} + +static void +ot_alert_reg_intr_state_write(OtAlertState *s, unsigned reg, uint32_t value) +{ + value &= s->access_table[reg].mask; + DVAL(s->regs.shadow[reg]) &= ~value; + + for (unsigned ix = 0; ix < s->n_classes; ix++) { + /* + * "Software should clear the corresponding interrupt state bit + * INTR_STATE.CLASSn before the timeout expires to avoid escalation." + */ + if (value & (1u << ix)) { + OtAlertAClassState state = ot_alert_get_class_state(s, ix); + if (state == STATE_TIMEOUT) { + if (timer_pending(&s->schedulers[ix].timer)) { + trace_ot_alert_cancel_timeout(s->ot_id, ACLASS(ix)); + timer_del(&s->schedulers[ix].timer); + } + ot_alert_set_class_state(s, ix, STATE_IDLE); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: clearing IRQ for class %c in state %s " + "did not stop escalation", + __func__, s->ot_id, ACLASS(ix), ST_NAME(state)); + } + } + } +} + +static void ot_alert_set_class_timer(OtAlertState *s, unsigned nclass, + uint32_t timeout) +{ + OtAlertScheduler *atimer = &s->schedulers[nclass]; + /* TODO: update running schedulers if timeout_cyc_shadowed is updated */ + int64_t ns = (int64_t)muldiv64(timeout, NANOSECONDS_PER_SECOND, s->pclk); + + OtAlertAClassState state = ot_alert_get_class_state(s, nclass); + trace_ot_alert_set_class_timer(s->ot_id, ACLASS(nclass), ST_NAME(state), + ns / 1000, timeout); + + ns += qemu_clock_get_ns(OT_VIRTUAL_CLOCK); + if (ns < timer_expire_time_ns(&atimer->timer)) { + timer_mod_anticipate_ns(&atimer->timer, ns); + } +} + +static bool +ot_alert_is_escalation_enabled(OtAlertState *s, unsigned nclass, unsigned esc) +{ + const OtAlertAClass *aclass = &s->regs.classes[nclass]; + uint32_t ctrl = ot_shadow_reg_peek(&aclass->ctrl_shadowed); + + bool enable; + switch (esc) { + case 0: + enable = (bool)SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_EN_E0); + break; + case 1: + enable = (bool)SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_EN_E1); + break; + case 2: + enable = (bool)SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_EN_E2); + break; + case 3: + enable = (bool)SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_EN_E3); + break; + default: + g_assert_not_reached(); + break; + } + + return enable; +} + +static IbexIRQ * +ot_alert_get_escalation_output(OtAlertState *s, unsigned nclass, unsigned esc) +{ + const OtAlertAClass *aclass = &s->regs.classes[nclass]; + uint32_t ctrl = ot_shadow_reg_peek(&aclass->ctrl_shadowed); + + uint32_t out; + switch (esc) { + case 0: + out = SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_MAP_E0); + break; + case 1: + out = SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_MAP_E1); + break; + case 2: + out = SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_MAP_E2); + break; + case 3: + out = SHARED_FIELD_EX32(ctrl, CLASS_CTRL_SHADOWED_MAP_E3); + break; + default: + g_assert_not_reached(); + break; + } + + return &s->esc_txs[out]; +} + +static void ot_alert_clear_alert(OtAlertState *s, unsigned nclass) +{ + OtAlertAClass *aclass = &s->regs.classes[nclass]; + OtAlertAClassState state = DVAL(aclass->state); + + if (state == STATE_FSMERROR) { + trace_ot_alert_error(s->ot_id, ACLASS(nclass), + "cannot exit FSMERROR state"); + return; + } + + uint32_t ctrl = ot_shadow_reg_peek(&aclass->ctrl_shadowed); + + if (ctrl & CLASS_CTRL_SHADOWED_LOCK_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: class %c cannot clear escalation: locked\n", + __func__, s->ot_id, ACLASS(nclass)); + return; + } + + OtAlertScheduler *atimer = &s->schedulers[nclass]; + timer_del(&atimer->timer); + for (unsigned ix = 0; ix < PARAM_N_ESC_SEV; ix++) { + IbexIRQ *esc_tx = ot_alert_get_escalation_output(s, nclass, ix); + if (ibex_irq_get_level(esc_tx)) { + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), ix, "release"); + } + ibex_irq_set(esc_tx, 0); + } + /* + * "Software can clear CLASSn_ACCUM_CNT with a write to CLASSA_CLR_SHADOWED" + */ + DVAL(aclass->accum_cnt) = 0; + ot_alert_set_class_state(s, nclass, STATE_IDLE); +} + +static void ot_alert_configure_phase_cycles(OtAlertState *s, unsigned nclass) +{ + OtAlertAClass *aclass = &s->regs.classes[nclass]; + OtAlertAClassState state = DVAL(aclass->state); + + unsigned phase; + + switch (state) { + case STATE_PHASE0: + case STATE_PHASE1: + case STATE_PHASE2: + case STATE_PHASE3: + phase = state - STATE_PHASE0; + break; + default: + g_assert_not_reached(); + return; + } + + uint32_t cycles = + ot_shadow_reg_peek(&s->regs.classes[nclass].phase_cyc_shadowed[phase]); + ot_alert_set_class_timer(s, nclass, cycles); +} + +static void ot_alert_fsm_update(OtAlertState *s, unsigned nclass, + bool from_timer) +{ + OtAlertAClass *aclass = &s->regs.classes[nclass]; + OtAlertAClassState state = DVAL(aclass->state); + + bool accu_trig = DVAL(aclass->accum_cnt) > + ot_shadow_reg_peek(&aclass->accum_thresh_shadowed); + + trace_ot_alert_fsm_update(s->ot_id, ACLASS(nclass), ST_NAME(state), + from_timer, accu_trig); + + IbexIRQ *esc_tx; + switch (state) { + case STATE_IDLE: + if (accu_trig) { + unsigned esc = 0; + ot_alert_set_class_state(s, nclass, STATE_PHASE0); + if (ot_alert_is_escalation_enabled(s, nclass, esc)) { + esc_tx = ot_alert_get_escalation_output(s, nclass, esc); + ibex_irq_set(esc_tx, true); + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "activate"); + } else { + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "disabled"); + } + ot_alert_configure_phase_cycles(s, nclass); + } else { + uint32_t timeout = DVAL(aclass->timeout_cyc_shadowed); + if (timeout) { + ot_alert_set_class_state(s, nclass, STATE_TIMEOUT); + ot_alert_set_class_timer(s, nclass, timeout); + } + } + break; + case STATE_TIMEOUT: + if (from_timer || accu_trig) { + /* cancel timer, even if only useful on accu_trigg */ + OtAlertScheduler *atimer = &s->schedulers[nclass]; + timer_del(&atimer->timer); + ot_alert_set_class_state(s, nclass, STATE_PHASE0); + unsigned esc = 0; + if (ot_alert_is_escalation_enabled(s, nclass, esc)) { + esc_tx = ot_alert_get_escalation_output(s, nclass, esc); + ibex_irq_set(esc_tx, true); + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "activate"); + } else { + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "disabled"); + } + ot_alert_configure_phase_cycles(s, nclass); + } + break; + case STATE_PHASE0: + case STATE_PHASE1: + case STATE_PHASE2: + case STATE_PHASE3: + /* cycle count has reached threshold */ + if (from_timer) { + if (state <= STATE_PHASE2) { + /* + * Store escalation output to release before updating state. + * HW raise next escalation output before releasing current one. + * Use a BH to release with on "next" cycle. + */ + unsigned esc = state - STATE_PHASE0; + esc_tx = ot_alert_get_escalation_output(s, nclass, esc); + s->schedulers[nclass].esc_tx_release = esc_tx; + state += 1; + esc = state - STATE_PHASE0; + ot_alert_set_class_state(s, nclass, state); + if (ot_alert_is_escalation_enabled(s, nclass, esc)) { + esc_tx = ot_alert_get_escalation_output(s, nclass, esc); + ibex_irq_set(esc_tx, true); + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "activate"); + } else { + trace_ot_alert_escalation(s->ot_id, ACLASS(nclass), esc, + "disabled"); + } + ot_alert_configure_phase_cycles(s, nclass); + qemu_bh_schedule(s->schedulers[nclass].esc_releaser); + } else /* PHASE3 */ { + ot_alert_set_class_state(s, nclass, STATE_TERMINAL); + } + } + break; + case STATE_TERMINAL: + break; + case STATE_FSMERROR: + default: + g_assert_not_reached(); + break; + } +} + +static void ot_alert_timer_expire(void *opaque) +{ + OtAlertScheduler *scheduler = opaque; + OtAlertState *s = scheduler->parent; + + trace_ot_alert_timer_expire(s->ot_id, ACLASS(scheduler->nclass)); + + ot_alert_fsm_update(s, scheduler->nclass, true); +} + +static void ot_alert_release_esc_fn(void *opaque) +{ + OtAlertScheduler *scheduler = opaque; + + IbexIRQ *esc_tx = scheduler->esc_tx_release; + g_assert(esc_tx); + + if (ibex_irq_get_level(esc_tx)) { + OtAlertState *s = scheduler->parent; + unsigned ix = (unsigned)(uintptr_t)(esc_tx - &s->esc_txs[0]); + trace_ot_alert_escalation(s->ot_id, ACLASS(scheduler->nclass), ix, + "release"); + } + ibex_irq_set(esc_tx, false); + scheduler->esc_tx_release = NULL; +} + +static void ot_alert_signal_tx(void *opaque, int n, int level) +{ + OtAlertState *s = opaque; + + unsigned alert = (unsigned)n; + + g_assert(alert < s->n_alerts); + + OtAlertArrays *alerts = &s->regs.alerts; + bool alert_en = ot_shadow_reg_peek(&alerts->en_shadowed[alert]); + + trace_ot_alert_signal_tx(s->ot_id, alert, (bool)level, alert_en); + + if (!alert_en || !level) { + /* releasing the alert does not clear it */ + return; + } + + DVAL(alerts->cause[alert]) |= ALERT_CAUSE_EN_MASK; + + unsigned nclass = ot_shadow_reg_peek(&alerts->class_shadowed[alert]); + + OtAlertAClass *aclass = &s->regs.classes[nclass]; + + uint32_t ac_ctrl = ot_shadow_reg_peek(&aclass->ctrl_shadowed); + bool class_en = (bool)(ac_ctrl & CLASS_CTRL_SHADOWED_EN_MASK); + + trace_ot_alert_signal_class(s->ot_id, alert, ACLASS(nclass), class_en); + + if (class_en) { + DVAL(s->regs.intr->state) |= 1u << nclass; + + /* saturate (no roll over) */ + if (DVAL(aclass->accum_cnt) < CLASS_ACCUM_CNT_MASK) { + DVAL(aclass->accum_cnt) += 1u; + } + + ot_alert_fsm_update(s, nclass, false); + } + + ot_alert_update_irqs(s); +} + +static uint64_t ot_alert_regs_read(void *opaque, hwaddr addr, unsigned size) +{ + OtAlertState *s = opaque; + (void)size; + uint32_t val32; + + hwaddr reg = R32_OFF(addr); + + if (reg >= s->reg_count) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s:Invalid register 0x%03" HWADDR_PRIx "\n", __func__, + addr); + return 0; + } + + val32 = (*s->access_table[reg].read)(s, reg); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_alert_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(s, reg), + val32, pc); + + return (uint64_t)val32; +}; + +static void ot_alert_regs_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) +{ + OtAlertState *s = opaque; + (void)size; + uint32_t val32 = (uint32_t)val64; + + hwaddr reg = R32_OFF(addr); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_alert_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:Invalid register 0x%03" HWADDR_PRIx "\n", __func__, + addr); + return; + } + + (*s->access_table[reg].write)(s, reg, val32); + + if (s->access_table[reg].wpost & BIT(PWA_UPDATE_IRQ)) { + ot_alert_update_irqs(s); + } + + if (s->access_table[reg].wpost & BIT(PWA_CLEAR_ALERT)) { + unsigned nclass = ot_alert_get_nclass_from_reg(s, reg); + ot_alert_clear_alert(s, nclass); + } +}; + +static void ot_alert_fill_access_table(OtAlertState *s) +{ + OtAlertAccess *table = s->access_table; + + table[R_INTR_STATE] = R_ACC_IRQ(direct_read, intr_state_write); + table[R_INTR_ENABLE] = R_ACC_IRQ(direct_read, direct_write); + table[R_INTR_TEST] = R_ACC(write_only, direct_write); + table[R_PING_TIMER_REGWEN] = R_ACC(direct_read, direct_write); + table[R_PING_TIMEOUT_CYC_SHADOWED] = R_ACC(shadow_read, shadow_write); + table[R_PING_TIMER_EN_SHADOWED] = R_ACC(shadow_read, shadow_write); + OtShadowReg *first_var_reg = s->regs.alerts.regwen; + + unsigned offset = (unsigned)(first_var_reg - &s->regs.shadow[0]); + + /* ALERT_REGWEN */ + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + table[offset + ix] = + R_ACC_M(direct_read, direct_rw0c_write, ALERT_REGWEN_EN_MASK); + } + offset += s->n_alerts; + + /* ALERT_EN_SHADOWED */ + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + table[offset + ix] = + R_ACC_M(shadow_read, shadow_write, ALERT_EN_SHADOWED_EN_MASK); + } + offset += s->n_alerts; + + /* ALERT_CLASS_SHADOWED */ + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + table[offset + ix] = + R_ACC_M(shadow_read, shadow_write, s->n_classes - 1u); + } + offset += s->n_alerts; + + /* ALERT_CAUSE */ + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + table[offset + ix] = + R_ACC_M(direct_read, direct_rw1c_write, ALERT_CAUSE_EN_MASK); + } + offset += s->n_alerts; + + /* LOC_ALERT_REGWEN */ + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + table[offset + ix] = + R_ACC_M(direct_read, direct_rw0c_write, LOC_ALERT_REGWEN_EN_MASK); + } + offset += LOCAL_ALERT_COUNT; + + /* LOC_ALERT_EN_SHADOWED */ + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + table[offset + ix] = + R_ACC_M(shadow_read, shadow_write, LOC_ALERT_EN_SHADOWED_EN_MASK); + } + offset += LOCAL_ALERT_COUNT; + + /* LOC_ALERT_CLASS_SHADOWED */ + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + table[offset + ix] = + R_ACC_M(shadow_read, shadow_write, s->n_classes - 1u); + } + offset += LOCAL_ALERT_COUNT; + + /* LOC_ALERT_CAUSE */ + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + table[offset + ix] = + R_ACC_M(direct_read, direct_rw1c_write, LOC_ALERT_CAUSE_EN_MASK); + } + offset += LOCAL_ALERT_COUNT; + + for (unsigned ix = 0; ix < s->n_classes; ix++) { + /* CLASS_REGWEN */ + unsigned regwen = offset; + table[offset++] = + R_ACC_M(direct_read, direct_rw0c_write, CLASS_REGWEN_EN_MASK); + + /* CLASS_CTRL_SHADOWED */ + table[offset++] = R_ACC_MP(shadow_read, shadow_write, + CLASS_CTRL_SHADOWED_MASK, regwen); + + /* CLASS_CLR_REGWEN */ + unsigned clr_regwen = offset; + table[offset++] = + R_ACC_M(direct_read, direct_rw0c_write, CLASS_CLR_REGWEN_EN_MASK); + + /* CLASS_CLR_SHADOWED */ + table[offset++] = + R_ACC_MPA(shadow_read, shadow_write, CLASS_CLR_SHADOWED_EN_MASK, + clr_regwen, BIT(PWA_CLEAR_ALERT)); + + /* CLASS_ACCUM_CNT */ + table[offset++] = R_ACC(direct_read, read_only); + + /* CLASS_ACCUM_THRESH_SHADOWED */ + table[offset++] = + R_ACC_MP(shadow_read, shadow_write, UINT16_MAX, regwen); + + /* CLASS_TIMEOUT_CYC_SHADOWED */ + table[offset++] = R_ACC_P(shadow_read, shadow_write, regwen); + + /* CLASS_CRASHDUMP_TRIGGER_SHADOWED */ + table[offset++] = + R_ACC_MP(shadow_read, shadow_write, + CLASS_CRASHDUMP_TRIGGER_SHADOWED_MASK, regwen); + + /* CLASS_PHASE0_CYC_SHADOWED */ + table[offset++] = R_ACC_P(shadow_read, shadow_write, regwen); + + /* CLASS_PHASE1_CYC_SHADOWED */ + table[offset++] = R_ACC_P(shadow_read, shadow_write, regwen); + + /* CLASS_PHASE2_CYC_SHADOWED */ + table[offset++] = R_ACC_P(shadow_read, shadow_write, regwen); + + /* CLASS_PHASE3_CYC_SHADOWED */ + table[offset++] = R_ACC_P(shadow_read, shadow_write, regwen); + + /* CLASS_ESC_CNT */ + table[offset++] = R_ACC(esc_count_read, read_only); + + /* CLASS_STATE */ + table[offset++] = R_ACC(direct_read, read_only); + } + + g_assert(offset == s->reg_count); +} + +static Property ot_alert_properties[] = { + DEFINE_PROP_STRING("ot_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), + DEFINE_PROP_UINT32("pclk", OtAlertState, pclk, 0u), + DEFINE_PROP_LINK("edn", OtAlertState, edn, TYPE_OT_EDN, OtEDNState *), + DEFINE_PROP_UINT8("edn-ep", OtAlertState, edn_ep, UINT8_MAX), + DEFINE_PROP_END_OF_LIST(), +}; + +static const MemoryRegionOps ot_alert_regs_ops = { + .read = &ot_alert_regs_read, + .write = &ot_alert_regs_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static void ot_alert_reset(DeviceState *dev) +{ + OtAlertState *s = OT_ALERT(dev); + + for (unsigned ix = 0; ix < s->n_classes; ix++) { + timer_del(&s->schedulers[ix].timer); + } + + memset(s->regs.shadow, 0, sizeof(OtShadowReg) * s->reg_count); + + DVAL(s->regs.ping->timer_regwen) = R_PING_TIMER_REGWEN_EN_MASK; + ot_shadow_reg_init(&s->regs.ping->timeout_cyc_shadowed, 256u); + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + /* direct register */ + DVAL(s->regs.alerts.regwen[ix]) = ALERT_REGWEN_EN_MASK; + } + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + /* direct register */ + DVAL(s->regs.loc_alerts.regwen[ix]) = LOC_ALERT_REGWEN_EN_MASK; + } + for (unsigned ix = 0; ix < s->n_classes; ix++) { + DVAL(s->regs.classes[ix].regwen) = CLASS_REGWEN_EN_MASK; + ot_shadow_reg_init(&s->regs.classes[ix].ctrl_shadowed, 0x393cu); + DVAL(s->regs.classes[ix].clr_regwen) = CLASS_CLR_REGWEN_EN_MASK; + } + + ot_alert_update_irqs(s); +} + +static void ot_alert_realize(DeviceState *dev, Error **errp) +{ + (void)errp; + + OtAlertState *s = OT_ALERT(dev); + + g_assert(s->n_alerts != 0); + g_assert(s->pclk != 0); + g_assert(s->n_classes > 0 && s->n_classes <= 32); + + if (!s->ot_id) { + s->ot_id = + g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); + } + + size_t size = sizeof(OtAlertIntr) + sizeof(OtAlertPing) + + sizeof(OtAlertTemplate) * s->n_alerts + + sizeof(OtAlertTemplate) * LOCAL_ALERT_COUNT + + sizeof(OtAlertAClass) * s->n_classes; + + memory_region_init_io(&s->mmio, OBJECT(dev), &ot_alert_regs_ops, s, + TYPE_OT_ALERT, size); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); + + s->irqs = g_new0(IbexIRQ, s->n_classes); + for (unsigned ix = 0; ix < s->n_classes; ix++) { + ibex_sysbus_init_irq(OBJECT(dev), &s->irqs[ix]); + } + + s->esc_txs = g_new0(IbexIRQ, PARAM_N_ESC_SEV); + ibex_qdev_init_irqs(OBJECT(dev), s->esc_txs, OT_ALERT_ESCALATE, + PARAM_N_ESC_SEV); + + qdev_init_gpio_in_named(dev, &ot_alert_signal_tx, OT_DEVICE_ALERT, + s->n_alerts); + + s->schedulers = g_new0(OtAlertScheduler, s->n_classes); + for (unsigned ix = 0; ix < s->n_classes; ix++) { + s->schedulers[ix].parent = s; + s->schedulers[ix].nclass = ix; + timer_init_full(&s->schedulers[ix].timer, NULL, OT_VIRTUAL_CLOCK, + SCALE_NS, 0, &ot_alert_timer_expire, + &s->schedulers[ix]); + s->schedulers[ix].esc_releaser = + qemu_bh_new(&ot_alert_release_esc_fn, &s->schedulers[ix]); + s->schedulers[ix].esc_tx_release = NULL; + } + + s->reg_count = size / sizeof(OtShadowReg); + s->regs.shadow = g_new0(OtShadowReg, s->reg_count); + s->access_table = g_new0(OtAlertAccess, s->reg_count); + s->reg_names = g_new0(char *, s->reg_count); + + OtShadowReg *reg = s->regs.shadow; + s->regs.intr = (OtAlertIntr *)reg; + reg += REG_COUNT(OtAlertIntr); + s->regs.ping = (OtAlertPing *)reg; + reg += REG_COUNT(OtAlertPing); + s->regs.alerts.regwen = reg; + reg += s->n_alerts; + s->regs.alerts.en_shadowed = reg; + reg += s->n_alerts; + s->regs.alerts.class_shadowed = reg; + reg += s->n_alerts; + s->regs.alerts.cause = reg; + reg += s->n_alerts; + s->regs.loc_alerts.regwen = reg; + reg += LOCAL_ALERT_COUNT; + s->regs.loc_alerts.en_shadowed = reg; + reg += LOCAL_ALERT_COUNT; + s->regs.loc_alerts.class_shadowed = reg; + reg += LOCAL_ALERT_COUNT; + s->regs.loc_alerts.cause = reg; + reg += LOCAL_ALERT_COUNT; + s->regs.classes = (OtAlertAClass *)reg; + s->reg_aclass_pos = (unsigned)(uintptr_t)(reg - s->regs.shadow); + reg += REG_COUNT(OtAlertAClass) * s->n_classes; + + g_assert(reg - s->regs.shadow == s->reg_count); + + 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; + } + unsigned nreg = 0; + g_assert(s->reg_count > REG_COUNT(OtAlertIntr) + REG_COUNT(OtAlertPing)); + CREATE_NAME_REGISTER(s, INTR_STATE); + CREATE_NAME_REGISTER(s, INTR_ENABLE); + CREATE_NAME_REGISTER(s, INTR_TEST); + nreg += REG_COUNT(OtAlertIntr); + CREATE_NAME_REGISTER(s, PING_TIMER_REGWEN); + CREATE_NAME_REGISTER(s, PING_TIMEOUT_CYC_SHADOWED); + CREATE_NAME_REGISTER(s, PING_TIMER_EN_SHADOWED); + nreg += REG_COUNT(OtAlertPing); + for (unsigned ix = 0; ix < s->n_alerts; ix++) { + CREATE_NAME_REG_IX_AT(s, nreg + s->n_alerts * 0u + ix, ix, + ALERT_REGWEN); + CREATE_NAME_REG_IX_AT(s, nreg + s->n_alerts * 1u + ix, ix, + ALERT_EN_SHADOWED); + CREATE_NAME_REG_IX_AT(s, nreg + s->n_alerts * 2u + ix, ix, + ALERT_CLASS_SHADOWED); + CREATE_NAME_REG_IX_AT(s, nreg + s->n_alerts * 3u + ix, ix, ALERT_CAUSE); + } + nreg += REG_COUNT(OtAlertTemplate) * s->n_alerts; + for (unsigned ix = 0; ix < LOCAL_ALERT_COUNT; ix++) { + CREATE_NAME_REG_IX_AT(s, nreg + LOCAL_ALERT_COUNT * 0u + ix, ix, + LOC_ALERT_REGWEN); + CREATE_NAME_REG_IX_AT(s, nreg + LOCAL_ALERT_COUNT * 1u + ix, ix, + LOC_ALERT_EN_SHADOWED); + CREATE_NAME_REG_IX_AT(s, nreg + LOCAL_ALERT_COUNT * 2u + ix, ix, + LOC_ALERT_CLASS_SHADOWED); + CREATE_NAME_REG_IX_AT(s, nreg + LOCAL_ALERT_COUNT * 3u + ix, ix, + LOC_ALERT_CAUSE); + } + nreg += REG_COUNT(OtAlertTemplate) * LOCAL_ALERT_COUNT; + for (unsigned ix = 0; ix < s->n_classes; ix++) { + CREATE_NAME_REG_CLS_AT(s, nreg + 0u, ix, REGWEN); + CREATE_NAME_REG_CLS_AT(s, nreg + 1u, ix, CTRL_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 2u, ix, CLR_REGWEN); + CREATE_NAME_REG_CLS_AT(s, nreg + 3u, ix, CLR_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 4u, ix, ACCUM_CNT); + CREATE_NAME_REG_CLS_AT(s, nreg + 5u, ix, ACCUM_THRESH_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 6u, ix, TIMEOUT_CYC_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 7u, ix, CRASHDUMP_TRIGGER_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 8u, ix, PHASE0_CYC_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 9u, ix, PHASE1_CYC_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 10u, ix, PHASE2_CYC_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 11u, ix, PHASE3_CYC_SHADOWED); + CREATE_NAME_REG_CLS_AT(s, nreg + 12u, ix, ESC_CNT); + CREATE_NAME_REG_CLS_AT(s, nreg + 13u, ix, STATE); + nreg += 14u; + } + g_assert(nreg == s->reg_count); + +#ifdef ALERT_SHOW_OT_ID_REG_NAME + if (!strcmp(s->ot_id, ALERT_SHOW_OT_ID_REG_NAME)) { + fprintf(stderr, "nreg %u regcount %u\n", nreg, s->reg_count); + for (unsigned ix = 0; ix < nreg; ix++) { + fprintf(stderr, "reg[%03x]: %s\n", ix, s->reg_names[ix]); + } + } +#endif + + ot_alert_fill_access_table(s); +} + +static void ot_alert_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; -OBJECT_DEFINE_ABSTRACT_TYPE(OtAlertState, ot_alert, OT_ALERT, SYS_BUS_DEVICE) + dc->reset = &ot_alert_reset; + dc->realize = &ot_alert_realize; + device_class_set_props(dc, ot_alert_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} -static void ot_alert_class_init(ObjectClass *oc, void *data) {} +static const TypeInfo ot_alert_info = { + .name = TYPE_OT_ALERT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtAlertState), + .class_init = &ot_alert_class_init, +}; -static void ot_alert_init(Object *obj) {} +static void ot_alert_register_types(void) +{ + type_register_static(&ot_alert_info); +} -static void ot_alert_finalize(Object *obj) {} +type_init(ot_alert_register_types); diff --git a/hw/opentitan/ot_alert_dj.c b/hw/opentitan/ot_alert_dj.c deleted file mode 100644 index d6c16aff58761..0000000000000 --- a/hw/opentitan/ot_alert_dj.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * QEMU OpenTitan Darjeeling Alert handler device - * - * Copyright (c) 2023-2024 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. - * - * Note: for now, only a minimalist subset of Alert Handler device is - * implemented in order to enable OpenTitan's ROM boot to progress - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/typedefs.h" -#include "hw/opentitan/ot_alert_dj.h" -#include "hw/opentitan/ot_edn.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 "trace.h" - -#define PARAM_N_ALERTS 100u -#define PARAM_N_LPG 19u -#define PARAM_N_LPG_WIDTH 5u -#define PARAM_ESC_CNT_DW 32u -#define PARAM_ACCU_CNT_DW 16u -#define PARAM_N_CLASSES 4u -#define PARAM_N_ESC_SEV 4u -#define PARAM_N_PHASES 4u -#define PARAM_N_LOC_ALERT 7u -#define PARAM_PING_CNT_DW 16u -#define PARAM_PHASE_DW 2u -#define PARAM_CLASS_DW 2u - -/* clang-format off */ -REG32(INTR_STATE, 0x0u) - SHARED_FIELD(INTR_STATE_CLASSA, 0u, 1u) - SHARED_FIELD(INTR_STATE_CLASSB, 1u, 1u) - SHARED_FIELD(INTR_STATE_CLASSC, 2u, 1u) - SHARED_FIELD(INTR_STATE_CLASSD, 3u, 1u) -REG32(INTR_ENABLE, 0x4u) -REG32(INTR_TEST, 0x8u) -REG32(PING_TIMER_REGWEN, 0xcu) - FIELD(PING_TIMER_REGWEN, EN, 0u, 1u) -REG32(PING_TIMEOUT_CYC_SHADOWED, 0x10u) - FIELD(PING_TIMEOUT_CYC_SHADOWED, VAL, 0u, 16u) -REG32(PING_TIMER_EN_SHADOWED, 0x14u) - FIELD(PING_TIMER_EN_SHADOWED, EN, 0u, 1u) -REG32(ALERT_REGWEN, 0x18u) - SHARED_FIELD(ALERT_REGWEN_EN, 0u, 1u) -REG32(ALERT_EN_SHADOWED, 0x1a8u) - SHARED_FIELD(ALERT_EN_SHADOWED_EN, 0u, 1u) -REG32(ALERT_CLASS_SHADOWED, 0x338u) - SHARED_FIELD(ALERT_CLASS_SHADOWED_EN, 0u, 2u) -REG32(ALERT_CAUSE, 0x4c8u) - SHARED_FIELD(ALERT_CAUSE_EN, 0u, 1u) -REG32(LOC_ALERT_REGWEN, 0x658u) - SHARED_FIELD(LOC_ALERT_REGWEN_EN, 0u, 1u) -REG32(LOC_ALERT_EN_SHADOWED, 0x674u) - SHARED_FIELD(LOC_ALERT_EN_SHADOWED_EN, 0u, 1u) -REG32(LOC_ALERT_CLASS_SHADOWED, 0x690u) - SHARED_FIELD(LOC_ALERT_CLASS_SHADOWED_EN, 0u, 2u) -REG32(LOC_ALERT_CAUSE, 0x6acu) - SHARED_FIELD(LOC_ALERT_CAUSE_EN, 0u, 1u) -REG32(CLASS_REGWEN, 0x6c8u) - FIELD(CLASS_REGWEN, EN, 0u, 1u) -REG32(CLASS_CTRL_SHADOWED, 0x6ccu) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN, 0u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_LOCK, 1u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E0, 2u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E1, 3u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E2, 4u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E3, 5u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E0, 6u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E1, 8u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E2, 10u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E3, 12u, 2u) -REG32(CLASS_CLR_REGWEN, 0x6d0u) - SHARED_FIELD(CLASS_CLR_REGWEN_EN, 0u, 1u) -REG32(CLASS_CLR_SHADOWED, 0x6d4u) - SHARED_FIELD(CLASS_CLR_SHADOWED_EN, 0u, 1u) -REG32(CLASS_ACCUM_CNT, 0x6d8u) - SHARED_FIELD(CLASS_ACCUM_CNT, 0u, 16u) -REG32(CLASS_ACCUM_THRESH_SHADOWED, 0x6dcu) - SHARED_FIELD(CLASS_ACCUM_THRESH_SHADOWED, 0u, 16u) -REG32(CLASS_TIMEOUT_CYC_SHADOWED, 0x6e0u) -REG32(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0x6e4u) - SHARED_FIELD(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0u, 2u) -REG32(CLASS_PHASE0_CYC_SHADOWED, 0x6e8u) -REG32(CLASS_PHASE1_CYC_SHADOWED, 0x6ecu) -REG32(CLASS_PHASE2_CYC_SHADOWED, 0x6f0u) -REG32(CLASS_PHASE3_CYC_SHADOWED, 0x6f4u) -REG32(CLASS_ESC_CNT, 0x6f8u) -REG32(CLASS_STATE, 0x6fcu) - FIELD(CLASS_STATE, VAL, 0u, 3u) -/* clang-format on */ - -enum { - ALERT_ID_ALERT_PINGFAIL, - ALERT_ID_ESC_PINGFAIL, - ALERT_ID_ALERT_INTEGFAIL, - ALERT_ID_ESC_INTEGFAIL, - ALERT_ID_BUS_INTEGFAIL, - ALERT_ID_SHADOW_REG_UPDATE_ERROR, - ALERT_ID_SHADOW_REG_STORAGE_ERROR, -}; - -enum { - ALERT_CLASSA, - ALERT_CLASSB, - ALERT_CLASSC, - ALERT_CLASSD, -}; - -enum { - STATE_IDLE, - STATE_TIMEOUT, - STATE_FSMERROR, - STATE_TERMINAL, - STATE_PHASE0, - STATE_PHASE1, - STATE_PHASE2, - STATE_PHASE3, -}; - -#define INTR_MASK ((1u << PARAM_N_CLASSES) - 1u) -#define CLASS_CTRL_SHADOWED_MASK \ - (CLASS_CTRL_SHADOWED_EN_MASK | CLASS_CTRL_SHADOWED_LOCK_MASK | \ - CLASS_CTRL_SHADOWED_EN_E0_MASK | CLASS_CTRL_SHADOWED_EN_E1_MASK | \ - CLASS_CTRL_SHADOWED_EN_E2_MASK | CLASS_CTRL_SHADOWED_EN_E3_MASK | \ - CLASS_CTRL_SHADOWED_MAP_E0_MASK | CLASS_CTRL_SHADOWED_MAP_E1_MASK | \ - CLASS_CTRL_SHADOWED_MAP_E2_MASK | CLASS_CTRL_SHADOWED_MAP_E3_MASK) - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) - -#define R_LAST_REG R32_OFF(0x7a4u) -#define REGS_COUNT (R_LAST_REG + 1u) -#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) - -#define ALERT_SLOT_SIZE R32_OFF(sizeof(struct alerts)) -#define LOC_ALERT_SLOT_SIZE R32_OFF(sizeof(struct loc_alerts)) -#define CLASS_SLOT_SIZE R32_OFF(sizeof(struct classes)) -#define CASE_RANGE(_reg_, _cnt_) (_reg_)...((_reg_) + (_cnt_) - (1u)) -#define CASE_STRIDE(_reg_, _cls_) ((_reg_) + (_cls_) * (CLASS_SLOT_SIZE)) -#define SLOT_OFFSET(_reg_, _base_, _kind_) \ - (((_reg_) - (_base_)) / _kind_##_SLOT_SIZE) -#define ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_ALERT_REGWEN, ALERT) -#define LOC_ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_LOC_ALERT_REGWEN, LOC_ALERT) -#define CLASS_SLOT(_reg_) SLOT_OFFSET(_reg_, R_CLASS_REGWEN, CLASS) - -#define CHECK_REGWEN(_reg_, _cond_) \ - ot_alert_dj_check_regwen(__func__, (_reg_), (_cond_)) - -struct intr { - uint32_t state; - uint32_t enable; - uint32_t test; -}; - -struct ping { - uint32_t timer_regwen; - uint32_t timeout_cyc_shadowed; - uint32_t timer_en_shadowed; -}; - -struct alerts { - uint32_t regwen; - uint32_t en_shadowed; - uint32_t class_shadowed; - uint32_t cause; -}; - -struct loc_alerts { - uint32_t regwen; - uint32_t en_shadowed; - uint32_t class_shadowed; - uint32_t cause; -}; - -struct classes { - uint32_t regwen; - uint32_t ctrl_shadowed; - uint32_t clr_regwen; - uint32_t clr_shadowed; - uint32_t accum_cnt; - uint32_t accum_thresh_shadowed; - uint32_t timeout_cyc_shadowed; - uint32_t crashdump_trigger_shadowed; - uint32_t phase0_cyc_shadowed; - uint32_t phase1_cyc_shadowed; - uint32_t phase2_cyc_shadowed; - uint32_t phase3_cyc_shadowed; - uint32_t esc_cnt; - uint32_t state; -}; - -typedef struct OtAlertRegs { - struct intr intr; - struct ping ping; - struct alerts alerts[PARAM_N_ALERTS]; - struct loc_alerts loc_alerts[PARAM_N_LOC_ALERT]; - struct classes classes[PARAM_N_CLASSES]; -} OtAlertRegs; - -struct OtAlertDjState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - IbexIRQ irqs[PARAM_N_CLASSES]; - - OtAlertRegs *regs; - - OtEDNState *edn; - uint8_t edn_ep; -}; - -static inline bool -ot_alert_dj_check_regwen(const char *func, unsigned reg, bool cond) -{ - if (!cond) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: reg 0x%04x is write-protected\n", - func, (unsigned)(reg * sizeof(uint32_t))); - return false; - } - return true; -} - -static void ot_alert_dj_update_irqs(OtAlertDjState *s) -{ - uint32_t level = s->regs->intr.state & s->regs->intr.enable; - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); - } -} - -static uint64_t ot_alert_dj_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - OtAlertDjState *s = opaque; - (void)size; - OtAlertRegs *regs = s->regs; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - - switch (reg) { - case R_INTR_STATE: - val32 = regs->intr.state; - break; - case R_INTR_ENABLE: - val32 = regs->intr.enable; - break; - case R_PING_TIMER_REGWEN: - val32 = regs->ping.timer_regwen; - break; - case R_PING_TIMEOUT_CYC_SHADOWED: - val32 = regs->ping.timeout_cyc_shadowed; - break; - case R_PING_TIMER_EN_SHADOWED: - val32 = regs->ping.timer_en_shadowed; - break; - case R_INTR_TEST: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: W/O register 0x%03" HWADDR_PRIx "\n", __func__, - addr); - val32 = 0; - break; - case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].regwen; - break; - case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].en_shadowed; - break; - case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].class_shadowed; - break; - case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].cause; - break; - case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen; - break; - case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed; - break; - case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].class_shadowed; - break; - case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause; - break; - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].regwen; - break; - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].ctrl_shadowed; - break; - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].clr_regwen; - break; - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].clr_shadowed; - break; - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].accum_cnt; - break; - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed; - break; - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].esc_cnt; - break; - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): - val32 = - regs->classes[reg - CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA)].state; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); - val32 = 0; - break; - } - - uint32_t pc = ibex_get_current_pc(); - trace_ot_alert_io_read_out((uint32_t)addr, val32, pc); - - return (uint64_t)val32; -}; - -static void ot_alert_dj_regs_write(void *opaque, hwaddr addr, uint64_t val64, - unsigned size) -{ - OtAlertDjState *s = opaque; - (void)size; - OtAlertRegs *regs = s->regs; - uint32_t val32 = (uint32_t)val64; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - trace_ot_alert_io_write((uint32_t)addr, val32, pc); - - switch (reg) { - case R_INTR_STATE: - val32 &= INTR_MASK; - regs->intr.state &= ~val32; /* RW1C */ - ot_alert_dj_update_irqs(s); - break; - case R_INTR_ENABLE: - val32 &= INTR_MASK; - regs->intr.enable = val32; - ot_alert_dj_update_irqs(s); - break; - case R_INTR_TEST: - val32 &= INTR_MASK; - regs->intr.state |= val32; - ot_alert_dj_update_irqs(s); - break; - case R_PING_TIMER_REGWEN: - val32 &= R_PING_TIMER_REGWEN_EN_MASK; - regs->ping.timer_regwen &= ~val32; /* RW1C */ - break; - case R_PING_TIMEOUT_CYC_SHADOWED: - val32 &= R_PING_TIMEOUT_CYC_SHADOWED_VAL_MASK; - regs->ping.timeout_cyc_shadowed = val32; - break; - case R_PING_TIMER_EN_SHADOWED: - val32 = R_PING_TIMER_EN_SHADOWED_EN_MASK; /* RW1S */ - regs->ping.timer_en_shadowed |= val32; - break; - case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): - val32 &= ALERT_REGWEN_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): - if (CHECK_REGWEN(reg, regs->alerts[reg - R_ALERT_EN_SHADOWED].regwen)) { - val32 &= ALERT_EN_SHADOWED_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): - if (CHECK_REGWEN(reg, - regs->alerts[reg - R_ALERT_CLASS_SHADOWED].regwen)) { - val32 &= ALERT_CLASS_SHADOWED_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): - val32 = ALERT_CAUSE_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ - break; - case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): - val32 &= LOC_ALERT_REGWEN_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): - if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { - val32 &= LOC_ALERT_EN_SHADOWED_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): - if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { - val32 &= LOC_ALERT_CLASS_SHADOWED_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): - val32 = LOC_ALERT_CAUSE_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ - break; - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): - val32 = R_CLASS_REGWEN_EN_MASK; - regs->classes[CLASS_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_CTRL_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].ctrl_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): - val32 &= CLASS_CLR_REGWEN_EN_MASK; - regs->classes[CLASS_SLOT(reg)].clr_regwen &= val32; /* RW0C */ - break; - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].clr_regwen)) { - val32 &= CLASS_CLR_SHADOWED_EN_MASK; - regs->classes[CLASS_SLOT(reg)].clr_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_ACCUM_THRESH_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_CRASHDUMP_TRIGGER_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): - qemu_log_mask(LOG_GUEST_ERROR, - "%s: R/O register 0x%03" HWADDR_PRIx "\n", __func__, - addr); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); - break; - } -}; - -static Property ot_alert_dj_properties[] = { - DEFINE_PROP_LINK("edn", OtAlertDjState, edn, TYPE_OT_EDN, OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtAlertDjState, edn_ep, UINT8_MAX), - DEFINE_PROP_END_OF_LIST(), -}; - -static const MemoryRegionOps ot_alert_dj_regs_ops = { - .read = &ot_alert_dj_regs_read, - .write = &ot_alert_dj_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static void ot_alert_dj_reset(DeviceState *dev) -{ - OtAlertDjState *s = OT_ALERT_DJ(dev); - - OtAlertRegs *regs = s->regs; - memset(regs, 0, sizeof(*regs)); - - regs->ping.timer_regwen = 0x1u; - regs->ping.timeout_cyc_shadowed = 0x100u; - for (unsigned ix = 0; ix < PARAM_N_ALERTS; ix++) { - regs->alerts[ix].regwen = 0x1u; - } - for (unsigned ix = 0; ix < PARAM_N_LOC_ALERT; ix++) { - regs->loc_alerts[ix].regwen = 0x1u; - } - for (unsigned ix = 0; ix < PARAM_N_CLASSES; ix++) { - regs->classes[ix].regwen = 0x1u; - regs->classes[ix].ctrl_shadowed = 0x393cu; - regs->classes[ix].clr_regwen = 0x1u; - } - - ot_alert_dj_update_irqs(s); -} - -static void ot_alert_dj_init(Object *obj) -{ - OtAlertDjState *s = OT_ALERT_DJ(obj); - - memory_region_init_io(&s->mmio, obj, &ot_alert_dj_regs_ops, s, - TYPE_OT_ALERT_DJ, REGS_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); - - s->regs = g_new0(OtAlertRegs, 1); - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_sysbus_init_irq(obj, &s->irqs[ix]); - } -} - -static void ot_alert_dj_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - (void)data; - - dc->reset = &ot_alert_dj_reset; - device_class_set_props(dc, ot_alert_dj_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo ot_alert_dj_info = { - .name = TYPE_OT_ALERT_DJ, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OtAlertDjState), - .instance_init = &ot_alert_dj_init, - .class_size = sizeof(OtAlertStateClass), - .class_init = &ot_alert_dj_class_init, -}; - -static void ot_alert_dj_register_types(void) -{ - type_register_static(&ot_alert_dj_info); -} - -type_init(ot_alert_dj_register_types); diff --git a/hw/opentitan/ot_alert_eg.c b/hw/opentitan/ot_alert_eg.c deleted file mode 100644 index 68dfa0cc4a022..0000000000000 --- a/hw/opentitan/ot_alert_eg.c +++ /dev/null @@ -1,684 +0,0 @@ -/* - * QEMU OpenTitan EarlGrey Alert handler device - * - * Copyright (c) 2023-2024 Rivos, Inc. - * - * 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. - * - * Note: for now, only a minimalist subset of Alert Handler device is - * implemented in order to enable OpenTitan's ROM boot to progress - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/typedefs.h" -#include "hw/opentitan/ot_alert_eg.h" -#include "hw/opentitan/ot_edn.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 "trace.h" - -#define PARAM_N_ALERTS 65u -#define PARAM_N_LPG 24u -#define PARAM_N_LPG_WIDTH 5u -#define PARAM_ESC_CNT_DW 32u -#define PARAM_ACCU_CNT_DW 16u -#define PARAM_N_CLASSES 4u -#define PARAM_N_ESC_SEV 4u -#define PARAM_N_PHASES 4u -#define PARAM_N_LOC_ALERT 7u -#define PARAM_PING_CNT_DW 16u -#define PARAM_PHASE_DW 2u -#define PARAM_CLASS_DW 2u - -/* clang-format off */ -REG32(INTR_STATE, 0x0u) - SHARED_FIELD(INTR_STATE_CLASSA, 0u, 1u) - SHARED_FIELD(INTR_STATE_CLASSB, 1u, 1u) - SHARED_FIELD(INTR_STATE_CLASSC, 2u, 1u) - SHARED_FIELD(INTR_STATE_CLASSD, 3u, 1u) -REG32(INTR_ENABLE, 0x4u) -REG32(INTR_TEST, 0x8u) -REG32(PING_TIMER_REGWEN, 0xcu) - FIELD(PING_TIMER_REGWEN, EN, 0u, 1u) -REG32(PING_TIMEOUT_CYC_SHADOWED, 0x10u) - FIELD(PING_TIMEOUT_CYC_SHADOWED, VAL, 0u, 16u) -REG32(PING_TIMER_EN_SHADOWED, 0x14u) - FIELD(PING_TIMER_EN_SHADOWED, EN, 0u, 1u) -REG32(ALERT_REGWEN, 0x18u) - SHARED_FIELD(ALERT_REGWEN_EN, 0u, 1u) -REG32(ALERT_EN_SHADOWED, 0x11cu) - SHARED_FIELD(ALERT_EN_SHADOWED_EN, 0u, 1u) -REG32(ALERT_CLASS_SHADOWED, 0x220u) - SHARED_FIELD(ALERT_CLASS_SHADOWED_EN, 0u, 2u) -REG32(ALERT_CAUSE, 0x324u) - SHARED_FIELD(ALERT_CAUSE_EN, 0u, 1u) -REG32(LOC_ALERT_REGWEN, 0x428u) - SHARED_FIELD(LOC_ALERT_REGWEN_EN, 0u, 1u) -REG32(LOC_ALERT_EN_SHADOWED, 0x444u) - SHARED_FIELD(LOC_ALERT_EN_SHADOWED_EN, 0u, 1u) -REG32(LOC_ALERT_CLASS_SHADOWED, 0x460u) - SHARED_FIELD(LOC_ALERT_CLASS_SHADOWED_EN, 0u, 2u) -REG32(LOC_ALERT_CAUSE, 0x47cu) - SHARED_FIELD(LOC_ALERT_CAUSE_EN, 0u, 1u) -REG32(CLASS_REGWEN, 0x498u) - FIELD(CLASS_REGWEN, EN, 0u, 1u) -REG32(CLASS_CTRL_SHADOWED, 0x49cu) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN, 0u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_LOCK, 1u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E0, 2u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E1, 3u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E2, 4u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_EN_E3, 5u, 1u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E0, 6u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E1, 8u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E2, 10u, 2u) - SHARED_FIELD(CLASS_CTRL_SHADOWED_MAP_E3, 12u, 2u) -REG32(CLASS_CLR_REGWEN, 0x4a0u) - SHARED_FIELD(CLASS_CLR_REGWEN_EN, 0u, 1u) -REG32(CLASS_CLR_SHADOWED, 0x4a4u) - SHARED_FIELD(CLASS_CLR_SHADOWED_EN, 0u, 1u) -REG32(CLASS_ACCUM_CNT, 0x4a8u) - SHARED_FIELD(CLASS_ACCUM_CNT, 0u, 16u) -REG32(CLASS_ACCUM_THRESH_SHADOWED, 0x4acu) - SHARED_FIELD(CLASS_ACCUM_THRESH_SHADOWED, 0u, 16u) -REG32(CLASS_TIMEOUT_CYC_SHADOWED, 0x4b0u) -REG32(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0x4b4u) - SHARED_FIELD(CLASS_CRASHDUMP_TRIGGER_SHADOWED, 0u, 2u) -REG32(CLASS_PHASE0_CYC_SHADOWED, 0x4b8u) -REG32(CLASS_PHASE1_CYC_SHADOWED, 0x4bcu) -REG32(CLASS_PHASE2_CYC_SHADOWED, 0x4c0u) -REG32(CLASS_PHASE3_CYC_SHADOWED, 0x4c4u) -REG32(CLASS_ESC_CNT, 0x4c8u) -REG32(CLASS_STATE, 0x4ccu) - FIELD(CLASS_STATE, VAL, 0u, 3u) -/* clang-format on */ - -enum { - ALERT_ID_ALERT_PINGFAIL, - ALERT_ID_ESC_PINGFAIL, - ALERT_ID_ALERT_INTEGFAIL, - ALERT_ID_ESC_INTEGFAIL, - ALERT_ID_BUS_INTEGFAIL, - ALERT_ID_SHADOW_REG_UPDATE_ERROR, - ALERT_ID_SHADOW_REG_STORAGE_ERROR, -}; - -enum { - ALERT_CLASSA, - ALERT_CLASSB, - ALERT_CLASSC, - ALERT_CLASSD, -}; - -enum { - STATE_IDLE, - STATE_TIMEOUT, - STATE_FSMERROR, - STATE_TERMINAL, - STATE_PHASE0, - STATE_PHASE1, - STATE_PHASE2, - STATE_PHASE3, -}; - -#define INTR_MASK ((1u << PARAM_N_CLASSES) - 1u) -#define CLASS_CTRL_SHADOWED_MASK \ - (CLASS_CTRL_SHADOWED_EN_MASK | CLASS_CTRL_SHADOWED_LOCK_MASK | \ - CLASS_CTRL_SHADOWED_EN_E0_MASK | CLASS_CTRL_SHADOWED_EN_E1_MASK | \ - CLASS_CTRL_SHADOWED_EN_E2_MASK | CLASS_CTRL_SHADOWED_EN_E3_MASK | \ - CLASS_CTRL_SHADOWED_MAP_E0_MASK | CLASS_CTRL_SHADOWED_MAP_E1_MASK | \ - CLASS_CTRL_SHADOWED_MAP_E2_MASK | CLASS_CTRL_SHADOWED_MAP_E3_MASK) - -#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) - -#define R_LAST_REG R32_OFF(0x574u) -#define REGS_COUNT (R_LAST_REG + 1u) -#define REGS_SIZE (REGS_COUNT * sizeof(uint32_t)) - -#define ALERT_SLOT_SIZE R32_OFF(sizeof(struct alerts)) -#define LOC_ALERT_SLOT_SIZE R32_OFF(sizeof(struct loc_alerts)) -#define CLASS_SLOT_SIZE R32_OFF(sizeof(struct classes)) -#define CASE_RANGE(_reg_, _cnt_) (_reg_)...((_reg_) + (_cnt_) - (1u)) -#define CASE_STRIDE(_reg_, _cls_) ((_reg_) + (_cls_) * (CLASS_SLOT_SIZE)) -#define SLOT_OFFSET(_reg_, _base_, _kind_) \ - (((_reg_) - (_base_)) / _kind_##_SLOT_SIZE) -#define ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_ALERT_REGWEN, ALERT) -#define LOC_ALERT_SLOT(_reg_) SLOT_OFFSET(_reg_, R_LOC_ALERT_REGWEN, LOC_ALERT) -#define CLASS_SLOT(_reg_) SLOT_OFFSET(_reg_, R_CLASS_REGWEN, CLASS) - -#define CHECK_REGWEN(_reg_, _cond_) \ - ot_alert_eg_check_regwen(__func__, (_reg_), (_cond_)) - -struct intr { - uint32_t state; - uint32_t enable; - uint32_t test; -}; - -struct ping { - uint32_t timer_regwen; - uint32_t timeout_cyc_shadowed; - uint32_t timer_en_shadowed; -}; - -struct alerts { - uint32_t regwen; - uint32_t en_shadowed; - uint32_t class_shadowed; - uint32_t cause; -}; - -struct loc_alerts { - uint32_t regwen; - uint32_t en_shadowed; - uint32_t class_shadowed; - uint32_t cause; -}; - -struct classes { - uint32_t regwen; - uint32_t ctrl_shadowed; - uint32_t clr_regwen; - uint32_t clr_shadowed; - uint32_t accum_cnt; - uint32_t accum_thresh_shadowed; - uint32_t timeout_cyc_shadowed; - uint32_t crashdump_trigger_shadowed; - uint32_t phase0_cyc_shadowed; - uint32_t phase1_cyc_shadowed; - uint32_t phase2_cyc_shadowed; - uint32_t phase3_cyc_shadowed; - uint32_t esc_cnt; - uint32_t state; -}; - -typedef struct OtAlertRegs { - struct intr intr; - struct ping ping; - struct alerts alerts[PARAM_N_ALERTS]; - struct loc_alerts loc_alerts[PARAM_N_LOC_ALERT]; - struct classes classes[PARAM_N_CLASSES]; -} OtAlertRegs; - -struct OtAlertEgState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - IbexIRQ irqs[PARAM_N_CLASSES]; - - OtAlertRegs *regs; - - OtEDNState *edn; - uint8_t edn_ep; -}; - -static inline bool -ot_alert_eg_check_regwen(const char *func, unsigned reg, bool cond) -{ - if (!cond) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: reg 0x%04x is write-protected\n", - func, (unsigned)(reg * sizeof(uint32_t))); - return false; - } - return true; -} - -static void ot_alert_eg_update_irqs(OtAlertEgState *s) -{ - uint32_t level = s->regs->intr.state & s->regs->intr.enable; - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1)); - } -} - -static uint64_t ot_alert_eg_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - OtAlertEgState *s = opaque; - (void)size; - OtAlertRegs *regs = s->regs; - uint32_t val32; - - hwaddr reg = R32_OFF(addr); - - switch (reg) { - case R_INTR_STATE: - val32 = regs->intr.state; - break; - case R_INTR_ENABLE: - val32 = regs->intr.enable; - break; - case R_PING_TIMER_REGWEN: - val32 = regs->ping.timer_regwen; - break; - case R_PING_TIMEOUT_CYC_SHADOWED: - val32 = regs->ping.timeout_cyc_shadowed; - break; - case R_PING_TIMER_EN_SHADOWED: - val32 = regs->ping.timer_en_shadowed; - break; - case R_INTR_TEST: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: W/O register 0x%03" HWADDR_PRIx "\n", __func__, - addr); - val32 = 0; - break; - case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].regwen; - break; - case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].en_shadowed; - break; - case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].class_shadowed; - break; - case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): - val32 = regs->alerts[ALERT_SLOT(reg)].cause; - break; - case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen; - break; - case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed; - break; - case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].class_shadowed; - break; - case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): - val32 = regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause; - break; - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].regwen; - break; - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].ctrl_shadowed; - break; - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].clr_regwen; - break; - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].clr_shadowed; - break; - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].accum_cnt; - break; - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed; - break; - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed; - break; - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): - val32 = regs->classes[CLASS_SLOT(reg)].esc_cnt; - break; - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): - val32 = - regs->classes[reg - CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA)].state; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); - val32 = 0; - break; - } - - uint32_t pc = ibex_get_current_pc(); - trace_ot_alert_io_read_out((uint32_t)addr, val32, pc); - - return (uint64_t)val32; -}; - -static void ot_alert_eg_regs_write(void *opaque, hwaddr addr, uint64_t val64, - unsigned size) -{ - OtAlertEgState *s = opaque; - (void)size; - OtAlertRegs *regs = s->regs; - uint32_t val32 = (uint32_t)val64; - - hwaddr reg = R32_OFF(addr); - - uint32_t pc = ibex_get_current_pc(); - trace_ot_alert_io_write((uint32_t)addr, val32, pc); - - switch (reg) { - case R_INTR_STATE: - val32 &= INTR_MASK; - regs->intr.state &= ~val32; /* RW1C */ - ot_alert_eg_update_irqs(s); - break; - case R_INTR_ENABLE: - val32 &= INTR_MASK; - regs->intr.enable = val32; - ot_alert_eg_update_irqs(s); - break; - case R_INTR_TEST: - val32 &= INTR_MASK; - regs->intr.state |= val32; - ot_alert_eg_update_irqs(s); - break; - case R_PING_TIMER_REGWEN: - val32 &= R_PING_TIMER_REGWEN_EN_MASK; - regs->ping.timer_regwen &= ~val32; /* RW1C */ - break; - case R_PING_TIMEOUT_CYC_SHADOWED: - val32 &= R_PING_TIMEOUT_CYC_SHADOWED_VAL_MASK; - regs->ping.timeout_cyc_shadowed = val32; - break; - case R_PING_TIMER_EN_SHADOWED: - val32 = R_PING_TIMER_EN_SHADOWED_EN_MASK; /* RW1S */ - regs->ping.timer_en_shadowed |= val32; - break; - case CASE_RANGE(R_ALERT_REGWEN, PARAM_N_ALERTS): - val32 &= ALERT_REGWEN_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_RANGE(R_ALERT_EN_SHADOWED, PARAM_N_ALERTS): - if (CHECK_REGWEN(reg, regs->alerts[reg - R_ALERT_EN_SHADOWED].regwen)) { - val32 &= ALERT_EN_SHADOWED_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_ALERT_CLASS_SHADOWED, PARAM_N_ALERTS): - if (CHECK_REGWEN(reg, - regs->alerts[reg - R_ALERT_CLASS_SHADOWED].regwen)) { - val32 &= ALERT_CLASS_SHADOWED_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_ALERT_CAUSE, PARAM_N_ALERTS): - val32 = ALERT_CAUSE_EN_MASK; - regs->alerts[ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ - break; - case CASE_RANGE(R_LOC_ALERT_REGWEN, PARAM_N_LOC_ALERT): - val32 &= LOC_ALERT_REGWEN_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_RANGE(R_LOC_ALERT_EN_SHADOWED, PARAM_N_LOC_ALERT): - if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { - val32 &= LOC_ALERT_EN_SHADOWED_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_LOC_ALERT_CLASS_SHADOWED, PARAM_N_LOC_ALERT): - if (CHECK_REGWEN(reg, regs->loc_alerts[LOC_ALERT_SLOT(reg)].regwen)) { - val32 &= LOC_ALERT_CLASS_SHADOWED_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].en_shadowed = val32; - } - break; - case CASE_RANGE(R_LOC_ALERT_CAUSE, PARAM_N_LOC_ALERT): - val32 = LOC_ALERT_CAUSE_EN_MASK; - regs->loc_alerts[LOC_ALERT_SLOT(reg)].cause &= ~val32; /* RW1C */ - break; - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_REGWEN, ALERT_CLASSD): - val32 = R_CLASS_REGWEN_EN_MASK; - regs->classes[CLASS_SLOT(reg)].regwen &= val32; /* RW0C */ - break; - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CTRL_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_CTRL_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].ctrl_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_REGWEN, ALERT_CLASSD): - val32 &= CLASS_CLR_REGWEN_EN_MASK; - regs->classes[CLASS_SLOT(reg)].clr_regwen &= val32; /* RW0C */ - break; - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CLR_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].clr_regwen)) { - val32 &= CLASS_CLR_SHADOWED_EN_MASK; - regs->classes[CLASS_SLOT(reg)].clr_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_THRESH_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_ACCUM_THRESH_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].accum_thresh_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_TIMEOUT_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].timeout_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_CRASHDUMP_TRIGGER_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - val32 &= CLASS_CRASHDUMP_TRIGGER_SHADOWED_MASK; - regs->classes[CLASS_SLOT(reg)].crashdump_trigger_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE0_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase0_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE1_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase1_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE2_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase2_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_PHASE3_CYC_SHADOWED, ALERT_CLASSD): - if (CHECK_REGWEN(reg, regs->classes[CLASS_SLOT(reg)].regwen)) { - regs->classes[CLASS_SLOT(reg)].phase3_cyc_shadowed = val32; - } - break; - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ACCUM_CNT, ALERT_CLASSD): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_ESC_CNT, ALERT_CLASSD): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSA): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSB): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSC): - case CASE_STRIDE(R_CLASS_STATE, ALERT_CLASSD): - qemu_log_mask(LOG_GUEST_ERROR, - "%s: R/O register 0x%03" HWADDR_PRIx "\n", __func__, - addr); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", - __func__, addr); - break; - } -}; - -static Property ot_alert_eg_properties[] = { - DEFINE_PROP_LINK("edn", OtAlertEgState, edn, TYPE_OT_EDN, OtEDNState *), - DEFINE_PROP_UINT8("edn-ep", OtAlertEgState, edn_ep, UINT8_MAX), - DEFINE_PROP_END_OF_LIST(), -}; - -static const MemoryRegionOps ot_alert_eg_regs_ops = { - .read = &ot_alert_eg_regs_read, - .write = &ot_alert_eg_regs_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 4u, - .impl.max_access_size = 4u, -}; - -static void ot_alert_eg_reset(DeviceState *dev) -{ - OtAlertEgState *s = OT_ALERT_EG(dev); - - OtAlertRegs *regs = s->regs; - memset(regs, 0, sizeof(*regs)); - - regs->ping.timer_regwen = 0x1u; - regs->ping.timeout_cyc_shadowed = 0x100u; - for (unsigned ix = 0; ix < PARAM_N_ALERTS; ix++) { - regs->alerts[ix].regwen = 0x1u; - } - for (unsigned ix = 0; ix < PARAM_N_LOC_ALERT; ix++) { - regs->loc_alerts[ix].regwen = 0x1u; - } - for (unsigned ix = 0; ix < PARAM_N_CLASSES; ix++) { - regs->classes[ix].regwen = 0x1u; - regs->classes[ix].ctrl_shadowed = 0x393cu; - regs->classes[ix].clr_regwen = 0x1u; - } - - ot_alert_eg_update_irqs(s); -} - -static void ot_alert_eg_init(Object *obj) -{ - OtAlertEgState *s = OT_ALERT_EG(obj); - - memory_region_init_io(&s->mmio, obj, &ot_alert_eg_regs_ops, s, - TYPE_OT_ALERT_EG, REGS_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); - - s->regs = g_new0(OtAlertRegs, 1); - - for (unsigned ix = 0; ix < ARRAY_SIZE(s->irqs); ix++) { - ibex_sysbus_init_irq(obj, &s->irqs[ix]); - } -} - -static void ot_alert_eg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - (void)data; - - dc->reset = &ot_alert_eg_reset; - device_class_set_props(dc, ot_alert_eg_properties); - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo ot_alert_eg_info = { - .name = TYPE_OT_ALERT_EG, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OtAlertEgState), - .instance_init = &ot_alert_eg_init, - .class_size = sizeof(OtAlertStateClass), - .class_init = &ot_alert_eg_class_init, -}; - -static void ot_alert_eg_register_types(void) -{ - type_register_static(&ot_alert_eg_info); -} - -type_init(ot_alert_eg_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index bbd36465657d5..313cdfa9bbd33 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -16,8 +16,20 @@ ot_aes_schedule(void) "" # ot_alert.c -ot_alert_io_read_out(uint32_t addr, uint32_t val, uint32_t pc) "addr=0x%02x, val=0x%x, pc=0x%x" -ot_alert_io_write(uint32_t addr, uint32_t val, uint32_t pc) "addr=0x%02x, val=0x%x, pc=0x%x" +ot_alert_cancel_timeout(const char *id, char cls) "%s: class %c" +ot_alert_error(const char *id, char cls, const char *msg) "%s: class %c: %s" +ot_alert_esc_count(const char *id, char cls, const char *st, uint32_t cnt) "%s: class %c [%s] %u cycles" +ot_alert_escalation(const char *id, char cls, unsigned esc, const char *msg) "%s: class %c esc %u %s" +ot_alert_fsm_update(const char *id, char cls, const char *st, bool timer, bool atrig) "%s: class %c [%s] timer %u atrig %u" +ot_alert_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%01x msk:0x%01x eff:0x%01x" +ot_alert_timer_expire(const char *id, char cls) "%s: class %c" +ot_alert_io_read_out(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%03x (%s), val=0x%x, pc=0x%x" +ot_alert_io_write(const char *id, uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "%s: addr=0x%03x (%s), val=0x%x, pc=0x%x" +ot_alert_set_class_state(const char *id, char cls, const char *ost, const char *nst) "%s: class %c [%s] -> [%s]" +ot_alert_set_class_timer(const char *id, char cls, const char *st, uint64_t us, uint32_t cycles) "%s: class %c [%s] %" PRIu64 " us (%u cycles)" +ot_alert_signal_class(const char *id, unsigned alert, char cls, bool class_en) "%s: alert:%u -> class %c enabled:%u" +ot_alert_signal_tx(const char *id, unsigned n, bool level, bool alert_en) "%s: alert:%u level:%u enabled:%u" +ot_alert_skip_active(const char *id, char cls, const char *stname) "%s: class %c %s" # ot_aon_timer.c diff --git a/include/hw/opentitan/ot_alert.h b/include/hw/opentitan/ot_alert.h index e126afc9562d4..8f93ed4c88194 100644 --- a/include/hw/opentitan/ot_alert.h +++ b/include/hw/opentitan/ot_alert.h @@ -35,14 +35,7 @@ #define TYPE_OT_ALERT "ot-alert" OBJECT_DECLARE_TYPE(OtAlertState, OtAlertStateClass, OT_ALERT) -#define OT_DEVICE_ALERT TYPE_OT_ALERT "-sig" - -struct OtAlertState { - SysBusDevice parent_obj; -}; - -struct OtAlertStateClass { - SysBusDeviceClass parent_class; -}; +#define OT_DEVICE_ALERT TYPE_OT_ALERT "-sig" +#define OT_ALERT_ESCALATE TYPE_OT_ALERT "-esc" #endif /* HW_OPENTITAN_OT_ALERT_H */ diff --git a/include/hw/opentitan/ot_alert_dj.h b/include/hw/opentitan/ot_alert_dj.h deleted file mode 100644 index d549d89bcc594..0000000000000 --- a/include/hw/opentitan/ot_alert_dj.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * QEMU OpenTitan Darjeeling Alert handler device - * - * Copyright (c) 2022-2024 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_ALERT_DJ_H -#define HW_OPENTITAN_OT_ALERT_DJ_H - -#include "qom/object.h" -#include "hw/opentitan/ot_alert.h" - -#define TYPE_OT_ALERT_DJ "ot-alert-djg" -OBJECT_DECLARE_TYPE(OtAlertDjState, OtAlertStateClass, OT_ALERT_DJ) - -#endif /* HW_OPENTITAN_OT_ALERT_DJ_H */ diff --git a/include/hw/opentitan/ot_alert_eg.h b/include/hw/opentitan/ot_alert_eg.h deleted file mode 100644 index b69f74db1b372..0000000000000 --- a/include/hw/opentitan/ot_alert_eg.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * QEMU OpenTitan EarlGrey Alert handler device - * - * Copyright (c) 2022-2024 Rivos, Inc. - * - * 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_ALERT_EG_H -#define HW_OPENTITAN_OT_ALERT_EG_H - -#include "qom/object.h" -#include "hw/opentitan/ot_alert.h" - -#define TYPE_OT_ALERT_EG "ot-alert-eg" -OBJECT_DECLARE_TYPE(OtAlertEgState, OtAlertStateClass, OT_ALERT_EG) - -#endif /* HW_OPENTITAN_OT_ALERT_EG_H */ From ea82cbe00c27b81a1fad17a70420902754f95489 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 May 2024 18:33:21 +0200 Subject: [PATCH 16/65] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: use updated ot_alert device Signed-off-by: Emmanuel Blot --- hw/riscv/Kconfig | 2 ++ hw/riscv/ot_darjeeling.c | 9 ++++++--- hw/riscv/ot_earlgrey.c | 9 ++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index a38d720f3ed9d..d4e8198229eaa 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,6 +15,7 @@ config OT_DARJEELING select IBEX_COMMON select OT_ADDRESS_SPACE select OT_AES + select OT_ALERT select OT_AON_TIMER select OT_AST_DJ select OT_CLKMGR @@ -55,6 +56,7 @@ config OT_EARLGREY select IBEX select IBEX_COMMON select OT_AES + select OT_ALERT select OT_AON_TIMER select OT_AST_EG select OT_CLKMGR diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 60ecf4e12d054..d4b6a46745365 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -37,6 +37,7 @@ #include "hw/misc/unimp.h" #include "hw/opentitan/ot_address_space.h" #include "hw/opentitan/ot_aes.h" +#include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_aon_timer.h" #include "hw/opentitan/ot_ast_dj.h" #include "hw/opentitan/ot_clkmgr.h" @@ -1042,9 +1043,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("kmac-app", 1u) ) }, -#if 0 [OT_DJ_SOC_DEV_ALERT_HANDLER] = { - .type = TYPE_OT_ALERT_DJ, + .type = TYPE_OT_ALERT, .memmap = MEMMAPENTRIES( { 0x30150000u, 0x800u } ), @@ -1058,10 +1058,13 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_DEVLINK("edn", EDN0) ), .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ), + IBEX_DEV_UINT_PROP("n_alerts", 99u), + IBEX_DEV_UINT_PROP("n_classes", 4u), + IBEX_DEV_UINT_PROP("n_lpg", 18u), IBEX_DEV_UINT_PROP("edn-ep", 4u) ), }, -#endif [OT_DJ_SOC_DEV_SPI_HOST0] = { .type = TYPE_OT_SPI_HOST, .instance = 0, diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 3b99007b4b831..274b6ad384d6d 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -34,6 +34,7 @@ #include "hw/misc/pulp_rv_dm.h" #include "hw/misc/unimp.h" #include "hw/opentitan/ot_aes.h" +#include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_aon_timer.h" #include "hw/opentitan/ot_ast_eg.h" #include "hw/opentitan/ot_clkmgr.h" @@ -535,9 +536,8 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_UINT_PROP("kmac-app", 1u) ) }, -#if 0 [OT_EG_SOC_DEV_ALERT_HANDLER] = { - .type = TYPE_OT_ALERT_EG, + .type = TYPE_OT_ALERT, .memmap = MEMMAPENTRIES( { 0x40150000u, 0x800u } ), @@ -551,10 +551,13 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_DEVLINK("edn", EDN0) ), .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("pclk", OT_EG_PERIPHERAL_CLK_HZ), + IBEX_DEV_UINT_PROP("n_alerts", 65u), + IBEX_DEV_UINT_PROP("n_classes", 4u), + IBEX_DEV_UINT_PROP("n_lpg", 22u), IBEX_DEV_UINT_PROP("edn-ep", 4u) ), }, -#endif [OT_EG_SOC_DEV_SPI_HOST0] = { .type = TYPE_OT_SPI_HOST, .instance = 0, From fe046fe8cfcfaba043bb80bed976e55d0781b4bc Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 26 Apr 2024 18:06:35 +0200 Subject: [PATCH 17/65] [ot] hw/opentitan: ot_uart: add alert support Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_uart.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_uart.c b/hw/opentitan/ot_uart.c index f73ad34eadb03..6b0118ecbe334 100644 --- a/hw/opentitan/ot_uart.c +++ b/hw/opentitan/ot_uart.c @@ -34,6 +34,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "chardev/char-fe.h" +#include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_uart.h" #include "hw/qdev-properties-system.h" #include "hw/qdev-properties.h" @@ -143,20 +144,19 @@ static const char *REG_NAMES[REGS_COUNT] = { struct OtUARTState { SysBusDevice parent_obj; - MemoryRegion mmio; + IbexIRQ irqs[OT_UART_IRQ_NUM]; + IbexIRQ alert; + + uint32_t regs[REGS_COUNT]; Fifo8 tx_fifo; Fifo8 rx_fifo; uint32_t tx_watermark_level; + guint watch_tag; - uint32_t regs[REGS_COUNT]; uint32_t pclk; - CharBackend chr; - guint watch_tag; - - IbexIRQ irqs[OT_UART_IRQ_NUM]; }; static uint32_t ot_uart_get_tx_watermark_level(OtUARTState *s) @@ -480,6 +480,11 @@ static void ot_uart_write(void *opaque, hwaddr addr, uint64_t val64, s->regs[R_INTR_STATE] |= val32; ot_uart_update_irqs(s); break; + case R_ALERT_TEST: + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + s->regs[reg] = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); + break; case R_CTRL: if (val32 & ~CTRL_SUP_MASK) { qemu_log_mask(LOG_UNIMP, @@ -596,6 +601,7 @@ static void ot_uart_reset(DeviceState *dev) ot_uart_reset_rx_fifo(s); ot_uart_update_irqs(s); + ibex_irq_set(&s->alert, 0); } static void ot_uart_init(Object *obj) @@ -605,6 +611,7 @@ static void ot_uart_init(Object *obj) for (unsigned index = 0; index < OT_UART_IRQ_NUM; index++) { ibex_sysbus_init_irq(obj, &s->irqs[index]); } + ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); memory_region_init_io(&s->mmio, obj, &ot_uart_ops, s, TYPE_OT_UART, REGS_SIZE); From 53eebd507e8607eb87a1ec811bec5928e76fe8cd Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 May 2024 18:02:38 +0200 Subject: [PATCH 18/65] [ot] hw/opentitan: ot_plic_ext: add OT PLIC extension implementation Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 3 + hw/opentitan/meson.build | 1 + hw/opentitan/ot_plic_ext.c | 209 +++++++++++++++++++++++++++++ hw/opentitan/trace-events | 5 + include/hw/opentitan/ot_plic_ext.h | 36 +++++ 5 files changed, 254 insertions(+) create mode 100644 hw/opentitan/ot_plic_ext.c create mode 100644 include/hw/opentitan/ot_plic_ext.h diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 94d0dba3c695a..064063a62b3f9 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -105,6 +105,9 @@ config OT_PINMUX_DJ config OT_PINMUX_EG bool +config OT_PLIC_EXT + bool + config OT_PRESENT bool diff --git a/hw/opentitan/meson.build b/hw/opentitan/meson.build index 373f17cb0d9f2..06009ccfa5fd5 100644 --- a/hw/opentitan/meson.build +++ b/hw/opentitan/meson.build @@ -35,6 +35,7 @@ system_ss.add(when: 'CONFIG_OT_OTP_DJ', if_true: files('ot_otp_dj.c')) system_ss.add(when: 'CONFIG_OT_OTP_EG', if_true: files('ot_otp_eg.c')) system_ss.add(when: 'CONFIG_OT_PINMUX_EG', if_true: files('ot_pinmux_eg.c')) system_ss.add(when: 'CONFIG_OT_PINMUX_DJ', if_true: files('ot_pinmux_dj.c')) +system_ss.add(when: 'CONFIG_OT_PLIC_EXT', if_true: files('ot_plic_ext.c')) system_ss.add(when: 'CONFIG_OT_PRESENT', if_true: files('ot_present.c')) system_ss.add(when: 'CONFIG_OT_PRNG', if_true: files('ot_prng.c')) system_ss.add(when: 'CONFIG_OT_PWRMGR', if_true: files('ot_pwrmgr.c')) diff --git a/hw/opentitan/ot_plic_ext.c b/hw/opentitan/ot_plic_ext.c new file mode 100644 index 0000000000000..9f714c5c55c12 --- /dev/null +++ b/hw/opentitan/ot_plic_ext.c @@ -0,0 +1,209 @@ +/* + * QEMU OpenTitan PLIC extension + * + * Copyright (c) 2024 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 "qapi/error.h" +#include "exec/memory.h" +#include "hw/opentitan/ot_alert.h" +#include "hw/opentitan/ot_common.h" +#include "hw/opentitan/ot_plic_ext.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" +#include "hw/riscv/ibex_common.h" +#include "hw/riscv/ibex_irq.h" +#include "trace.h" + +/* clang-format off */ +REG32(MSIP0, 0x0u) + FIELD(MSIP0, EN, 0u, 1u) +REG32(ALERT_TEST, 0x4u) + FIELD(ALERT_TEST, FATAL_FAULT, 0u, 1u) +/* clang-format on */ + +#define R32_OFF(_r_) ((_r_) / sizeof(uint32_t)) + +#define R_LAST_REG (R_ALERT_TEST) +#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_] : "?") + +struct OtPlicExtState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + IbexIRQ irq; + IbexIRQ alert; + + uint32_t regs[REGS_COUNT]; + + char *ot_id; +}; + +#define REG_NAME_ENTRY(_reg_) [R_##_reg_] = stringify(_reg_) +static const char *REG_NAMES[REGS_COUNT] = { + REG_NAME_ENTRY(MSIP0), + REG_NAME_ENTRY(ALERT_TEST), +}; +#undef REG_NAME_ENTRY + +static uint64_t ot_plic_ext_regs_read(void *opaque, hwaddr addr, unsigned size) +{ + OtPlicExtState *s = opaque; + (void)size; + uint32_t val32; + + hwaddr reg = R32_OFF(addr); + + switch (reg) { + case R_MSIP0: + val32 = s->regs[reg]; + break; + case R_ALERT_TEST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: W/O register 0x%02" HWADDR_PRIx " (%s)\n", + __func__, s->ot_id, addr, REG_NAME(reg)); + val32 = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); + val32 = 0; + break; + } + + uint32_t pc = ibex_get_current_pc(); + trace_ot_plic_ext_io_read_out(s->ot_id, (uint32_t)addr, REG_NAME(reg), + val32, pc); + + return (uint64_t)val32; +} + +static void ot_plic_ext_regs_write(void *opaque, hwaddr addr, uint64_t val64, + unsigned size) +{ + OtPlicExtState *s = opaque; + uint32_t val32 = (uint32_t)val64; + (void)size; + + hwaddr reg = R32_OFF(addr); + + uint32_t pc = ibex_get_current_pc(); + trace_ot_plic_ext_io_write(s->ot_id, (uint32_t)addr, REG_NAME(reg), val32, + pc); + + switch (reg) { + case R_MSIP0: + val32 &= R_MSIP0_EN_MASK; + s->regs[reg] = val32; + ibex_irq_set(&s->irq, (int)(bool)val32); + break; + case R_ALERT_TEST: + val32 &= R_ALERT_TEST_FATAL_FAULT_MASK; + s->regs[reg] = val32; + ibex_irq_set(&s->alert, (int)(bool)val32); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, + s->ot_id, addr); + break; + } +} + +static Property ot_plic_ext_properties[] = { + DEFINE_PROP_STRING("ot_id", OtPlicExtState, ot_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static const MemoryRegionOps ot_plic_ext_regs_ops = { + .read = &ot_plic_ext_regs_read, + .write = &ot_plic_ext_regs_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl.min_access_size = 4u, + .impl.max_access_size = 4u, +}; + +static void ot_plic_ext_reset(DeviceState *dev) +{ + OtPlicExtState *s = OT_PLIC_EXT(dev); + + ibex_irq_set(&s->irq, 0); + ibex_irq_set(&s->alert, 0); +} + +static void ot_plic_ext_realize(DeviceState *dev, Error **errp) +{ + (void)errp; + + OtPlicExtState *s = OT_PLIC_EXT(dev); + + if (!s->ot_id) { + s->ot_id = + g_strdup(object_get_canonical_path_component(OBJECT(s)->parent)); + } +} + +static void ot_plic_ext_init(Object *obj) +{ + OtPlicExtState *s = OT_PLIC_EXT(obj); + + memory_region_init_io(&s->mmio, obj, &ot_plic_ext_regs_ops, s, + TYPE_OT_PLIC_EXT, REGS_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); + + ibex_qdev_init_irq(obj, &s->irq, NULL); + ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); +} + +static void ot_plic_ext_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->reset = &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); +} + +static const TypeInfo ot_plic_ext_info = { + .name = TYPE_OT_PLIC_EXT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OtPlicExtState), + .instance_init = &ot_plic_ext_init, + .class_init = &ot_plic_ext_class_init, +}; + +static void ot_plic_ext_register_types(void) +{ + type_register_static(&ot_plic_ext_info); +} + +type_init(ot_plic_ext_register_types); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 313cdfa9bbd33..b8732a2776c86 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -307,6 +307,11 @@ ot_otp_skip_digest(const char* part, unsigned pix) "skipping empty digest on %s ot_pinmux_io_read_out(uint32_t addr, uint32_t val, uint32_t pc) "addr=0x%02x, val=0x%x, pc=0x%x" ot_pinmux_io_write(uint32_t addr, uint32_t val, uint32_t pc) "addr=0x%02x, val=0x%x, pc=0x%x" +# ot_plic_ext.c + +ot_plic_ext_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_plic_ext_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_pwrmgr.c ot_pwrmgr_change_state(const char *id, int line, const char *type, const char *old, int nold, const char *new, int nnew) "%s @ %d %s: [%s:%d] -> [%s:%d]" diff --git a/include/hw/opentitan/ot_plic_ext.h b/include/hw/opentitan/ot_plic_ext.h new file mode 100644 index 0000000000000..e766e952a3f20 --- /dev/null +++ b/include/hw/opentitan/ot_plic_ext.h @@ -0,0 +1,36 @@ +/* + * QEMU OpenTitan PLIC extension + * + * Copyright (c) 2024 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_PLIC_EXT_H +#define HW_OPENTITAN_OT_PLIC_EXT_H + +#include "qom/object.h" + +#define TYPE_OT_PLIC_EXT "ot-plic-ext" +OBJECT_DECLARE_SIMPLE_TYPE(OtPlicExtState, OT_PLIC_EXT) + +#endif /* HW_OPENTITAN_OT_PLIC_EXT_H */ From 5c0cc47cf85ef5255b7cd1c08c8b1254f78f35e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 May 2024 18:03:05 +0200 Subject: [PATCH 19/65] [ot] hw/opentitan: Kconfig: add missing dependency for SRAM controller Signed-off-by: Emmanuel Blot --- hw/opentitan/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/opentitan/Kconfig b/hw/opentitan/Kconfig index 064063a62b3f9..d59f31731a082 100644 --- a/hw/opentitan/Kconfig +++ b/hw/opentitan/Kconfig @@ -141,6 +141,7 @@ config OT_SPI_HOST select SSI config OT_SRAM_CTRL + select OT_PRESENT bool config OT_TIMER From d816665c6abe17c71094cc0450e41aa4ed01450a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 26 Apr 2024 18:15:52 +0200 Subject: [PATCH 20/65] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: add OT PLIC extension Signed-off-by: Emmanuel Blot --- hw/riscv/Kconfig | 2 ++ hw/riscv/ot_darjeeling.c | 15 +++++++++++++-- hw/riscv/ot_earlgrey.c | 15 +++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index d4e8198229eaa..c17a363f0c7d8 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -35,6 +35,7 @@ config OT_DARJEELING select OT_OTBN select OT_OTP_DJ select OT_PINMUX_DJ + select OT_PLIC_EXT select OT_PWRMGR select OT_RSTMGR select OT_ROM_CTRL @@ -72,6 +73,7 @@ config OT_EARLGREY select OT_OTBN select OT_OTP_EG select OT_PINMUX_EG + select OT_PLIC_EXT select OT_PWRMGR select OT_ROM_CTRL select OT_RSTMGR diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index d4b6a46745365..31bbd37e4b063 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -58,6 +58,7 @@ #include "hw/opentitan/ot_otbn.h" #include "hw/opentitan/ot_otp_dj.h" #include "hw/opentitan/ot_pinmux_dj.h" +#include "hw/opentitan/ot_plic_ext.h" #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/opentitan/ot_rstmgr.h" @@ -137,6 +138,7 @@ enum OtDjSocDevice { OT_DJ_SOC_DEV_OTP_CTRL, OT_DJ_SOC_DEV_PINMUX, OT_DJ_SOC_DEV_PLIC, + OT_DJ_SOC_DEV_PLIC_EXT, OT_DJ_SOC_DEV_PWRMGR, OT_DJ_SOC_DEV_ROM0, OT_DJ_SOC_DEV_ROM1, @@ -852,7 +854,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { [OT_DJ_SOC_DEV_PLIC] = { .type = TYPE_SIFIVE_PLIC, .memmap = MEMMAPENTRIES( - { 0x28000000u, 0x8000000u } + { 0x28000000u, 0x4000000u } ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO(1, HART, IRQ_M_EXT) @@ -869,7 +871,16 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_UINT_PROP("enable-stride", 32u), IBEX_DEV_UINT_PROP("context-base", 0x200000u), IBEX_DEV_UINT_PROP("context-stride", 8u), - IBEX_DEV_UINT_PROP("aperture-size", 0x8000000u) + IBEX_DEV_UINT_PROP("aperture-size", 0x4000000u) + ), + }, + [OT_DJ_SOC_DEV_PLIC_EXT] = { + .type = TYPE_OT_PLIC_EXT, + .memmap = MEMMAPENTRIES( + { 0x2c000000u, 0x10u } + ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO(0, HART, IRQ_M_SOFT) ), }, [OT_DJ_SOC_DEV_GPIO] = { diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 274b6ad384d6d..21b16c0717dc4 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -50,6 +50,7 @@ #include "hw/opentitan/ot_otbn.h" #include "hw/opentitan/ot_otp_eg.h" #include "hw/opentitan/ot_pinmux_eg.h" +#include "hw/opentitan/ot_plic_ext.h" #include "hw/opentitan/ot_pwrmgr.h" #include "hw/opentitan/ot_rom_ctrl.h" #include "hw/opentitan/ot_rstmgr.h" @@ -116,6 +117,7 @@ enum OtEGSocDevice { OT_EG_SOC_DEV_PATTGEN, OT_EG_SOC_DEV_PINMUX, OT_EG_SOC_DEV_PLIC, + OT_EG_SOC_DEV_PLIC_EXT, OT_EG_SOC_DEV_PWM, OT_EG_SOC_DEV_PWRMGR, OT_EG_SOC_DEV_SRAM_RET_CTRL, @@ -926,7 +928,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { [OT_EG_SOC_DEV_PLIC] = { .type = TYPE_SIFIVE_PLIC, .memmap = MEMMAPENTRIES( - { 0x48000000u, 0x8000000u } + { 0x48000000u, 0x4000000u } ), .gpio = IBEXGPIOCONNDEFS( OT_EG_SOC_GPIO(1, HART, IRQ_M_EXT) @@ -943,7 +945,16 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_UINT_PROP("enable-stride", 32u), IBEX_DEV_UINT_PROP("context-base", 0x200000u), IBEX_DEV_UINT_PROP("context-stride", 8u), - IBEX_DEV_UINT_PROP("aperture-size", 0x8000000u) + IBEX_DEV_UINT_PROP("aperture-size", 0x4000000u) + ), + }, + [OT_EG_SOC_DEV_PLIC_EXT] = { + .type = TYPE_OT_PLIC_EXT, + .memmap = MEMMAPENTRIES( + { 0x2c000000u, 0x10u } + ), + .gpio = IBEXGPIOCONNDEFS( + OT_EG_SOC_GPIO(0, HART, IRQ_M_SOFT) ), }, /* clang-format on */ From 0ecea2baa34e2ecb41e9c69138addc39e66bb95e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 26 Apr 2024 20:01:09 +0200 Subject: [PATCH 21/65] [ot] hw/opentitan: ot_ibex_wrapper: add support for alert escalation Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_ibex_wrapper_dj.c | 58 +++++++++++++++++++++++-------- hw/opentitan/ot_ibex_wrapper_eg.c | 58 +++++++++++++++++++++++-------- hw/opentitan/trace-events | 4 ++- 3 files changed, 91 insertions(+), 29 deletions(-) diff --git a/hw/opentitan/ot_ibex_wrapper_dj.c b/hw/opentitan/ot_ibex_wrapper_dj.c index 6b821881da3d8..f528c419bc886 100644 --- a/hw/opentitan/ot_ibex_wrapper_dj.c +++ b/hw/opentitan/ot_ibex_wrapper_dj.c @@ -710,6 +710,7 @@ struct OtIbexWrapperDjState { OtIbexTestLogEngine *log_engine; CPUState *cpu; uint8_t cpu_en_bm; + bool esc_rx; bool entropy_requested; bool edn_connected; @@ -1261,25 +1262,17 @@ ot_ibex_wrapper_dj_log_handle(OtIbexWrapperDjState *s, uint32_t value) } } -static void ot_ibex_wrapper_dj_cpu_enable_recv(void *opaque, int n, int level) +static void ot_ibex_wrapper_dj_update_exec(OtIbexWrapperDjState *s) { - 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." */ - bool enable = ((s->cpu_en_bm & OT_IBEX_CPU_EN_MASK) == OT_IBEX_CPU_EN_MASK); - trace_ot_ibex_wrapper_cpu_enable(s->ot_id ?: "", n ? "PWR" : "LC", - (bool)level, s->cpu_en_bm, enable); + 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; @@ -1295,6 +1288,41 @@ static void ot_ibex_wrapper_dj_cpu_enable_recv(void *opaque, int n, int level) } } +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) { @@ -1554,6 +1582,8 @@ static void ot_ibex_wrapper_dj_init(Object *obj) 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); diff --git a/hw/opentitan/ot_ibex_wrapper_eg.c b/hw/opentitan/ot_ibex_wrapper_eg.c index 76a444dd25d42..275d32e7f28a3 100644 --- a/hw/opentitan/ot_ibex_wrapper_eg.c +++ b/hw/opentitan/ot_ibex_wrapper_eg.c @@ -232,6 +232,7 @@ struct OtIbexWrapperEgState { uint8_t cpu_en_bm; bool entropy_requested; bool edn_connected; + bool esc_rx; char *ot_id; OtEDNState *edn; @@ -720,25 +721,17 @@ ot_ibex_wrapper_eg_log_handle(OtIbexWrapperEgState *s, uint32_t value) } } -static void ot_ibex_wrapper_eg_cpu_enable_recv(void *opaque, int n, int level) +static void ot_ibex_wrapper_eg_update_exec(OtIbexWrapperEgState *s) { - 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." */ - bool enable = ((s->cpu_en_bm & OT_IBEX_CPU_EN_MASK) == OT_IBEX_CPU_EN_MASK); - trace_ot_ibex_wrapper_cpu_enable(s->ot_id ?: "", n ? "PWR" : "LC", - (bool)level, s->cpu_en_bm, enable); + 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; @@ -754,6 +747,41 @@ static void ot_ibex_wrapper_eg_cpu_enable_recv(void *opaque, int n, int level) } } +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) { @@ -988,6 +1016,8 @@ static void ot_ibex_wrapper_eg_init(Object *obj) 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); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index b8732a2776c86..8c84959177a30 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -207,8 +207,9 @@ ot_i2c_update_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) # ot_ibex_wrapper.c -ot_ibex_wrapper_cpu_enable(const char *id, const char *ch, bool level, uint32_t bm, bool cpu_en) "%s: %s:%u (0x%x)-> CPU enable %u" +ot_ibex_wrapper_cpu_enable(const char *id, const char *ch, bool level) "%s: %s:%u" ot_ibex_wrapper_error(const char *id, const char *func, int line, const char *msg) "%s: %s:%d %s" +ot_ibex_wrapper_escalate_rx(const char *id, bool level) "%s: %u" ot_ibex_wrapper_exit(const char *id, const char *msg, int val) "%s: %s (%d)" ot_ibex_wrapper_fill_entropy(const char *id, uint32_t bits, bool fips) "%s: 0x%08x fips:%u" ot_ibex_wrapper_info(const char *id, const char *func, int line, const char *msg) "%s: %s:%d %s" @@ -218,6 +219,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) "%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_kmac.c From e0657465f50621343857659e4c8d6c4be35d88ea Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 29 Apr 2024 11:28:55 +0200 Subject: [PATCH 22/65] [ot] hw/opentitan: ot_lc_ctrl: add support for alert escalation. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_lc_ctrl.c | 26 ++++++++++++++++++++++++++ hw/opentitan/trace-events | 1 + 2 files changed, 27 insertions(+) diff --git a/hw/opentitan/ot_lc_ctrl.c b/hw/opentitan/ot_lc_ctrl.c index 4ab52abae17d9..32abf6481c44d 100644 --- a/hw/opentitan/ot_lc_ctrl.c +++ b/hw/opentitan/ot_lc_ctrl.c @@ -275,6 +275,7 @@ struct OtLcCtrlState { MemoryRegion mmio; MemoryRegion dmi_mmio; QEMUBH *pwc_lc_bh; + QEMUBH *escalate_bh; IbexIRQ alerts[NUM_ALERTS]; IbexIRQ broadcasts[OT_LC_BROADCAST_COUNT]; IbexIRQ pwc_lc_rsp; @@ -1233,6 +1234,28 @@ static void ot_lc_ctrl_pwr_lc_req(void *opaque, int n, int level) } } +static void ot_lc_ctrl_escalate_rx(void *opaque, int n, int level) +{ + OtLcCtrlState *s = opaque; + + g_assert((unsigned)n < 2u); + + trace_ot_lc_ctrl_escalate_rx((unsigned)n, (bool)level); + + if (level) { + qemu_bh_schedule(s->escalate_bh); + } +} + +static void ot_lc_ctrl_escalate_bh(void *opaque) +{ + OtLcCtrlState *s = opaque; + + LC_FSM_CHANGE_STATE(s, ST_ESCALATE); + + ot_lc_ctrl_update_broadcast(s); +} + static void ot_lc_ctrl_pwr_lc_bh(void *opaque) { OtLcCtrlState *s = opaque; @@ -1671,8 +1694,11 @@ static void ot_lc_ctrl_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), &ot_lc_ctrl_pwr_lc_req, OT_PWRMGR_LC_REQ, 1); + qdev_init_gpio_in_named(DEVICE(obj), &ot_lc_ctrl_escalate_rx, + OT_ALERT_ESCALATE, 2); s->pwc_lc_bh = qemu_bh_new(&ot_lc_ctrl_pwr_lc_bh, s); + s->escalate_bh = qemu_bh_new(&ot_lc_ctrl_escalate_bh, s); } static void ot_lc_ctrl_class_init(ObjectClass *klass, void *data) diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 8c84959177a30..4651464a226c7 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -240,6 +240,7 @@ ot_kmac_state_read_out(uint32_t addr, uint32_t val, uint32_t pc) "addr=0x%03x, v ot_lc_ctrl_change_state(int line, const char *old, int nold, const char *new, int nnew) "@ %d [%s:%d] -> [%s:%d]" ot_lc_ctrl_error(const char *msg) "%s" ot_lc_ctrl_escalate(const char *fsm, const char *lc) "%s: %s" +ot_lc_ctrl_escalate_rx(unsigned line, bool level) "%u: %u" ot_lc_ctrl_info(const char *msg) "%s" ot_lc_ctrl_initialize(const char *cst, int cstix, unsigned tcount, const char *state, int six) "%s (%d), tcount %u, state %s (%u)" ot_lc_ctrl_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" From aeb61e883ce9f4ad6410ba3d7edb0547326b36c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 29 Apr 2024 11:29:07 +0200 Subject: [PATCH 23/65] [ot] hw/opentitan: ot_pwrmgr: add support for alert escalation. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_pwrmgr.c | 36 +++++++++++++++++++++++++++++++++++- hw/opentitan/trace-events | 2 ++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index eed1f4f056b76..b07ed72b43d82 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -40,6 +40,7 @@ #include "hw/riscv/ibex_common.h" #include "hw/riscv/ibex_irq.h" #include "hw/sysbus.h" +#include "sysemu/runstate.h" #include "trace.h" #define PARAM_NUM_RST_REQS 2u @@ -115,6 +116,9 @@ REG32(FAULT_STATUS, 0x40u) #define PWRMGR_WAKEUP_MAX 6u #define PWRMGR_RESET_MAX 3u +/* special exit error code to report escalation panic */ +#define EXIT_ESCALATION_PANIC 39 + /* Verbatim definitions from RTL */ #define NUM_SW_RST_REQ 1u #define HW_RESET_WIDTH \ @@ -218,6 +222,7 @@ typedef union { uint8_t sw_reset : 1; /* SW reset request */ uint8_t otp_done : 1; uint8_t lc_done : 1; + uint8_t escalate : 1; /* escalation from alert handler */ uint8_t holdon_fetch : 1; /* custom extension */ uint8_t rom_good; /* up to 8 ROMs */ uint8_t rom_done; /* up to 8 ROMs */ @@ -251,6 +256,7 @@ struct OtPwrMgrState { OtRstMgrState *rstmgr; uint8_t num_rom; uint8_t version; + bool main; /* main power manager (for machines w/ multiple PwrMgr) */ bool fetch_ctrl; }; @@ -564,10 +570,20 @@ static void ot_pwrmgr_slow_fsm_tick(OtPwrMgrState *s) static void ot_pwrmgr_fast_fsm_tick(OtPwrMgrState *s) { if (s->s_state != OT_PWR_SLOW_ST_IDLE) { - // TODO: to be handled + qemu_log_mask(LOG_UNIMP, "%s: %s: slow FSM not supported\n", __func__, + s->ot_id); return; } + if (s->fsm_events.escalate) { + PWR_CHANGE_FAST_STATE(s, REQ_PWR_DN); + trace_ot_pwrmgr_shutdown(s->ot_id, s->main); + if (s->main) { + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_PANIC, + EXIT_ESCALATION_PANIC); + } + } + switch (s->f_state) { case OT_PWR_FAST_ST_LOW_POWER: PWR_CHANGE_FAST_STATE(s, ENABLE_CLOCKS); @@ -670,6 +686,21 @@ static void ot_pwrmgr_fsm_tick(void *opaque) * Input lines */ +static void ot_pwrmgr_escalate_rx(void *opaque, int n, int level) +{ + OtPwrMgrState *s = opaque; + + g_assert(n == 0); + + trace_ot_pwrmgr_escalate_rx(s->ot_id, (bool)level); + + if (level) { + s->regs[R_ESCALATE_RESET_STATUS] |= R_ESCALATE_RESET_STATUS_VAL_MASK; + s->fsm_events.escalate = true; + ot_pwrmgr_schedule_fsm(s); + } +} + static void ot_pwrmgr_pwr_lc_rsp(void *opaque, int n, int level) { OtPwrMgrState *s = opaque; @@ -854,6 +885,7 @@ static Property ot_pwrmgr_properties[] = { 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), + DEFINE_PROP_BOOL("main", OtPwrMgrState, main, true), DEFINE_PROP_LINK("rstmgr", OtPwrMgrState, rstmgr, TYPE_OT_RSTMGR, OtRstMgrState *), DEFINE_PROP_END_OF_LIST(), @@ -972,6 +1004,8 @@ static void ot_pwrmgr_init(Object *obj) OT_PWRMGR_LC_RSP, 1); qdev_init_gpio_in_named(DEVICE(obj), &ot_pwrmgr_pwr_otp_rsp, OT_PWRMGR_OTP_RSP, 1); + qdev_init_gpio_in_named(DEVICE(obj), &ot_pwrmgr_escalate_rx, + OT_ALERT_ESCALATE, 1); s->fsm_tick_bh = qemu_bh_new(&ot_pwrmgr_fsm_tick, s); } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 4651464a226c7..9f1f9f3f95afa 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -318,6 +318,7 @@ ot_plic_ext_io_write(const char *id, uint32_t addr, const char *regname, uint32_ # ot_pwrmgr.c ot_pwrmgr_change_state(const char *id, int line, const char *type, const char *old, int nold, const char *new, int nnew) "%s @ %d %s: [%s:%d] -> [%s:%d]" +ot_pwrmgr_escalate_rx(const char *id, bool level) "%s: level %u" ot_pwrmgr_go_idle(const char *id, const char *state) "%s: %s" ot_pwrmgr_ignore_req(const char *reason) "%s" ot_pwrmgr_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" @@ -327,6 +328,7 @@ ot_pwrmgr_reset_req(const char *id, const char *name, unsigned val) "%s: %s: %u" ot_pwrmgr_rom(const char *id, int n, const char *what, bool val) "%s rom #%u %s=%u" ot_pwrmgr_rst_req(const char *id, const char *name, unsigned src) "%s %s(%u)" ot_pwrmgr_schedule_fsm(const char *id, const char *func, int line) "%s @ %s:%d" +ot_pwrmgr_shutdown(const char *id, bool main) "%s: main %u" ot_pwrmgr_sw_rst_req(const char *id, unsigned src, bool active) "%s: %u: %u" ot_pwrmgr_wkup(const char *id, const char *name, unsigned src, bool active) "%s: %s(%u): %u" From 87d3038e5a5d2c7257ecc816c06bc0ea2f6ee6bb Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 May 2024 14:45:43 +0200 Subject: [PATCH 24/65] [ot] hw/opentitan: ot_i2c: add missing alert IRQ line initialization. Also move instantiation calls instance_init. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_i2c_dj.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/hw/opentitan/ot_i2c_dj.c b/hw/opentitan/ot_i2c_dj.c index d18b05cb27450..4ff6ca296d33c 100644 --- a/hw/opentitan/ot_i2c_dj.c +++ b/hw/opentitan/ot_i2c_dj.c @@ -54,6 +54,7 @@ #include "qemu/fifo8.h" #include "qemu/log.h" #include "hw/i2c/i2c.h" +#include "hw/opentitan/ot_alert.h" #include "hw/opentitan/ot_fifo32.h" #include "hw/opentitan/ot_i2c_dj.h" #include "hw/qdev-clock.h" @@ -927,22 +928,22 @@ static void ot_i2c_dj_target_class_init(ObjectClass *klass, void *data) (void)data; dc->desc = "OpenTitan I2C Target"; - sc->event = ot_i2c_dj_target_event; - sc->send_async = ot_i2c_dj_target_send_async; - sc->recv = ot_i2c_dj_target_recv; + sc->event = &ot_i2c_dj_target_event; + sc->send_async = &ot_i2c_dj_target_send_async; + sc->recv = &ot_i2c_dj_target_recv; } 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_init = &ot_i2c_dj_target_class_init, .class_size = sizeof(I2CSlaveClass), }; static const MemoryRegionOps ot_i2c_dj_ops = { - .read = ot_i2c_dj_read, - .write = ot_i2c_dj_write, + .read = &ot_i2c_dj_read, + .write = &ot_i2c_dj_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, @@ -983,21 +984,28 @@ static void ot_i2c_dj_realize(DeviceState *dev, Error **errp) OtI2CDjState *s = OT_I2C_DJ(dev); (void)errp; + /* TODO: check if the following can be moved to ot_i2c_dj_init */ + s->bus = i2c_init_bus(dev, TYPE_OT_I2C_DJ); + s->target = i2c_slave_create_simple(s->bus, TYPE_OT_I2C_DJ_TARGET, 0xff); +} + +static void ot_i2c_dj_init(Object *obj) +{ + OtI2CDjState *s = OT_I2C_DJ(obj); + for (unsigned index = 0; index < ARRAY_SIZE(s->irqs); index++) { - ibex_sysbus_init_irq(OBJECT(dev), &s->irqs[index]); + ibex_sysbus_init_irq(obj, &s->irqs[index]); } + ibex_qdev_init_irq(obj, &s->alert, OT_DEVICE_ALERT); - memory_region_init_io(&s->mmio, OBJECT(dev), &ot_i2c_dj_ops, s, - TYPE_OT_I2C_DJ, REGS_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); + memory_region_init_io(&s->mmio, obj, &ot_i2c_dj_ops, s, TYPE_OT_I2C_DJ, + REGS_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); fifo8_create(&s->host_tx_fifo, OT_I2C_DJ_FIFO_SIZE); fifo8_create(&s->host_rx_fifo, OT_I2C_DJ_FIFO_SIZE); fifo8_create(&s->target_tx_fifo, OT_I2C_DJ_FIFO_SIZE); ot_fifo32_create(&s->target_rx_fifo, OT_I2C_DJ_FIFO_SIZE); - - s->bus = i2c_init_bus(dev, TYPE_OT_I2C_DJ); - s->target = i2c_slave_create_simple(s->bus, TYPE_OT_I2C_DJ_TARGET, 0xff); } static void ot_i2c_dj_class_init(ObjectClass *klass, void *data) @@ -1017,7 +1025,8 @@ static const TypeInfo ot_i2c_dj_info = { .name = TYPE_OT_I2C_DJ, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OtI2CDjState), - .class_init = ot_i2c_dj_class_init, + .instance_init = &ot_i2c_dj_init, + .class_init = &ot_i2c_dj_class_init, }; static void ot_i2c_dj_register_types(void) From f274e98805b700a154d085136624858c8fbb5bd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 May 2024 14:51:36 +0200 Subject: [PATCH 25/65] [ot] hw/opentitan: ot_dma: update trace messages Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dma.c | 5 +++-- hw/opentitan/trace-events | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index ef8af951f08dc..57f5f0551f443 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -396,7 +396,7 @@ static const char *STATE_NAMES[] = { static void ot_dma_change_state_line(OtDMAState *s, OtDMASM state, int line) { if (s->state != state) { - trace_ot_dma_change_state(line, STATE_NAME(state), state); + trace_ot_dma_change_state(s->ot_id, line, STATE_NAME(state), state); s->state = state; } } @@ -404,7 +404,8 @@ static void ot_dma_change_state_line(OtDMAState *s, OtDMASM state, int line) static void ot_dma_update_irqs(OtDMAState *s) { uint32_t level = s->regs[R_INTR_STATE] & s->regs[R_INTR_ENABLE]; - trace_ot_dma_irqs(s->regs[R_INTR_STATE], s->regs[R_INTR_ENABLE], level); + trace_ot_dma_irqs(s->ot_id, s->regs[R_INTR_STATE], s->regs[R_INTR_ENABLE], + level); for (unsigned ix = 0; ix < PARAM_NUM_IRQS; ix++) { ibex_irq_set(&s->irqs[ix], (int)((level >> ix) & 0x1u)); } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 9f1f9f3f95afa..981592270b208 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -105,17 +105,17 @@ ot_dm_tl_update(uint32_t addr, uint32_t value, const char *msg, uint32_t res) "0 # ot_dma.c -ot_dma_abort(const char *did) "%s" -ot_dma_change_state(int line, const char *state, int stateval) "@%d: %s [%d]" -ot_dma_check_device(const char *did, 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_complete(const char *did, int res) "%s: %d" -ot_dma_io_read_out(const char *did, 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 *did, 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_irqs(uint32_t active, uint32_t mask, uint32_t eff) "act:0x%08x msk:0x%08x eff:0x%08x" -ot_dma_new_op(const char *did, const char *dir, const char *asname, const char *mname, uint64_t addr, uint64_t size) "%s: %s as:%s m:%s a:0x%" PRIx64 " s:0x%" PRIx64 +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_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" +ot_dma_irqs(const char *id, uint32_t active, uint32_t mask, uint32_t eff) "%s: act:0x%01x msk:0x%01x eff:0x%01x" +ot_dma_new_op(const char *id, const char *dir, const char *asname, const char *mname, uint64_t addr, uint64_t size) "%s: %s as:%s m:%s a:0x%" PRIx64 " s:0x%" PRIx64 ot_dma_operation(const char *op, bool init) "%s: %u" -ot_dma_set_error(const char *did, const char *func, int line, unsigned err) "%s: %s:%d err:%u" -ot_dma_transfer(const char *did, const char *dir, const char *asname, uint64_t addr, uint64_t size) "%s: %s as=%s addr=0x%" PRIx64 " size:0x%" PRIx64 +ot_dma_set_error(const char *id, const char *func, int line, unsigned err) "%s: %s:%d err:%u" +ot_dma_transfer(const char *id, const char *dir, const char *asname, uint64_t addr, uint64_t size) "%s: %s as=%s addr=0x%" PRIx64 " size:0x%" PRIx64 # ot_edn.c From 1d03b73683507f8ffb332f894b5fd9e8cb9d9028 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 May 2024 15:36:03 +0200 Subject: [PATCH 26/65] [ot] hw/opentitan: ot_darjeeling: connect device alerts to alert handler Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 183 ++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 42 deletions(-) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 31bbd37e4b063..cc0cbb172a93d 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -341,6 +341,14 @@ static const uint32_t ot_dj_pmp_addrs[] = { #define OT_DJ_SOC_GPIO_SYSBUS_IRQ(_irq_, _target_, _num_) \ IBEX_GPIO_SYSBUS_IRQ(_irq_, OT_DJ_SOC_DEV_##_target_, _num_) +#define OT_DJ_SOC_GPIO_ALERT(_snum_, _tnum_) \ + OT_DJ_SOC_SIGNAL(OT_DEVICE_ALERT, _snum_, ALERT_HANDLER, OT_DEVICE_ALERT, \ + _tnum_) + +#define OT_DJ_SOC_GPIO_ESCALATE(_snum_, _tgt_, _tnum_) \ + OT_DJ_SOC_SIGNAL(OT_ALERT_ESCALATE, _snum_, _tgt_, OT_ALERT_ESCALATE, \ + _tnum_) + #define OT_DJ_SOC_DEVLINK(_pname_, _target_) \ IBEX_DEVLINK(_pname_, OT_DJ_SOC_DEV_##_target_) @@ -391,24 +399,27 @@ static const uint32_t ot_dj_pmp_addrs[] = { #define OT_DJ_SOC_RSP(_rsp_, _tgt_) \ OT_DJ_SOC_SIGNAL(_rsp_##_RSP, 0, _tgt_, _rsp_##_RSP, 0) -#define OT_DJ_SOC_DEV_MBX(_ix_, _addr_, _asname_, _irq_) \ +#define OT_DJ_SOC_DEV_MBX(_ix_, _addr_, _asname_, _irq_, _alert_) \ .type = TYPE_OT_MBX, .instance = (_ix_), \ .memmap = MEMMAPENTRIES({ (_addr_), OT_MBX_HOST_APERTURE }), \ - .gpio = \ - IBEXGPIOCONNDEFS(OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, (_irq_)), \ - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, (_irq_) + 1u), \ - OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, (_irq_) + 2u)), \ + .gpio = IBEXGPIOCONNDEFS(OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, (_irq_)), \ + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, (_irq_) + 1u), \ + 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_)), \ IBEX_DEV_STRING_PROP("ram_as_name", _asname_)) -#define OT_DJ_SOC_DEV_MBX_DUAL(_ix_, _addr_, _asname_, _irq_, _xaddr_) \ +#define OT_DJ_SOC_DEV_MBX_DUAL(_ix_, _addr_, _asname_, _irq_, _alert_, \ + _xaddr_) \ .type = TYPE_OT_MBX, .instance = (_ix_), \ .memmap = MEMMAPENTRIES({ (_addr_), OT_MBX_HOST_APERTURE }, \ { (_xaddr_), OT_MBX_SYS_APERTURE }), \ - .gpio = \ - IBEXGPIOCONNDEFS(OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, (_irq_)), \ - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, (_irq_) + 1u), \ - OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, (_irq_) + 2u)), \ + .gpio = IBEXGPIOCONNDEFS(OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, (_irq_)), \ + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, (_irq_) + 1u), \ + 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_)), \ IBEX_DEV_STRING_PROP("ram_as_name", _asname_)) @@ -435,7 +446,7 @@ static const uint32_t ot_dj_pmp_addrs[] = { OT_DJ_DEBUG_TL_TO_DMI(OT_DJ_DEBUG_MBX_JTAG_ADDR) #define OT_DJ_DEBUG_MBX_JTAG_DMI_SIZE (OT_MBX_SYS_APERTURE / sizeof(uint32_t)) -#define OT_DARJEELING_SOC_DM_CONNECTION(_dst_dev_, _num_) \ +#define OT_DJ_SOC_DM_CONNECTION(_dst_dev_, _num_) \ { \ .out = { \ .name = PULP_RV_DM_ACK_OUT_LINES, \ @@ -546,7 +557,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x21100000u, 0x1000u } ), .gpio = IBEXGPIOCONNDEFS( - OT_DJ_SOC_CLKMGR_HINT(OT_CLKMGR_HINT_AES) + OT_DJ_SOC_CLKMGR_HINT(OT_CLKMGR_HINT_AES), + OT_DJ_SOC_GPIO_ALERT(0, 55), + OT_DJ_SOC_GPIO_ALERT(1, 56) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("edn", EDN0) @@ -564,6 +577,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 115), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 116), OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 117), + OT_DJ_SOC_GPIO_ALERT(0, 57), OT_DJ_SOC_CLKMGR_HINT(OT_CLKMGR_HINT_HMAC) ), }, @@ -575,7 +589,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 118), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 119), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 120) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 120), + OT_DJ_SOC_GPIO_ALERT(0, 58), + OT_DJ_SOC_GPIO_ALERT(1, 59) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("edn", EDN0) @@ -592,6 +608,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 121), + OT_DJ_SOC_GPIO_ALERT(0, 60), + OT_DJ_SOC_GPIO_ALERT(1, 61), OT_DJ_SOC_CLKMGR_HINT(OT_CLKMGR_HINT_OTBN) ), .link = IBEXDEVICELINKDEFS( @@ -620,7 +638,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 123), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 124), OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 125), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(3, PLIC, 126) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(3, PLIC, 126), + OT_DJ_SOC_GPIO_ALERT(0, 64), + OT_DJ_SOC_GPIO_ALERT(1, 65) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("random_src", AST), @@ -635,7 +655,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 127), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 128) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 128), + OT_DJ_SOC_GPIO_ALERT(0, 66), + OT_DJ_SOC_GPIO_ALERT(1, 67) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("csrng", CSRNG) @@ -652,7 +674,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 129), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 130) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 130), + OT_DJ_SOC_GPIO_ALERT(0, 68), + OT_DJ_SOC_GPIO_ALERT(1, 69) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("csrng", CSRNG) @@ -668,6 +692,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x211c0000u, 0x20u }, { 0x10000000, 0x10000u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 70) + ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("otp_ctrl", OTP_CTRL) ), @@ -683,6 +710,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x211d0000u, 0x20u }, { 0x11000000u, 0x1000u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 71) + ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("otp_ctrl", OTP_CTRL) ), @@ -699,6 +729,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x00008000u, 0x8000u } ), .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 72), OT_DJ_SOC_SIGNAL(OT_ROM_CTRL_GOOD, 0, PWRMGR, OT_PWRMGR_ROM_GOOD, 0), OT_DJ_SOC_SIGNAL(OT_ROM_CTRL_DONE, 0, PWRMGR, @@ -721,6 +752,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x00020000u, 0x10000u } ), .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 73), OT_DJ_SOC_SIGNAL(OT_ROM_CTRL_GOOD, 0, PWRMGR, OT_PWRMGR_ROM_GOOD, 1), OT_DJ_SOC_SIGNAL(OT_ROM_CTRL_DONE, 0, PWRMGR, @@ -740,6 +772,12 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .memmap = MEMMAPENTRIES( { 0x211f0000u, 0x800u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 95), + OT_DJ_SOC_GPIO_ALERT(1, 96), + OT_DJ_SOC_GPIO_ALERT(2, 97), + OT_DJ_SOC_GPIO_ALERT(3, 98) + ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("edn", EDN0) ), @@ -754,35 +792,36 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x21200000u, 0x1000u } ), .gpio = IBEXGPIOCONNDEFS( - OT_DARJEELING_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 0), - OT_DARJEELING_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 1), - OT_DARJEELING_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 2), - OT_DARJEELING_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 3) + OT_DJ_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 0), + OT_DJ_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 1), + OT_DJ_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 2), + OT_DJ_SOC_DM_CONNECTION(OT_DJ_SOC_DEV_DM, 3), + OT_DJ_SOC_GPIO_ALERT(0, 53) ), }, [OT_DJ_SOC_DEV_MBX0] = { - OT_DJ_SOC_DEV_MBX(0, 0x22000000u, "ot-mbx-sram", 134), + OT_DJ_SOC_DEV_MBX(0, 0x22000000u, "ot-mbx-sram", 134, 75), }, [OT_DJ_SOC_DEV_MBX1] = { - OT_DJ_SOC_DEV_MBX(1, 0x22000100u, "ot-mbx-sram", 137), + OT_DJ_SOC_DEV_MBX(1, 0x22000100u, "ot-mbx-sram", 137, 77), }, [OT_DJ_SOC_DEV_MBX2] = { - OT_DJ_SOC_DEV_MBX(2, 0x22000200u, "ot-mbx-sram", 140), + OT_DJ_SOC_DEV_MBX(2, 0x22000200u, "ot-mbx-sram", 140, 79), }, [OT_DJ_SOC_DEV_MBX3] = { - OT_DJ_SOC_DEV_MBX(3, 0x22000300u, "ot-mbx-sram", 143), + OT_DJ_SOC_DEV_MBX(3, 0x22000300u, "ot-mbx-sram", 143, 81), }, [OT_DJ_SOC_DEV_MBX4] = { - OT_DJ_SOC_DEV_MBX(4, 0x22000400u, "ot-mbx-sram", 146), + OT_DJ_SOC_DEV_MBX(4, 0x22000400u, "ot-mbx-sram", 146, 83), }, [OT_DJ_SOC_DEV_MBX5] = { - OT_DJ_SOC_DEV_MBX(5, 0x22000500u, "ot-mbx-sram", 149), + OT_DJ_SOC_DEV_MBX(5, 0x22000500u, "ot-mbx-sram", 149, 85), }, [OT_DJ_SOC_DEV_MBX6] = { - OT_DJ_SOC_DEV_MBX(6, 0x22000600u, "ot-mbx-sram", 152), + OT_DJ_SOC_DEV_MBX(6, 0x22000600u, "ot-mbx-sram", 152, 87), }, [OT_DJ_SOC_DEV_MBX_JTAG] = { - OT_DJ_SOC_DEV_MBX_DUAL(7, 0x22000800u, "ot-mbx-sram", 155, + OT_DJ_SOC_DEV_MBX_DUAL(7, 0x22000800u, "ot-mbx-sram", 155, 89, DEBUG_MEMORY(OT_DJ_DEBUG_MBX_JTAG_ADDR)), }, [OT_DJ_SOC_DEV_DMA] = { @@ -793,10 +832,10 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 131), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 132), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 133) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 133), + OT_DJ_SOC_GPIO_ALERT(0, 74) ), .prop = IBEXDEVICEPROPDEFS( - IBEX_DEV_STRING_PROP("ot_id", "0"), IBEX_DEV_STRING_PROP("ot_as_name", "ot-dma"), IBEX_DEV_STRING_PROP("ctn_as_name", "ctn-dma") ) @@ -839,17 +878,46 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(28, PLIC, 111), OT_DJ_SOC_GPIO_SYSBUS_IRQ(29, PLIC, 112), OT_DJ_SOC_GPIO_SYSBUS_IRQ(30, PLIC, 113), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(31, PLIC, 114) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(31, PLIC, 114), + OT_DJ_SOC_GPIO_ALERT(0, 23), + OT_DJ_SOC_GPIO_ALERT(1, 24), + OT_DJ_SOC_GPIO_ALERT(2, 25), + OT_DJ_SOC_GPIO_ALERT(3, 26), + OT_DJ_SOC_GPIO_ALERT(4, 27), + OT_DJ_SOC_GPIO_ALERT(5, 28), + OT_DJ_SOC_GPIO_ALERT(6, 29), + OT_DJ_SOC_GPIO_ALERT(7, 30), + OT_DJ_SOC_GPIO_ALERT(8, 31), + OT_DJ_SOC_GPIO_ALERT(9, 32), + OT_DJ_SOC_GPIO_ALERT(10, 33), + OT_DJ_SOC_GPIO_ALERT(11, 34), + OT_DJ_SOC_GPIO_ALERT(12, 35), + OT_DJ_SOC_GPIO_ALERT(13, 36), + OT_DJ_SOC_GPIO_ALERT(14, 37), + OT_DJ_SOC_GPIO_ALERT(15, 38), + OT_DJ_SOC_GPIO_ALERT(16, 39), + OT_DJ_SOC_GPIO_ALERT(17, 40), + OT_DJ_SOC_GPIO_ALERT(18, 41), + OT_DJ_SOC_GPIO_ALERT(19, 42), + OT_DJ_SOC_GPIO_ALERT(20, 43), + OT_DJ_SOC_GPIO_ALERT(21, 44), + OT_DJ_SOC_GPIO_ALERT(22, 45), + OT_DJ_SOC_GPIO_ALERT(23, 46), + OT_DJ_SOC_GPIO_ALERT(24, 47), + OT_DJ_SOC_GPIO_ALERT(25, 48), + OT_DJ_SOC_GPIO_ALERT(26, 49), + OT_DJ_SOC_GPIO_ALERT(27, 50), + OT_DJ_SOC_GPIO_ALERT(28, 51) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_STRING_PROP("ot_id", "0") ), }, [OT_DJ_SOC_DEV_MBX_PCIE0] = { - OT_DJ_SOC_DEV_MBX(8, 0x22040000u, "ot-mbx-sram", 158), + OT_DJ_SOC_DEV_MBX(8, 0x22040000u, "ot-mbx-sram", 158, 91), }, [OT_DJ_SOC_DEV_MBX_PCIE1] = { - OT_DJ_SOC_DEV_MBX(9, 0x22040100u, "ot-mbx-sram", 161), + OT_DJ_SOC_DEV_MBX(9, 0x22040100u, "ot-mbx-sram", 161, 93), }, [OT_DJ_SOC_DEV_PLIC] = { .type = TYPE_SIFIVE_PLIC, @@ -880,7 +948,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x2c000000u, 0x10u } ), .gpio = IBEXGPIOCONNDEFS( - OT_DJ_SOC_GPIO(0, HART, IRQ_M_SOFT) + OT_DJ_SOC_GPIO(0, HART, IRQ_M_SOFT), + OT_DJ_SOC_GPIO_ALERT(0, 54) ), }, [OT_DJ_SOC_DEV_GPIO] = { @@ -920,7 +989,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(28, PLIC, 37), OT_DJ_SOC_GPIO_SYSBUS_IRQ(29, PLIC, 38), OT_DJ_SOC_GPIO_SYSBUS_IRQ(30, PLIC, 39), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(31, PLIC, 40) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(31, PLIC, 40), + OT_DJ_SOC_GPIO_ALERT(0, 1) ) }, [OT_DJ_SOC_DEV_UART0] = { @@ -938,7 +1008,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(4, PLIC, 5), OT_DJ_SOC_GPIO_SYSBUS_IRQ(5, PLIC, 6), OT_DJ_SOC_GPIO_SYSBUS_IRQ(6, PLIC, 7), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(7, PLIC, 8) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(7, PLIC, 8), + OT_DJ_SOC_GPIO_ALERT(0, 0) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ) @@ -975,7 +1046,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(11, PLIC, 64), OT_DJ_SOC_GPIO_SYSBUS_IRQ(12, PLIC, 65), OT_DJ_SOC_GPIO_SYSBUS_IRQ(13, PLIC, 66), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(14, PLIC, 67) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(14, PLIC, 67), + OT_DJ_SOC_GPIO_ALERT(0, 3) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ) @@ -988,7 +1060,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO(0, HART, IRQ_M_TIMER), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 68) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 68), + OT_DJ_SOC_GPIO_ALERT(0, 4) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("pclk", OT_DJ_PERIPHERAL_CLK_HZ) @@ -1004,6 +1077,11 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 69), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 70), + OT_DJ_SOC_GPIO_ALERT(0, 5), + OT_DJ_SOC_GPIO_ALERT(1, 6), + OT_DJ_SOC_GPIO_ALERT(2, 7), + OT_DJ_SOC_GPIO_ALERT(3, 8), + OT_DJ_SOC_GPIO_ALERT(4, 9), OT_DJ_SOC_RSP(OT_PWRMGR_OTP, PWRMGR) ), .link = IBEXDEVICELINKDEFS( @@ -1020,6 +1098,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { DEBUG_MEMORY(OT_DJ_DEBUG_LC_CTRL_ADDR), OT_DJ_DEBUG_LC_CTRL_SIZE } ), .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 10), + OT_DJ_SOC_GPIO_ALERT(1, 11), + OT_DJ_SOC_GPIO_ALERT(2, 12), OT_DJ_SOC_D2S(OT_LC_BROADCAST, OT_LC_HW_DEBUG_EN, LC_HW_DEBUG), OT_DJ_SOC_D2S(OT_LC_BROADCAST, OT_LC_ESCALATE_EN, @@ -1063,7 +1144,11 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 71), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 72), OT_DJ_SOC_GPIO_SYSBUS_IRQ(2, PLIC, 73), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(3, PLIC, 74) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(3, PLIC, 74), + OT_DJ_SOC_GPIO_ESCALATE(0, IBEX_WRAPPER, 0), + OT_DJ_SOC_GPIO_ESCALATE(1, LC_CTRL, 0), + OT_DJ_SOC_GPIO_ESCALATE(2, LC_CTRL, 1), + OT_DJ_SOC_GPIO_ESCALATE(3, PWRMGR, 0) ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("edn", EDN0) @@ -1084,7 +1169,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 76), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 77) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 77), + OT_DJ_SOC_GPIO_ALERT(0, 13) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("bus-num", 0) @@ -1107,7 +1193,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_GPIO_SYSBUS_IRQ(8, PLIC, 49), OT_DJ_SOC_GPIO_SYSBUS_IRQ(9, PLIC, 50), OT_DJ_SOC_GPIO_SYSBUS_IRQ(10, PLIC, 51), - OT_DJ_SOC_GPIO_SYSBUS_IRQ(11, PLIC, 52) + OT_DJ_SOC_GPIO_SYSBUS_IRQ(11, PLIC, 52), + OT_DJ_SOC_GPIO_ALERT(0, 2) ), }, [OT_DJ_SOC_DEV_PWRMGR] = { @@ -1117,6 +1204,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { ), .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 78), + OT_DJ_SOC_GPIO_ALERT(0, 14), OT_DJ_SOC_REQ(OT_PWRMGR_OTP, OTP_CTRL), OT_DJ_SOC_REQ(OT_PWRMGR_LC, LC_CTRL), OT_DJ_SOC_SIGNAL(OT_PWRMGR_CPU_EN, 0, IBEX_WRAPPER, @@ -1138,6 +1226,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x30410000u, 0x80u } ), .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 15), + OT_DJ_SOC_GPIO_ALERT(1, 16), OT_DJ_SOC_SIGNAL(OT_RSTMGR_SW_RST, 0, PWRMGR, OT_PWRMGR_SW_RST, 0) ), @@ -1147,6 +1237,10 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .memmap = MEMMAPENTRIES( { 0x30420000u, 0x80u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 17), + OT_DJ_SOC_GPIO_ALERT(1, 18) + ) }, [OT_DJ_SOC_DEV_PINMUX] = { .type = TYPE_OT_PINMUX_DJ, @@ -1185,7 +1279,8 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_PINMUX_LINK(DIO, GPIO_GPIO28, GPIO, 28), OT_DJ_PINMUX_LINK(DIO, GPIO_GPIO29, GPIO, 29), OT_DJ_PINMUX_LINK(DIO, GPIO_GPIO30, GPIO, 30), - OT_DJ_PINMUX_LINK(DIO, GPIO_GPIO31, GPIO, 31) + OT_DJ_PINMUX_LINK(DIO, GPIO_GPIO31, GPIO, 31), + OT_DJ_SOC_GPIO_ALERT(0, 19) ), }, [OT_DJ_SOC_DEV_AON_TIMER] = { @@ -1196,6 +1291,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { .gpio = IBEXGPIOCONNDEFS( OT_DJ_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 79), OT_DJ_SOC_GPIO_SYSBUS_IRQ(1, PLIC, 80), + OT_DJ_SOC_GPIO_ALERT(0, 20), OT_DJ_SOC_SIGNAL(OT_AON_TIMER_WKUP, 0, PWRMGR, OT_PWRMGR_WKUP, OT_PWRMGR_WAKEUP_AON_TIMER), OT_DJ_SOC_SIGNAL(OT_AON_TIMER_BITE, 0, PWRMGR, @@ -1218,6 +1314,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { { 0x30500000u, 0x20u }, { 0x30600000u, 0x1000u } ), + .gpio = IBEXGPIOCONNDEFS( + OT_DJ_SOC_GPIO_ALERT(0, 52) + ), .link = IBEXDEVICELINKDEFS( OT_DJ_SOC_DEVLINK("otp_ctrl", OTP_CTRL) ), From 75f66626f3305c4b37d663e7c31ff6c7ddd88ab8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 May 2024 18:57:12 +0200 Subject: [PATCH 27/65] [ot] scripts/opentitan: pyot.py: add 'expect' option This option may be used in a test configuration section, to tell pyot.py what QEMU is expected to fail with a specific error code. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index f5d4b09943518..bbc8281f1a5ac 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -124,7 +124,7 @@ class QEMUWrapper: such as SIGABRT. """ - GUEST_ERROR_OFFSET = 30 + GUEST_ERROR_OFFSET = 40 """Offset for guest errors. Should be larger than the host max signal value. """ @@ -914,7 +914,9 @@ class QEMUExecuter: 1: 'ERROR', 6: 'ABORT', 11: 'CRASH', + QEMUWrapper.GUEST_ERROR_OFFSET - 1: 'GUEST_ESC', QEMUWrapper.GUEST_ERROR_OFFSET + 1: 'FAIL', + 98: 'UNEXP_SUCCESS', 99: 'CONTEXT', 124: 'TIMEOUT', 125: 'DEADLOCK', @@ -1006,13 +1008,22 @@ def run(self, debug: bool, allow_no_test: bool) -> int: 'UTFILE': basename(test), }) test_name = self.get_test_radix(test) - qemu_cmd, targs, timeout, temp_files, ctx, sdelay = \ + qemu_cmd, targs, timeout, temp_files, ctx, sdelay, texp = \ self._build_qemu_test_command(test) ctx.execute('pre') tret, xtime, err = qot.run(qemu_cmd, timeout, test_name, ctx, sdelay) cret = ctx.finalize() ctx.execute('post', tret) + if texp != 0: + if tret == texp: + self._log.info('QEMU failed with expected error, ' + 'assume success') + tret = 0 + elif tret == 0: + self._log.warning('QEMU success while expected ' + 'error %d, assume error', tret) + tret = 98 if tret == 0 and cret != 0: tret = 99 # pylint: disable=broad-except @@ -1224,13 +1235,13 @@ def _build_qemu_command(self, args: Namespace, def _build_qemu_test_command(self, filename: str) -> \ Tuple[List[str], Namespace, int, Dict[str, Set[str]], QEMUContext, - float]: + float, int]: test_name = self.get_test_radix(filename) - args, opts, timeout = self._build_test_args(test_name) + args, opts, timeout, texp = self._build_test_args(test_name) setattr(args, 'exec', filename) qemu_cmd, _, temp_files, sdelay = self._build_qemu_command(args, opts) ctx = self._build_test_context(test_name) - return qemu_cmd, args, timeout, temp_files, ctx, sdelay + return qemu_cmd, args, timeout, temp_files, ctx, sdelay, texp def _build_test_list(self, alphasort: bool = True) -> List[str]: pathnames = set() @@ -1365,7 +1376,16 @@ def _build_test_args(self, test_name: str) \ timeout = float(kwargs.get('timeout', DEFAULT_TIMEOUT)) tmfactor = float(kwargs.get('timeout_factor', DEFAULT_TIMEOUT_FACTOR)) itimeout = int(timeout * tmfactor) - return Namespace(**kwargs), opts or [], itimeout + texpect = kwargs.get('expect', 0) + try: + texp = int(texpect) + except ValueError: + result_map = {v: k for k, v in self.RESULT_MAP.items()} + try: + texp = result_map[texpect.upper()] + except KeyError as exc: + raise ValueError(f'Unsupported expect: {texpect}') from exc + return Namespace(**kwargs), opts or [], itimeout, texp def _build_test_context(self, test_name: str) -> QEMUContext: context = defaultdict(list) From f4675fbf906d9e681355a7a5e79072b92beab968 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 16 May 2024 19:09:12 +0200 Subject: [PATCH 28/65] [ot] .gitlab-ci.d: update unit test for alert handler Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index f439d8085f8da..1ef24f5d3af07 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240506-1" + BAREMETAL_REF: "240521-1" QEMU_BUILD_OPTS: "" include: From 0cba3de716ae9e5f34c7101b7e33a25872d23f6e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 21 May 2024 14:53:55 +0200 Subject: [PATCH 29/65] [ot] hw/jtag: move jtag from top-level directory to hw/jtag Signed-off-by: Emmanuel Blot --- hw/Kconfig | 3 +++ {jtag => hw/jtag}/jtag_bitbang.c | 0 {jtag => hw/jtag}/meson.build | 0 {jtag => hw/jtag}/trace-events | 0 hw/jtag/trace.h | 1 + hw/meson.build | 1 + include/{exec => hw/jtag}/jtagstub.h | 0 jtag/trace.h | 1 - meson.build | 7 +++---- qemu-options.hx | 17 ----------------- scripts/opentitan/clang-format.d/opentitan.lst | 4 ++-- system/vl.c | 10 ---------- 12 files changed, 10 insertions(+), 34 deletions(-) rename {jtag => hw/jtag}/jtag_bitbang.c (100%) rename {jtag => hw/jtag}/meson.build (100%) rename {jtag => hw/jtag}/trace-events (100%) create mode 100644 hw/jtag/trace.h rename include/{exec => hw/jtag}/jtagstub.h (100%) delete mode 100644 jtag/trace.h diff --git a/hw/Kconfig b/hw/Kconfig index cbe9b59bca126..ec373057cf6f2 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -71,6 +71,9 @@ source xtensa/Kconfig # Ibex Demo System devices source ibexdemo/Kconfig +# JTAG devices +source jtag/Kconfig + # OpenTitan devices source opentitan/Kconfig diff --git a/jtag/jtag_bitbang.c b/hw/jtag/jtag_bitbang.c similarity index 100% rename from jtag/jtag_bitbang.c rename to hw/jtag/jtag_bitbang.c diff --git a/jtag/meson.build b/hw/jtag/meson.build similarity index 100% rename from jtag/meson.build rename to hw/jtag/meson.build diff --git a/jtag/trace-events b/hw/jtag/trace-events similarity index 100% rename from jtag/trace-events rename to hw/jtag/trace-events diff --git a/hw/jtag/trace.h b/hw/jtag/trace.h new file mode 100644 index 0000000000000..b0a55dce5901c --- /dev/null +++ b/hw/jtag/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_jtag.h" diff --git a/hw/meson.build b/hw/meson.build index a014e6a36b209..eca13cdcef134 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -69,4 +69,5 @@ subdir('tricore') subdir('xtensa') subdir('ibexdemo') +subdir('jtag') subdir('opentitan') diff --git a/include/exec/jtagstub.h b/include/hw/jtag/jtagstub.h similarity index 100% rename from include/exec/jtagstub.h rename to include/hw/jtag/jtagstub.h diff --git a/jtag/trace.h b/jtag/trace.h deleted file mode 100644 index aece0b29db498..0000000000000 --- a/jtag/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-jtag.h" diff --git a/meson.build b/meson.build index dd77f0240408f..5e06aaeaf37c5 100644 --- a/meson.build +++ b/meson.build @@ -31,7 +31,7 @@ if targetos == 'darwin' and \ objc = meson.get_compiler('objc') endif -# Temporary directory used for files created while +# Temporary direct gory used for files created while # configure runs. Since it is in the build directory # we can safely blow away any previous version of it # (and we need not jump through hoops to try to delete @@ -3267,8 +3267,7 @@ trace_events_subdirs = [ 'qom', 'monitor', 'util', - 'gdbstub', - 'jtag' + 'gdbstub' ] if have_linux_user trace_events_subdirs += [ 'linux-user' ] @@ -3314,6 +3313,7 @@ if have_system 'hw/input', 'hw/intc', 'hw/isa', + 'hw/jtag', 'hw/mem', 'hw/mips', 'hw/misc', @@ -3398,7 +3398,6 @@ subdir('crypto') subdir('ui') subdir('hw') subdir('gdbstub') -subdir('jtag') if enable_modules libmodulecommon = static_library('module-common', files('module-common.c') + genh, pic: true, c_args: '-DBUILD_DSO') diff --git a/qemu-options.hx b/qemu-options.hx index a079d9bacd185..42fd09e4de96e 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4444,23 +4444,6 @@ SRST (see the :ref:`GDB usage` chapter in the System Emulation Users Guide). ERST -DEF("jtag", HAS_ARG, QEMU_OPTION_jtag, \ - "-jtag dev accept jtag connection on 'dev'. (QEMU defaults to starting\n" - " the guest without waiting for jtag to connect; use -S too\n" - " if you want it to not start execution.)\n", - QEMU_ARCH_ALL) -SRST -``-jtag dev`` - Accept a jtag connection on device dev. Note that this option does not pause - QEMU execution -- if you want QEMU to not start the guest until you - connect with jtag and issue a ``continue`` command, you will need to - also pass the ``-S`` option to QEMU. - - The most usual configuration is to listen on a local TCP socket:: - - -jtag tcp::3335 -ERST - DEF("d", HAS_ARG, QEMU_OPTION_d, \ "-d item1,... enable logging of specified items (use '-d help' for a list of log items)\n", QEMU_ARCH_ALL) diff --git a/scripts/opentitan/clang-format.d/opentitan.lst b/scripts/opentitan/clang-format.d/opentitan.lst index d417810651a39..d68d1aee1cba5 100644 --- a/scripts/opentitan/clang-format.d/opentitan.lst +++ b/scripts/opentitan/clang-format.d/opentitan.lst @@ -1,3 +1,4 @@ +hw/jtag/*.c hw/misc/pulp_rv_dm.c hw/opentitan/*.c hw/opentitan/*.h @@ -5,12 +6,11 @@ hw/riscv/debug.c hw/riscv/dm*.c hw/riscv/ibex*.c hw/riscv/ot_*.c -include/exec/jtagstub.h +include/hw/jtag/*.h include/hw/misc/pulp_rv_dm.h include/hw/opentitan/*.h include/hw/riscv/debug.h include/hw/riscv/dm*.h include/hw/riscv/ibex*.h include/hw/riscv/ot_*.h -jtag/*.c target/riscv/ibex*.c diff --git a/system/vl.c b/system/vl.c index f39c546a684fe..2bcd9efb9a643 100644 --- a/system/vl.c +++ b/system/vl.c @@ -98,7 +98,6 @@ #ifdef CONFIG_TCG #include "accel/tcg/perf.h" #endif -#include "exec/jtagstub.h" #include "disas/disas.h" @@ -1272,7 +1271,6 @@ struct device_config { DEV_PARALLEL, /* -parallel */ DEV_DEBUGCON, /* -debugcon */ DEV_GDB, /* -gdb, -s */ - DEV_JTAG, /* -jtag */ DEV_SCLP, /* s390 sclp */ } type; const char *cmdline; @@ -2688,7 +2686,6 @@ static void qemu_machine_creation_done(void) if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { exit(1); } - if (!vga_interface_created && !default_vga && vga_interface_type != VGA_NONE) { warn_report("A -vga option was passed but this machine " @@ -2704,10 +2701,6 @@ void qmp_x_exit_preconfig(Error **errp) return; } - if (foreach_device_config(DEV_JTAG, jtagserver_start) < 0) { - exit(1); - } - qemu_init_board(); qemu_create_cli_devices(); qemu_machine_creation_done(); @@ -3048,9 +3041,6 @@ void qemu_init(int argc, char **argv) case QEMU_OPTION_gdb: add_device_config(DEV_GDB, optarg); break; - case QEMU_OPTION_jtag: - add_device_config(DEV_JTAG, optarg); - break; case QEMU_OPTION_L: if (is_help_option(optarg)) { list_data_dirs = true; From daa13438bfada5570744efbabfd0a95356132838 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 22 May 2024 16:09:31 +0200 Subject: [PATCH 30/65] [ot] hw/opentitan: move ot_common get_chardev_by_id utility to ibex_common Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_common.c | 35 -------------------------------- hw/riscv/ibex_common.c | 35 ++++++++++++++++++++++++++++++++ include/hw/opentitan/ot_common.h | 8 -------- include/hw/riscv/ibex_common.h | 13 ++++++++++++ 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/hw/opentitan/ot_common.c b/hw/opentitan/ot_common.c index f0d19fb0a5113..7c9cca0d6a9d6 100644 --- a/hw/opentitan/ot_common.c +++ b/hw/opentitan/ot_common.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "qom/object.h" -#include "chardev/chardev-internal.h" #include "hw/opentitan/ot_common.h" typedef struct { @@ -109,40 +108,6 @@ void ot_common_ignore_chr_status_lines(CharBackend *chr) #endif } -typedef struct { - Chardev *chr; - const char *label; -} OtCommonChrMatch; - -static int ot_common_match_chardev(Object *child, void *opaque) -{ - OtCommonChrMatch *match = opaque; - Chardev *chr = CHARDEV(child); - - if (strcmp(match->label, chr->label) != 0) { - return 0; - } - - match->chr = chr; - return 1; -} - -Chardev *ot_common_get_chardev_by_id(const char *chrid) -{ - OtCommonChrMatch match = { - .chr = NULL, - .label = chrid, - }; - - /* "chardev-internal.h" inclusion is required for get_chardevs_root() */ - if (!object_child_foreach(get_chardevs_root(), &ot_common_match_chardev, - &match)) { - return NULL; - } - - return match.chr; -} - int ot_common_string_ends_with(const char *str, const char *suffix) { size_t str_len = strlen(str); diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c index d7db738effd58..8eb8e5c7d689d 100644 --- a/hw/riscv/ibex_common.c +++ b/hw/riscv/ibex_common.c @@ -25,6 +25,7 @@ #include "qemu/log.h" #include "qapi/error.h" #include "qom/object.h" +#include "chardev/chardev-internal.h" #include "cpu.h" #include "disas/disas.h" #include "elf.h" @@ -592,6 +593,40 @@ DeviceState *ibex_get_child_device(DeviceState *s, const char *typename, return DEVICE(match.child); } +typedef struct { + Chardev *chr; + const char *label; +} IbexChrMatch; + +static int ibex_match_chardev(Object *child, void *opaque) +{ + IbexChrMatch *match = opaque; + Chardev *chr = CHARDEV(child); + + if (strcmp(match->label, chr->label) != 0) { + return 0; + } + + match->chr = chr; + return 1; +} + +Chardev *ibex_get_chardev_by_id(const char *chrid) +{ + IbexChrMatch match = { + .chr = NULL, + .label = chrid, + }; + + /* "chardev-internal.h" inclusion is required for get_chardevs_root() */ + if (!object_child_foreach(get_chardevs_root(), &ibex_match_chardev, + &match)) { + return NULL; + } + + return match.chr; +} + void ibex_unimp_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { diff --git a/include/hw/opentitan/ot_common.h b/include/hw/opentitan/ot_common.h index ec00a88442c88..48f9a7d7d8cc9 100644 --- a/include/hw/opentitan/ot_common.h +++ b/include/hw/opentitan/ot_common.h @@ -284,14 +284,6 @@ AddressSpace *ot_common_get_local_address_space(DeviceState *s); */ void ot_common_ignore_chr_status_lines(CharBackend *chr); -/** - * Find a char device by its id, e.g. "-chardev type,id=,...`" - * - * @chrid the id of the char device - * @return the char device if found, @c NULL otherwise. - */ -Chardev *ot_common_get_chardev_by_id(const char *chrid); - /* ------------------------------------------------------------------------ */ /* String utilities */ /* ------------------------------------------------------------------------ */ diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index c414e565161e8..aa7e81da98ab2 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -515,4 +515,17 @@ enum { */ void ibex_log_vcpu_registers(uint64_t regbm); +/* ------------------------------------------------------------------------ */ +/* CharDev utilities */ +/* ------------------------------------------------------------------------ */ + +/** + * Find a char device by its id, e.g. "-chardev type,id=,...`" + * + * @chrid the id of the char device + * @return the char device if found, @c NULL otherwise. + */ +Chardev *ibex_get_chardev_by_id(const char *chrid); + + #endif /* HW_RISCV_IBEX_COMMON_H */ From 287c954b7b8e68bfb279c0edb16967957c65a5ba Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 22 May 2024 16:10:17 +0200 Subject: [PATCH 31/65] [ot] jtag: rework JTAG / TAP controller support. Initial JTAG implementation had borrowed GDB server to support incoming connections. It is better to use a regular QEMU device: it can be instantiated multiple times and does not need to rely on global variables as GDB server does. JTAG / TAP controller service is enabled when a CharDev is connected to the TAP controller instance. This CharDev may be created from the command line. There is no longer any need for a special `-jtag` option in `vl.c` Signed-off-by: Emmanuel Blot --- hw/jtag/Kconfig | 8 + hw/jtag/jtag_bitbang.c | 742 --------------------- hw/jtag/meson.build | 3 +- hw/jtag/tap_ctrl.c | 42 ++ hw/jtag/tap_ctrl_rbb.c | 732 ++++++++++++++++++++ hw/jtag/trace-events | 24 +- hw/misc/pulp_rv_dm.c | 2 +- hw/opentitan/ot_dm_tl.c | 36 +- hw/opentitan/trace-events | 7 +- hw/riscv/Kconfig | 4 + hw/riscv/dm.c | 12 +- hw/riscv/dtm.c | 84 ++- hw/riscv/ibexdemo.c | 42 +- hw/riscv/ot_darjeeling.c | 33 +- hw/riscv/ot_earlgrey.c | 33 +- include/hw/jtag/{jtagstub.h => tap_ctrl.h} | 73 +- include/hw/jtag/tap_ctrl_rbb.h | 35 + 17 files changed, 1035 insertions(+), 877 deletions(-) create mode 100644 hw/jtag/Kconfig delete mode 100644 hw/jtag/jtag_bitbang.c create mode 100644 hw/jtag/tap_ctrl.c create mode 100644 hw/jtag/tap_ctrl_rbb.c rename include/hw/jtag/{jtagstub.h => tap_ctrl.h} (61%) create mode 100644 include/hw/jtag/tap_ctrl_rbb.h diff --git a/hw/jtag/Kconfig b/hw/jtag/Kconfig new file mode 100644 index 0000000000000..1e3eddbf965e8 --- /dev/null +++ b/hw/jtag/Kconfig @@ -0,0 +1,8 @@ +# JTAG devices + +config TAP_CTRL + bool + +config TAP_CTRL_RBB + select TAP_CTRL + bool diff --git a/hw/jtag/jtag_bitbang.c b/hw/jtag/jtag_bitbang.c deleted file mode 100644 index c7b8a171b9150..0000000000000 --- a/hw/jtag/jtag_bitbang.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * QEMU OpenTitan JTAG TAP controller - * - * Copyright (c) 2022-2024 Rivos, Inc. - * Author(s): - * Emmanuel Blot - * - * For details check the documentation here: - * https://github.com/openocd-org/openocd/blob/master/ - * doc/manual/jtag/drivers/remote_bitbang.txt - * - * 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/ctype.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/sockets.h" -#include "qapi/error.h" -#include "qom/object.h" -#include "chardev/char-fe.h" -#include "chardev/char.h" -#include "exec/gdbstub.h" -#include "exec/hwaddr.h" -#include "exec/jtagstub.h" -#include "hw/boards.h" -#include "hw/cpu/cluster.h" -#include "hw/resettable.h" -#include "monitor/monitor.h" -#include "semihosting/semihost.h" -#include "sysemu/hw_accel.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "trace.h" - - -/* - * Type definitions - */ - -/* clang-format off */ - -typedef enum { - TEST_LOGIC_RESET, - RUN_TEST_IDLE, - SELECT_DR_SCAN, - CAPTURE_DR, - SHIFT_DR, - EXIT1_DR, - PAUSE_DR, - EXIT2_DR, - UPDATE_DR, - SELECT_IR_SCAN, - CAPTURE_IR, - SHIFT_IR, - EXIT1_IR, - PAUSE_IR, - EXIT2_IR, - UPDATE_IR, - _TAP_STATE_COUNT -} TAPState; - -typedef enum { TAPCTRL_BYPASS = 0, TAPCTRL_IDCODE = 1 } TAPCtrlKnownIrCodes; - -/* clang-format on */ - -typedef TAPDataHandler *tapctrl_data_reg_extender_t(uint64_t value); - -typedef struct _TAPController { - TAPState state; /* Current state */ - /* signals */ - bool trst; /* TAP controller reset */ - bool srst; /* System reset */ - bool tck; /* JTAG clock */ - bool tms; /* JTAG state machine selector */ - bool tdi; /* Register input */ - bool tdo; /* Register output */ - /* registers */ - uint64_t ir; /* instruction register value */ - size_t ir_len; /* count of meaningful bits in ir */ - uint64_t ir_hold; /* IR hold register */ - uint64_t dr; /* current data register value */ - size_t dr_len; /* count of meaningful bits in dr */ - /* handlers */ - TAPDataHandler *tdh; /* Current data register handler */ - GHashTable *tdhtable; /* Registered handlers */ - /* buffer */ -} TAPController; - -typedef struct _TAPRegisterState { - int base_reg; - int num_regs; - struct TAPRegisterState *next; -} TAPRegisterState; - -typedef struct _TAPProcess { - uint32_t pid; - bool attached; -} TAPProcess; - -typedef struct _TAPServerState { - TAPController *tap; - CharBackend chr; - bool enable_quit; /* whether VM quit can be remotely triggered */ - bool init; /* have we been initialised? */ -} TAPServerState; - -/* - * Macros - */ - -#define STRINGIFY_(_val_) #_val_ -#define STRINGIFY(_val_) STRINGIFY_(_val_) -#define NAME_FSMSTATE(_st_) [_st_] = STRINGIFY(_st_) - -/* - * Constants - */ - -#define DEFAULT_JTAG_BITBANG_PORT "3335" -#define MAX_PACKET_LENGTH 4096u - -/* - * TAP controller state machine state/event matrix - * - * Current state -> Next States for either TMS == 0 or TMS == 1 - */ -static const TAPState TAPFSM[_TAP_STATE_COUNT][2] = { - [TEST_LOGIC_RESET] = { RUN_TEST_IDLE, TEST_LOGIC_RESET }, - [RUN_TEST_IDLE] = { RUN_TEST_IDLE, SELECT_DR_SCAN }, - [SELECT_DR_SCAN] = { CAPTURE_DR, SELECT_IR_SCAN }, - [CAPTURE_DR] = { SHIFT_DR, EXIT1_DR }, - [SHIFT_DR] = { SHIFT_DR, EXIT1_DR }, - [EXIT1_DR] = { PAUSE_DR, UPDATE_DR }, - [PAUSE_DR] = { PAUSE_DR, EXIT2_DR }, - [EXIT2_DR] = { SHIFT_DR, UPDATE_DR }, - [UPDATE_DR] = { RUN_TEST_IDLE, SELECT_DR_SCAN }, - [SELECT_IR_SCAN] = { CAPTURE_IR, TEST_LOGIC_RESET }, - [CAPTURE_IR] = { SHIFT_IR, EXIT1_IR }, - [SHIFT_IR] = { SHIFT_IR, EXIT1_IR }, - [EXIT1_IR] = { PAUSE_IR, UPDATE_IR }, - [PAUSE_IR] = { PAUSE_IR, EXIT2_IR }, - [EXIT2_IR] = { SHIFT_IR, UPDATE_IR }, - [UPDATE_IR] = { RUN_TEST_IDLE, SELECT_DR_SCAN } -}; - -static const char TAPFSM_NAMES[_TAP_STATE_COUNT][18U] = { - NAME_FSMSTATE(TEST_LOGIC_RESET), NAME_FSMSTATE(RUN_TEST_IDLE), - NAME_FSMSTATE(SELECT_DR_SCAN), NAME_FSMSTATE(CAPTURE_DR), - NAME_FSMSTATE(SHIFT_DR), NAME_FSMSTATE(EXIT1_DR), - NAME_FSMSTATE(PAUSE_DR), NAME_FSMSTATE(EXIT2_DR), - NAME_FSMSTATE(UPDATE_DR), NAME_FSMSTATE(SELECT_IR_SCAN), - NAME_FSMSTATE(CAPTURE_IR), NAME_FSMSTATE(SHIFT_IR), - NAME_FSMSTATE(EXIT1_IR), NAME_FSMSTATE(PAUSE_IR), - NAME_FSMSTATE(EXIT2_IR), NAME_FSMSTATE(UPDATE_IR), -}; - -static void tapctrl_idcode_capture(TAPDataHandler *tdh); - -/* Common TAP instructions */ -static const TAPDataHandler tapctrl_bypass = { - .name = "bypass", - .length = 1, - .value = 0, -}; - -static const TAPDataHandler tapctrl_idcode = { - .name = "idcode", - .length = 32, - .capture = &tapctrl_idcode_capture, -}; - -/* - * Variables - */ - -/* Unique instance of the TAP server */ -static TAPServerState tapserver_state; - -/* - * TAP State Machine implementation - */ - -static void tapctrl_dump_register(const char *msg, const char *iname, - uint64_t value, size_t length) -{ - char buf[80]; - if (length > 64u) { - length = 64u; - } - unsigned ix = 0; - while (ix < length) { - buf[ix] = (char)('0' + ((value >> (length - ix - 1)) & 0b1)); - ix++; - } - buf[ix] = '\0'; - - if (iname) { - trace_jtag_tapctrl_idump_register(msg, iname, value, length, buf); - } else { - trace_jtag_tapctrl_dump_register(msg, value, length, buf); - } -} - -static bool tapctrl_has_data_handler(TAPController *tap, unsigned code) -{ - return (bool)g_hash_table_contains(tap->tdhtable, GINT_TO_POINTER(code)); -} - -static TAPDataHandler * -tapctrl_get_data_handler(TAPController *tap, unsigned code) -{ - TAPDataHandler *tdh; - tdh = (TAPDataHandler *)g_hash_table_lookup(tap->tdhtable, - GINT_TO_POINTER(code)); - return tdh; -} - -static void tapctrl_idcode_capture(TAPDataHandler *tdh) -{ - /* special case for ID code: opaque contains the ID code value */ - tdh->value = (uint64_t)(uintptr_t)tdh->opaque; -} - -static void tapctrl_reset(TAPController *tap) -{ - tap->state = TEST_LOGIC_RESET; - tap->trst = false; - tap->srst = false; - tap->tck = false; - tap->tms = false; - tap->tdi = false; - tap->tdo = false; - tap->ir = 0b01; - tap->ir_hold = 0b01; - tap->dr = 0u; - tap->dr_len = 0u; - tap->tdh = tapctrl_get_data_handler(tap, TAPCTRL_IDCODE); - g_assert(tap->tdh); -} - -static void tapctrl_system_reset(TAPController *tap) -{ - Object *mc = qdev_get_machine(); - ObjectClass *oc = object_get_class(mc); - (void)tap; - - if (!object_class_dynamic_cast(oc, TYPE_RESETTABLE_INTERFACE)) { - qemu_log_mask(LOG_UNIMP, "%s: Machine %s is not resettable\n", __func__, - object_get_typename(mc)); - return; - } - - trace_jtag_tapctrl_system_reset(); - resettable_reset(mc, RESET_TYPE_COLD); -} - -static void tapctrl_register_handler(TAPController *tap, unsigned code, - const TAPDataHandler *tdh) -{ - if (code >= (1 << tap->ir_len)) { - error_setg(&error_fatal, "JTAG: Invalid IR code: 0x%x", code); - g_assert_not_reached(); - } - if (tapctrl_has_data_handler(tap, code)) { - warn_report("JTAG: IR code already registered: 0x%x", code); - /* resume and override */ - } - TAPDataHandler *ltdh = g_new0(TAPDataHandler, 1u); - memcpy(ltdh, tdh, sizeof(*tdh)); - ltdh->name = g_strdup(tdh->name); - g_hash_table_insert(tap->tdhtable, GINT_TO_POINTER(code), ltdh); - trace_jtag_tapctrl_register(code, ltdh->name); -} - -static void tapctrl_free_data_handler(gpointer entry) -{ - TAPDataHandler *tdh = entry; - if (!entry) { - return; - } - g_free((char *)tdh->name); - g_free(tdh); -} - -static void tapctrl_init(TAPController *tap, size_t irlength, uint32_t idcode) -{ - trace_jtag_tapctrl_init(irlength, idcode); - tap->ir_len = irlength; - if (!tap->tdhtable) { - size_t irslots = 1u << irlength; - tap->tdhtable = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, tapctrl_free_data_handler); - tapctrl_register_handler(tap, TAPCTRL_BYPASS, &tapctrl_bypass); - tapctrl_register_handler(tap, TAPCTRL_IDCODE, &tapctrl_idcode); - tapctrl_register_handler(tap, irslots - 1u, &tapctrl_bypass); - /* special case for ID code: opaque store the constant idcode value */ - TAPDataHandler *tdh = tapctrl_get_data_handler(tap, TAPCTRL_IDCODE); - g_assert(tdh); - tdh->opaque = (void *)(uintptr_t)idcode; - } - tapctrl_reset(tap); -} - -static void tapctrl_deinit(TAPController *tap) -{ - if (tap->tdhtable) { - g_hash_table_destroy(tap->tdhtable); - tap->tdhtable = NULL; - } - tap->tdh = NULL; -} - -static TAPState tapctrl_get_next_state(TAPController *tap, bool tms) -{ - tap->state = TAPFSM[tap->state][(unsigned)tms]; - return tap->state; -} - -static void tapctrl_capture_ir(TAPController *tap) -{ - tap->ir = TAPCTRL_IDCODE; -} - -static void tapctrl_shift_ir(TAPController *tap, bool tdi) -{ - tap->ir >>= 1u; - tap->ir |= ((uint64_t)(tdi)) << (tap->ir_len - 1u); -} - -static void tapctrl_update_ir(TAPController *tap) -{ - tap->ir_hold = tap->ir; - tapctrl_dump_register("Update IR", NULL, tap->ir_hold, tap->ir_len); -} - -static void tapctrl_capture_dr(TAPController *tap) -{ - TAPDataHandler *prev = tap->tdh; - - if (tap->ir_hold >= (1 << tap->ir_len)) { - /* internal error, should never happen */ - error_setg(&error_fatal, "Invalid IR 0x%02x\n", (unsigned)tap->ir_hold); - g_assert_not_reached(); - } - - TAPDataHandler *tdh = tapctrl_get_data_handler(tap, tap->ir_hold); - if (!tdh) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown IR 0x%02x\n", __func__, - (unsigned)tap->ir_hold); - tap->dr = 0; - return; - } - - if (tdh != prev) { - trace_jtag_tapctrl_select_dr(tdh->name, tap->ir_hold); - } - - tap->tdh = tdh; - tap->dr_len = tdh->length; - - if (tdh->capture) { - tdh->capture(tdh); - } - tap->dr = tdh->value; - tapctrl_dump_register("Capture DR", tap->tdh->name, tap->dr, tap->dr_len); -} - -static void tapctrl_shift_dr(TAPController *tap, bool tdi) -{ - tap->dr >>= 1u; - tap->dr |= ((uint64_t)(tdi)) << (tap->dr_len - 1u); -} - -static void tapctrl_update_dr(TAPController *tap) -{ - tapctrl_dump_register("Update DR", tap->tdh->name, tap->dr, tap->dr_len); - TAPDataHandler *tdh = tap->tdh; - tdh->value = tap->dr; - if (tdh->update) { - tdh->update(tdh); - } -} - -static void tapctrl_step(TAPController *tap, bool tck, bool tms, bool tdi) -{ - trace_jtag_tapctrl_step(tck, tms, tdi); - - if (tap->trst) { - return; - } - - if (!tap->tck && tck) { - /* Rising clock edge */ - if (tap->state == SHIFT_IR) { - tapctrl_shift_ir(tap, tap->tdi); - } else if (tap->state == SHIFT_DR) { - tapctrl_shift_dr(tap, tap->tdi); - } - TAPState prev = tap->state; - TAPState new = tapctrl_get_next_state(tap, tms); - if (prev != new) { - trace_jtag_tapctrl_change_state(TAPFSM_NAMES[prev], - TAPFSM_NAMES[new]); - } - } else { - /* Falling clock edge */ - switch (tap->state) { - case RUN_TEST_IDLE: - /* do nothing */ - break; - case TEST_LOGIC_RESET: - tapctrl_reset(tap); - break; - case CAPTURE_DR: - tapctrl_capture_dr(tap); - break; - case SHIFT_DR: - tap->tdo = tap->dr & 0b1; - break; - case UPDATE_DR: - tapctrl_update_dr(tap); - break; - case CAPTURE_IR: - tapctrl_capture_ir(tap); - break; - case SHIFT_IR: - tap->tdo = tap->ir & 0b1; - break; - case UPDATE_IR: - tapctrl_update_ir(tap); - break; - default: - /* nothing to do on the other state transition */ - break; - } - } - tap->tck = tck; - tap->tdi = tdi; - tap->tms = tms; -} - -static void tapctrl_bb_blink(TAPController *tap, bool light) {} - -static void tapctrl_bb_read(TAPController *tap) -{ - trace_jtag_tapctrl_read(tap->tdo); -} - -static void tapctrl_bb_quit(TAPController *tap) -{ - (void)tap; - - if (tapserver_state.enable_quit) { - info_report("%s: JTAG-requested termination\n", __func__); - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } else { - info_report("%s: JTAG termination disabled\n", __func__); - } -} - -static void tapctrl_bb_write(TAPController *tap, bool tck, bool tms, bool tdi) -{ - tapctrl_step(tap, tck, tms, tdi); -} - -static void tapctrl_bb_reset(TAPController *tap, bool trst, bool srst) -{ - trace_jtag_tapctrl_reset(trst, srst); - if (trst) { - tapctrl_reset(tap); - } - if (srst) { - tapctrl_system_reset(tap); - } - tap->trst = trst; - tap->srst = srst; -} - -/* - * TAP Server implementation - */ - -static bool tap_read_byte(uint8_t ch) -{ - switch ((char)ch) { - case 'B': - tapctrl_bb_blink(tapserver_state.tap, true); - break; - case 'b': - tapctrl_bb_blink(tapserver_state.tap, false); - break; - case 'R': - tapctrl_bb_read(tapserver_state.tap); - break; - case 'Q': - tapctrl_bb_quit(tapserver_state.tap); - break; - case '0': - tapctrl_bb_write(tapserver_state.tap, false, false, false); - break; - case '1': - tapctrl_bb_write(tapserver_state.tap, false, false, true); - break; - case '2': - tapctrl_bb_write(tapserver_state.tap, false, true, false); - break; - case '3': - tapctrl_bb_write(tapserver_state.tap, false, true, true); - break; - case '4': - tapctrl_bb_write(tapserver_state.tap, true, false, false); - break; - case '5': - tapctrl_bb_write(tapserver_state.tap, true, false, true); - break; - case '6': - tapctrl_bb_write(tapserver_state.tap, true, true, false); - break; - case '7': - tapctrl_bb_write(tapserver_state.tap, true, true, true); - break; - case 'r': - tapctrl_bb_reset(tapserver_state.tap, false, false); - break; - case 's': - tapctrl_bb_reset(tapserver_state.tap, false, true); - break; - case 't': - tapctrl_bb_reset(tapserver_state.tap, true, false); - break; - case 'u': - tapctrl_bb_reset(tapserver_state.tap, true, true); - break; - default: - qemu_log_mask(LOG_UNIMP, "%s: Unknown TAP code 0x%02x\n", __func__, - (unsigned)ch); - break; - } - - /* true if TDO level should be sent to the peer */ - return (int)ch == 'R'; -} - -static int tap_chr_can_receive(void *opaque) -{ - /* do not accept any input till a TAP controller is available */ - return ((TAPServerState *)opaque)->tap ? MAX_PACKET_LENGTH : 0; -} - -static void tap_chr_receive(void *opaque, const uint8_t *buf, int size) -{ - TAPServerState *s = (TAPServerState *)opaque; - - for (unsigned ix = 0; ix < size; ix++) { - if (tap_read_byte(buf[ix])) { - const TAPController *tap = s->tap; - uint8_t outbuf[1] = { '0' + (unsigned)tap->tdo }; - qemu_chr_fe_write_all(&s->chr, outbuf, (int)sizeof(outbuf)); - } - } -} - -static int tap_monitor_write(Chardev *chr, const uint8_t *buf, int len) -{ - (void)chr; - (void)buf; - (void)len; - return 0; -} - -static void tap_monitor_open(Chardev *chr, ChardevBackend *backend, - bool *be_opened, Error **errp) -{ - (void)chr; - (void)backend; - (void)errp; - *be_opened = false; -} - -static void char_tap_class_init(ObjectClass *oc, void *data) -{ - ChardevClass *cc = CHARDEV_CLASS(oc); - (void)data; - - cc->internal = true; - cc->open = tap_monitor_open; - cc->chr_write = tap_monitor_write; -} - -#define TYPE_CHARDEV_JTAG "chardev-jtag" - -static const TypeInfo char_tap_type_info = { - .name = TYPE_CHARDEV_JTAG, - .parent = TYPE_CHARDEV, - .class_init = char_tap_class_init, -}; - -static void init_tapserver_state(bool enable_quit) -{ - g_assert(!tapserver_state.init); - memset(&tapserver_state, 0, sizeof(TAPServerState)); - tapserver_state.init = true; - tapserver_state.enable_quit = enable_quit; -} - -int jtagserver_start(const char *device) -{ - if (!device) { - return -1; - } - - Chardev *chr = NULL; - gchar **parts = g_strsplit(device, ",", 0); - unsigned quit_pos = UINT_MAX; - unsigned length = 0; - const char *quit = NULL; - bool enable_quit = true; - - for (gchar **p = parts; *p; p++, length++) { - if (strstart(*p, "quit=", &quit)) { - enable_quit = (bool)strcmp(quit, "off"); - quit_pos = length; - } - } - length += 1; /* account for NULL array terminator */ - if (quit_pos < length) { - /* skip quit parameter */ - g_free(parts[quit_pos]); - memmove(&parts[quit_pos], &parts[quit_pos + 1u], - (length - quit_pos - 1) * sizeof(gchar *)); - } - if (strcmp(parts[0], "none")) { - if (strstart(parts[0], "tcp:", NULL)) { - gchar *first = - g_strjoin(",", parts[0], "wait=off,nodelay=on,server=on", NULL); - g_free(parts[0]); - parts[0] = first; - } - gchar *device_name = g_strjoinv(",", parts); - chr = qemu_chr_new_noreplay("tap", device_name, true, NULL); - g_free(device_name); - } - - g_strfreev(parts); - - if (!chr) { - return -1; - } - - if (!tapserver_state.init) { - init_tapserver_state(enable_quit); - } else { - qemu_chr_fe_deinit(&tapserver_state.chr, true); - } - - if (chr) { - qemu_chr_fe_init(&tapserver_state.chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&tapserver_state.chr, tap_chr_can_receive, - tap_chr_receive, NULL, NULL, &tapserver_state, - NULL, true); - } - - return 0; -} - -void jtagserver_exit(void) -{ - if (!tapserver_state.init) { - return; - } - - qemu_chr_fe_deinit(&tapserver_state.chr, true); - - tapctrl_deinit(tapserver_state.tap); - g_free(tapserver_state.tap); - tapserver_state.tap = NULL; -} - -int jtag_register_handler(unsigned code, const TAPDataHandler *tdh) -{ - if (!tapserver_state.tap) { - return -1; - } - - tapctrl_register_handler(tapserver_state.tap, code, tdh); - - return 0; -} - -void jtag_configure_tap(size_t irlength, uint32_t idcode) -{ - if (irlength > 8u) { - error_setg(&error_fatal, "Unsupported IR length"); - return; - } - - if (idcode == 0u) { - error_setg(&error_fatal, "Invalid IDCODE"); - return; - } - - if (tapserver_state.init) { - if (!tapserver_state.tap) { - TAPController *tap = g_new0(TAPController, 1); - tapctrl_init(tap, irlength, idcode); - tapserver_state.tap = tap; - qemu_chr_fe_accept_input(&tapserver_state.chr); - } - } -} - -bool jtag_tap_enabled(void) -{ - return tapserver_state.init && tapserver_state.tap; -} - -static void register_types(void) -{ - type_register_static(&char_tap_type_info); -} - -type_init(register_types); diff --git a/hw/jtag/meson.build b/hw/jtag/meson.build index 469801d7de874..677ea188ee5e2 100644 --- a/hw/jtag/meson.build +++ b/hw/jtag/meson.build @@ -1 +1,2 @@ -system_ss.add(files('jtag_bitbang.c')) +system_ss.add(when: 'CONFIG_TAP_CTRL', if_true: files('tap_ctrl.c')) +system_ss.add(when: 'CONFIG_TAP_CTRL_RBB', if_true: files('tap_ctrl_rbb.c')) diff --git a/hw/jtag/tap_ctrl.c b/hw/jtag/tap_ctrl.c new file mode 100644 index 0000000000000..e335e584045e7 --- /dev/null +++ b/hw/jtag/tap_ctrl.c @@ -0,0 +1,42 @@ +/* + * QEMU JTAG TAP controller interface + * + * Copyright (c) 2023 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 "hw/jtag/tap_ctrl.h" + +static const TypeInfo tap_ctrl_info = { + .name = TYPE_TAP_CTRL_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(TapCtrlIfClass), +}; + +static void tap_ctrl_register_types(void) +{ + type_register_static(&tap_ctrl_info); +} + +type_init(tap_ctrl_register_types); diff --git a/hw/jtag/tap_ctrl_rbb.c b/hw/jtag/tap_ctrl_rbb.c new file mode 100644 index 0000000000000..04c8a6eb90a7c --- /dev/null +++ b/hw/jtag/tap_ctrl_rbb.c @@ -0,0 +1,732 @@ +/* + * QEMU JTAG TAP controller for the OpenOCD/Spike Remote BigBang protocol + * + * Copyright (c) 2022-2024 Rivos, Inc. + * Author(s): + * Emmanuel Blot + * + * For details check the documentation here: + * https://github.com/openocd-org/openocd/blob/master/ + * doc/manual/jtag/drivers/remote_bitbang.txt + * + * 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/ctype.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/sockets.h" +#include "qapi/error.h" +#include "qom/object.h" +#include "chardev/char-fe.h" +#include "chardev/char.h" +#include "exec/gdbstub.h" +#include "exec/hwaddr.h" +#include "hw/boards.h" +#include "hw/cpu/cluster.h" +#include "hw/jtag/tap_ctrl.h" +#include "hw/jtag/tap_ctrl_rbb.h" +#include "hw/qdev-properties-system.h" +#include "hw/qdev-properties.h" +#include "hw/resettable.h" +#include "monitor/monitor.h" +#include "semihosting/semihost.h" +#include "sysemu/hw_accel.h" +#include "sysemu/replay.h" +#include "sysemu/runstate.h" +#include "trace.h" + + +/* clang-format off */ + +typedef enum { + TEST_LOGIC_RESET, + RUN_TEST_IDLE, + SELECT_DR_SCAN, + CAPTURE_DR, + SHIFT_DR, + EXIT1_DR, + PAUSE_DR, + EXIT2_DR, + UPDATE_DR, + SELECT_IR_SCAN, + CAPTURE_IR, + SHIFT_IR, + EXIT1_IR, + PAUSE_IR, + EXIT2_IR, + UPDATE_IR, + _TAP_STATE_COUNT +} TAPState; + +#define TAP_CTRL_BYPASS_INST 0 + +/* clang-format on */ + +typedef TapDataHandler *tap_ctrl_data_reg_extender_t(uint64_t value); + +typedef struct TapCtrlRbbState { + DeviceState parent; + + TAPState state; /* Current state */ + + /* signals */ + bool trst; /* TAP controller reset */ + bool srst; /* System reset */ + bool tck; /* JTAG clock */ + bool tms; /* JTAG state machine selector */ + bool tdi; /* Register input */ + bool tdo; /* Register output */ + + /* registers */ + uint64_t ir; /* instruction register value */ + uint64_t ir_hold; /* IR hold register */ + uint64_t dr; /* current data register value */ + size_t dr_len; /* count of meaningful bits in dr */ + + /* handlers */ + TapDataHandler *tdh; /* Current data register handler */ + GHashTable *tdhtable; /* Registered handlers */ + + guint watch_tag; /* tracker for comm device change */ + + /* properties */ + CharBackend chr; + uint32_t idcode; /* TAP controller identifier */ + uint8_t ir_length; /* count of meaningful bits in ir */ + uint8_t idcode_inst; /* instruction to get ID code */ + bool enable_quit; /* whether VM quit can be remotely triggered */ +} TapCtrlRbbState; + +typedef struct _TAPRegisterState { + int base_reg; + int num_regs; + struct TAPRegisterState *next; +} TAPRegisterState; + +typedef struct _TAPProcess { + uint32_t pid; + bool attached; +} TAPProcess; + +#define STRINGIFY_(_val_) #_val_ +#define STRINGIFY(_val_) STRINGIFY_(_val_) +#define NAME_FSMSTATE(_st_) [_st_] = STRINGIFY(_st_) + +#define DEFAULT_JTAG_BITBANG_PORT "3335" +#define MAX_PACKET_LENGTH 4096u + +/* + * TAP controller state machine state/event matrix + * + * Current state -> Next States for either TMS == 0 or TMS == 1 + */ +static const TAPState TAPFSM[_TAP_STATE_COUNT][2] = { + [TEST_LOGIC_RESET] = { RUN_TEST_IDLE, TEST_LOGIC_RESET }, + [RUN_TEST_IDLE] = { RUN_TEST_IDLE, SELECT_DR_SCAN }, + [SELECT_DR_SCAN] = { CAPTURE_DR, SELECT_IR_SCAN }, + [CAPTURE_DR] = { SHIFT_DR, EXIT1_DR }, + [SHIFT_DR] = { SHIFT_DR, EXIT1_DR }, + [EXIT1_DR] = { PAUSE_DR, UPDATE_DR }, + [PAUSE_DR] = { PAUSE_DR, EXIT2_DR }, + [EXIT2_DR] = { SHIFT_DR, UPDATE_DR }, + [UPDATE_DR] = { RUN_TEST_IDLE, SELECT_DR_SCAN }, + [SELECT_IR_SCAN] = { CAPTURE_IR, TEST_LOGIC_RESET }, + [CAPTURE_IR] = { SHIFT_IR, EXIT1_IR }, + [SHIFT_IR] = { SHIFT_IR, EXIT1_IR }, + [EXIT1_IR] = { PAUSE_IR, UPDATE_IR }, + [PAUSE_IR] = { PAUSE_IR, EXIT2_IR }, + [EXIT2_IR] = { SHIFT_IR, UPDATE_IR }, + [UPDATE_IR] = { RUN_TEST_IDLE, SELECT_DR_SCAN } +}; + +static const char TAPFSM_NAMES[_TAP_STATE_COUNT][18U] = { + NAME_FSMSTATE(TEST_LOGIC_RESET), NAME_FSMSTATE(RUN_TEST_IDLE), + NAME_FSMSTATE(SELECT_DR_SCAN), NAME_FSMSTATE(CAPTURE_DR), + NAME_FSMSTATE(SHIFT_DR), NAME_FSMSTATE(EXIT1_DR), + NAME_FSMSTATE(PAUSE_DR), NAME_FSMSTATE(EXIT2_DR), + NAME_FSMSTATE(UPDATE_DR), NAME_FSMSTATE(SELECT_IR_SCAN), + NAME_FSMSTATE(CAPTURE_IR), NAME_FSMSTATE(SHIFT_IR), + NAME_FSMSTATE(EXIT1_IR), NAME_FSMSTATE(PAUSE_IR), + NAME_FSMSTATE(EXIT2_IR), NAME_FSMSTATE(UPDATE_IR), +}; + +static void tap_ctrl_rbb_idcode_capture(TapDataHandler *tdh); + +/* Common TAP instructions */ +static const TapDataHandler tap_ctrl_rbb_bypass = { + .name = "bypass", + .length = 1, + .value = 0, +}; + +static const TapDataHandler tap_ctrl_rbb_idcode = { + .name = "idcode", + .length = 32, + .capture = &tap_ctrl_rbb_idcode_capture, +}; + +/* + * TAP State Machine implementation + */ + +static void tap_ctrl_rbb_dump_register(const char *msg, const char *iname, + uint64_t value, size_t length) +{ + char buf[80]; + if (length > 64u) { + length = 64u; + } + unsigned ix = 0; + while (ix < length) { + buf[ix] = (char)('0' + ((value >> (length - ix - 1)) & 0b1)); + ix++; + } + buf[ix] = '\0'; + + if (iname) { + trace_tap_ctrl_rbb_idump_register(msg, iname, value, length, buf); + } else { + trace_tap_ctrl_rbb_dump_register(msg, value, length, buf); + } +} + +static bool tap_ctrl_rbb_has_data_handler(TapCtrlRbbState *tap, unsigned code) +{ + return (bool)g_hash_table_contains(tap->tdhtable, GINT_TO_POINTER(code)); +} + +static TapDataHandler * +tap_ctrl_rbb_get_data_handler(TapCtrlRbbState *tap, unsigned code) +{ + TapDataHandler *tdh; + tdh = (TapDataHandler *)g_hash_table_lookup(tap->tdhtable, + GINT_TO_POINTER(code)); + return tdh; +} + +static void tap_ctrl_rbb_idcode_capture(TapDataHandler *tdh) +{ + /* special case for ID code: opaque contains the ID code value */ + tdh->value = (uint64_t)(uintptr_t)tdh->opaque; +} + +static void tap_ctrl_rbb_tap_reset(TapCtrlRbbState *tap) +{ + tap->state = TEST_LOGIC_RESET; + tap->trst = false; + tap->srst = false; + tap->tck = false; + tap->tms = false; + tap->tdi = false; + tap->tdo = false; + tap->ir = tap->idcode_inst; + tap->ir_hold = tap->idcode_inst; + tap->dr = 0u; + tap->dr_len = 0u; + tap->tdh = tap_ctrl_rbb_get_data_handler(tap, tap->idcode_inst); + g_assert(tap->tdh); +} + +static void tap_ctrl_rbb_system_reset(TapCtrlRbbState *tap) +{ + Object *mc = qdev_get_machine(); + ObjectClass *oc = object_get_class(mc); + (void)tap; + + if (!object_class_dynamic_cast(oc, TYPE_RESETTABLE_INTERFACE)) { + qemu_log_mask(LOG_UNIMP, "%s: Machine %s is not resettable\n", __func__, + object_get_typename(mc)); + return; + } + + trace_tap_ctrl_rbb_system_reset(); + resettable_reset(mc, RESET_TYPE_COLD); +} + +static TAPState tap_ctrl_rbb_get_next_state(TapCtrlRbbState *tap, bool tms) +{ + tap->state = TAPFSM[tap->state][(unsigned)tms]; + return tap->state; +} + +static void tap_ctrl_rbb_capture_ir(TapCtrlRbbState *tap) +{ + tap->ir = tap->idcode_inst; +} + +static void tap_ctrl_rbb_shift_ir(TapCtrlRbbState *tap, bool tdi) +{ + tap->ir >>= 1u; + tap->ir |= ((uint64_t)(tdi)) << (tap->ir_length - 1u); +} + +static void tap_ctrl_rbb_update_ir(TapCtrlRbbState *tap) +{ + tap->ir_hold = tap->ir; + tap_ctrl_rbb_dump_register("Update IR", NULL, tap->ir_hold, tap->ir_length); +} + +static void tap_ctrl_rbb_capture_dr(TapCtrlRbbState *tap) +{ + TapDataHandler *prev = tap->tdh; + + if (tap->ir_hold >= (1 << tap->ir_length)) { + /* internal error, should never happen */ + error_setg(&error_fatal, "Invalid IR 0x%02x\n", (unsigned)tap->ir_hold); + g_assert_not_reached(); + } + + TapDataHandler *tdh = tap_ctrl_rbb_get_data_handler(tap, tap->ir_hold); + if (!tdh) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown IR 0x%02x\n", __func__, + (unsigned)tap->ir_hold); + tap->dr = 0; + return; + } + + if (tdh != prev) { + trace_tap_ctrl_rbb_select_dr(tdh->name, tap->ir_hold); + } + + tap->tdh = tdh; + tap->dr_len = tdh->length; + + if (tdh->capture) { + tdh->capture(tdh); + } + tap->dr = tdh->value; + tap_ctrl_rbb_dump_register("Capture DR", tap->tdh->name, tap->dr, + tap->dr_len); +} + +static void tap_ctrl_rbb_shift_dr(TapCtrlRbbState *tap, bool tdi) +{ + tap->dr >>= 1u; + tap->dr |= ((uint64_t)(tdi)) << (tap->dr_len - 1u); +} + +static void tap_ctrl_rbb_update_dr(TapCtrlRbbState *tap) +{ + tap_ctrl_rbb_dump_register("Update DR", tap->tdh->name, tap->dr, + tap->dr_len); + TapDataHandler *tdh = tap->tdh; + tdh->value = tap->dr; + if (tdh->update) { + tdh->update(tdh); + } +} + +static void tap_ctrl_rbb_step(TapCtrlRbbState *tap, bool tck, bool tms, + bool tdi) +{ + trace_tap_ctrl_rbb_step(tck, tms, tdi); + + if (tap->trst) { + return; + } + + if (!tap->tck && tck) { + /* Rising clock edge */ + if (tap->state == SHIFT_IR) { + tap_ctrl_rbb_shift_ir(tap, tap->tdi); + } else if (tap->state == SHIFT_DR) { + tap_ctrl_rbb_shift_dr(tap, tap->tdi); + } + TAPState prev = tap->state; + TAPState new = tap_ctrl_rbb_get_next_state(tap, tms); + if (prev != new) { + trace_tap_ctrl_rbb_change_state(TAPFSM_NAMES[prev], + TAPFSM_NAMES[new]); + } + } else { + /* Falling clock edge */ + switch (tap->state) { + case RUN_TEST_IDLE: + /* do nothing */ + break; + case TEST_LOGIC_RESET: + tap_ctrl_rbb_tap_reset(tap); + break; + case CAPTURE_DR: + tap_ctrl_rbb_capture_dr(tap); + break; + case SHIFT_DR: + tap->tdo = tap->dr & 0b1; + break; + case UPDATE_DR: + tap_ctrl_rbb_update_dr(tap); + break; + case CAPTURE_IR: + tap_ctrl_rbb_capture_ir(tap); + break; + case SHIFT_IR: + tap->tdo = tap->ir & 0b1; + break; + case UPDATE_IR: + tap_ctrl_rbb_update_ir(tap); + break; + default: + /* nothing to do on the other state transition */ + break; + } + } + tap->tck = tck; + tap->tdi = tdi; + tap->tms = tms; +} + +static void tap_ctrl_rbb_blink(TapCtrlRbbState *tap, bool light) {} + +static void tap_ctrl_rbb_read(TapCtrlRbbState *tap) +{ + trace_tap_ctrl_rbb_read(tap->tdo); +} + +static void tap_ctrl_rbb_quit(TapCtrlRbbState *tap) +{ + (void)tap; + + if (tap->enable_quit) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } else { + info_report("%s: JTAG termination disabled\n", __func__); + } +} + +static void tap_ctrl_rbb_write(TapCtrlRbbState *tap, bool tck, bool tms, + bool tdi) +{ + tap_ctrl_rbb_step(tap, tck, tms, tdi); +} + +static void tap_ctrl_rbb_reset_tap(TapCtrlRbbState *tap, bool trst, bool srst) +{ + trace_tap_ctrl_rbb_reset(trst, srst); + if (trst) { + tap_ctrl_rbb_tap_reset(tap); + } + if (srst) { + tap_ctrl_rbb_system_reset(tap); + } + tap->trst = trst; + tap->srst = srst; +} + +/* + * TAP Server implementation + */ + +static bool tap_ctrl_rbb_read_byte(TapCtrlRbbState *tap, uint8_t ch) +{ + switch ((char)ch) { + case 'B': + tap_ctrl_rbb_blink(tap, true); + break; + case 'b': + tap_ctrl_rbb_blink(tap, false); + break; + case 'R': + tap_ctrl_rbb_read(tap); + break; + case 'Q': + tap_ctrl_rbb_quit(tap); + break; + case '0': + tap_ctrl_rbb_write(tap, false, false, false); + break; + case '1': + tap_ctrl_rbb_write(tap, false, false, true); + break; + case '2': + tap_ctrl_rbb_write(tap, false, true, false); + break; + case '3': + tap_ctrl_rbb_write(tap, false, true, true); + break; + case '4': + tap_ctrl_rbb_write(tap, true, false, false); + break; + case '5': + tap_ctrl_rbb_write(tap, true, false, true); + break; + case '6': + tap_ctrl_rbb_write(tap, true, true, false); + break; + case '7': + tap_ctrl_rbb_write(tap, true, true, true); + break; + case 'r': + tap_ctrl_rbb_reset_tap(tap, false, false); + break; + case 's': + tap_ctrl_rbb_reset_tap(tap, false, true); + break; + case 't': + tap_ctrl_rbb_reset_tap(tap, true, false); + break; + case 'u': + tap_ctrl_rbb_reset_tap(tap, true, true); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unknown TAP code 0x%02x\n", __func__, + (unsigned)ch); + break; + } + + /* true if TDO level should be sent to the peer */ + return (int)ch == 'R'; +} + +static int tap_ctrl_rbb_chr_can_receive(void *opaque) +{ + TapCtrlRbbState *tap = (TapCtrlRbbState *)opaque; + + /* do not accept any input till a TAP controller is available */ + return qemu_chr_fe_backend_connected(&tap->chr) ? MAX_PACKET_LENGTH : 0; +} + +static void tap_ctrl_rbb_chr_receive(void *opaque, const uint8_t *buf, int size) +{ + TapCtrlRbbState *tap = (TapCtrlRbbState *)opaque; + + for (unsigned ix = 0; ix < size; ix++) { + if (tap_ctrl_rbb_read_byte(tap, buf[ix])) { + uint8_t outbuf[1] = { '0' + (unsigned)tap->tdo }; + qemu_chr_fe_write_all(&tap->chr, outbuf, (int)sizeof(outbuf)); + } + } +} + +static void tap_ctrl_rbb_chr_event_hander(void *opaque, QEMUChrEvent event) +{ + TapCtrlRbbState *tap = opaque; + + if (event == CHR_EVENT_OPENED) { + if (!qemu_chr_fe_backend_connected(&tap->chr)) { + return; + } + + tap_ctrl_rbb_tap_reset(tap); + } +} + +static gboolean +tap_ctrl_rbb_chr_watch_cb(void *do_not_use, GIOCondition cond, void *opaque) +{ + TapCtrlRbbState *tap = opaque; + (void)do_not_use; + (void)cond; + + tap->watch_tag = 0; + + return FALSE; +} + +static int tap_ctrl_rbb_chr_be_change(void *opaque) +{ + TapCtrlRbbState *tap = opaque; + + qemu_chr_fe_set_handlers(&tap->chr, &tap_ctrl_rbb_chr_can_receive, + &tap_ctrl_rbb_chr_receive, + &tap_ctrl_rbb_chr_event_hander, + &tap_ctrl_rbb_chr_be_change, tap, NULL, true); + + tap_ctrl_rbb_tap_reset(tap); + + if (tap->watch_tag > 0) { + g_source_remove(tap->watch_tag); + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) + tap->watch_tag = qemu_chr_fe_add_watch(&tap->chr, G_IO_OUT | G_IO_HUP, + &tap_ctrl_rbb_chr_watch_cb, tap); + } + + return 0; +} + +static void tap_ctrl_rbb_verify_handler(const TapCtrlRbbState *tap, + unsigned code, const char *name) +{ + if (code >= (1 << tap->ir_length)) { + error_setg(&error_fatal, "JTAG: Invalid IR code: 0x%x for %s", code, + name); + g_assert_not_reached(); + } +} + +static void +tap_ctrl_rbb_verify_handler_fn(gpointer key, gpointer value, gpointer user_data) +{ + unsigned code = GPOINTER_TO_INT(key); + const TapDataHandler *tdh = (const TapDataHandler *)value; + const TapCtrlRbbState *tap = (const TapCtrlRbbState *)user_data; + + tap_ctrl_rbb_verify_handler(tap, code, tdh->name); +} + +static void tap_ctrl_rbb_register_handler(TapCtrlRbbState *tap, unsigned code, + const TapDataHandler *tdh, bool check) +{ + if (check) { + tap_ctrl_rbb_verify_handler(tap, code, tdh->name); + } + + if (tap_ctrl_rbb_has_data_handler(tap, code)) { + warn_report("JTAG: IR code already registered: 0x%x", code); + /* resume and override */ + } + + TapDataHandler *ltdh = g_new0(TapDataHandler, 1u); + memcpy(ltdh, tdh, sizeof(*tdh)); + ltdh->name = g_strdup(tdh->name); + g_hash_table_insert(tap->tdhtable, GINT_TO_POINTER(code), ltdh); + + trace_tap_ctrl_rbb_register(code, ltdh->name); +} + +static void tap_ctrl_rbb_free_data_handler(gpointer entry) +{ + TapDataHandler *tdh = entry; + if (!entry) { + return; + } + g_free((char *)tdh->name); + g_free(tdh); +} + +static bool tap_ctrl_rbb_is_enabled(TapCtrlIf *dev) +{ + TapCtrlRbbState *tap = TAP_CTRL_RBB(dev); + + return qemu_chr_fe_backend_connected(&tap->chr); +} + +static int tap_ctrl_rbb_register_instruction(TapCtrlIf *dev, unsigned code, + const TapDataHandler *tdh) +{ + TapCtrlRbbState *tap = TAP_CTRL_RBB(dev); + + tap_ctrl_rbb_register_handler(tap, code, tdh, DEVICE(tap)->realized); + + return 0; +} + +static Property tap_ctrl_rbb_properties[] = { + DEFINE_PROP_UINT32("idcode", TapCtrlRbbState, idcode, 0), + DEFINE_PROP_UINT8("ir_length", TapCtrlRbbState, ir_length, 0), + DEFINE_PROP_UINT8("idcode_inst", TapCtrlRbbState, idcode_inst, 1u), + DEFINE_PROP_BOOL("quit", TapCtrlRbbState, enable_quit, true), + DEFINE_PROP_CHR("chardev", TapCtrlRbbState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tap_ctrl_rbb_realize(DeviceState *dev, Error **errp) +{ + TapCtrlRbbState *tap = TAP_CTRL_RBB(dev); + + if (tap->ir_length == 0 || tap->ir_length > 8u) { + error_setg(errp, "Unsupported IR length: %u", tap->ir_length); + return; + } + + if (tap->idcode == 0u) { + error_setg(errp, "Invalid IDCODE: 0x%x", tap->idcode); + return; + } + + if (tap->idcode_inst == TAP_CTRL_BYPASS_INST) { + error_setg(errp, "Invalid IDCODE instruction: 0x%x", tap->idcode_inst); + return; + } + + trace_tap_ctrl_rbb_realize(tap->ir_length, tap->idcode); + + /* + * Handlers may be registered before the TAP controller is configured. + * Need to check their configuration once the configuration is known + */ + g_hash_table_foreach(tap->tdhtable, &tap_ctrl_rbb_verify_handler_fn, + (gpointer)tap); + + size_t irslots = 1u << tap->ir_length; + tap_ctrl_rbb_register_handler(tap, TAP_CTRL_BYPASS_INST, + &tap_ctrl_rbb_bypass, true); + tap_ctrl_rbb_register_handler(tap, tap->idcode_inst, &tap_ctrl_rbb_idcode, + true); + tap_ctrl_rbb_register_handler(tap, irslots - 1u, &tap_ctrl_rbb_bypass, + true); + /* special case for ID code: opaque store the constant idcode value */ + TapDataHandler *tdh = tap_ctrl_rbb_get_data_handler(tap, tap->idcode_inst); + g_assert(tdh); + tdh->opaque = (void *)(uintptr_t)tap->idcode; + + qemu_chr_fe_set_handlers(&tap->chr, &tap_ctrl_rbb_chr_can_receive, + &tap_ctrl_rbb_chr_receive, + &tap_ctrl_rbb_chr_event_hander, + &tap_ctrl_rbb_chr_be_change, tap, NULL, true); + + tap_ctrl_rbb_tap_reset(tap); + + qemu_chr_fe_accept_input(&tap->chr); +} + +static void tap_ctrl_rbb_init(Object *obj) +{ + TapCtrlRbbState *tap = TAP_CTRL_RBB(obj); + + tap->tdhtable = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + tap_ctrl_rbb_free_data_handler); +} + +static void tap_ctrl_rbb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + (void)data; + + dc->realize = &tap_ctrl_rbb_realize; + device_class_set_props(dc, tap_ctrl_rbb_properties); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + TapCtrlIfClass *tcc = TAP_CTRL_IF_CLASS(klass); + tcc->is_enabled = &tap_ctrl_rbb_is_enabled; + tcc->register_instruction = &tap_ctrl_rbb_register_instruction; +} + +static const TypeInfo tap_ctrl_rbb_info = { + .name = TYPE_TAP_CTRL_RBB, + .parent = TYPE_DEVICE, + .instance_size = sizeof(TapCtrlRbbState), + .instance_init = &tap_ctrl_rbb_init, + .class_init = &tap_ctrl_rbb_class_init, + .interfaces = + (InterfaceInfo[]){ + { TYPE_TAP_CTRL_IF }, + {}, + }, +}; + +static void register_types(void) +{ + type_register_static(&tap_ctrl_rbb_info); +} + +type_init(register_types); diff --git a/hw/jtag/trace-events b/hw/jtag/trace-events index 7a3679a1e4414..ab419c3454759 100644 --- a/hw/jtag/trace-events +++ b/hw/jtag/trace-events @@ -1,13 +1,13 @@ -# jtag_bitbang.c +# tap_ctrl_rbb.c -jtag_tapctrl_change(const char *msg, const char *value) "%s %s" -jtag_tapctrl_change_state(const char *prev, const char *new) "%s -> %s" -jtag_tapctrl_dump_register(const char *msg, uint64_t val, size_t len, const char *buf) "%s: 0x%" PRIx64 "/%zu [b%s]" -jtag_tapctrl_idump_register(const char *msg, const char *iname, uint64_t val, size_t len, const char *buf) "%s (%s): 0x%" PRIx64 "/%zu [b%s]" -jtag_tapctrl_init(size_t irlen, uint32_t ircode) "irlength %zu, idcode 0x%08x" -jtag_tapctrl_read(bool tdo) "tdo %u" -jtag_tapctrl_register(unsigned code, const char *name) "register 0x%x: %s" -jtag_tapctrl_reset(bool tap, bool sys) "tap: %u, system: %u" -jtag_tapctrl_select_dr(const char *name, uint64_t value) "Select DR %s 0x%02" PRIx64 -jtag_tapctrl_step(bool tck, bool tms, bool tdi) "tck:%u tms:%u tdi:%u" -jtag_tapctrl_system_reset(void) "SYSTEM RESET" +tap_ctrl_rbb_change(const char *msg, const char *value) "%s %s" +tap_ctrl_rbb_change_state(const char *prev, const char *new) "%s -> %s" +tap_ctrl_rbb_dump_register(const char *msg, uint64_t val, size_t len, const char *buf) "%s: 0x%" PRIx64 "/%zu [b%s]" +tap_ctrl_rbb_idump_register(const char *msg, const char *iname, uint64_t val, size_t len, const char *buf) "%s (%s): 0x%" PRIx64 "/%zu [b%s]" +tap_ctrl_rbb_read(bool tdo) "tdo %u" +tap_ctrl_rbb_realize(size_t irlen, uint32_t ircode) "irlength %zu, idcode 0x%08x" +tap_ctrl_rbb_register(unsigned code, const char *name) "register 0x%x: %s" +tap_ctrl_rbb_reset(bool tap, bool sys) "tap: %u, system: %u" +tap_ctrl_rbb_select_dr(const char *name, uint64_t value) "Select DR %s 0x%02" PRIx64 +tap_ctrl_rbb_step(bool tck, bool tms, bool tdi) "tck:%u tms:%u tdi:%u" +tap_ctrl_rbb_system_reset(void) "SYSTEM RESET" diff --git a/hw/misc/pulp_rv_dm.c b/hw/misc/pulp_rv_dm.c index 7445cda044608..96450838c1910 100644 --- a/hw/misc/pulp_rv_dm.c +++ b/hw/misc/pulp_rv_dm.c @@ -37,11 +37,11 @@ #include "qemu/timer.h" #include "qemu/typedefs.h" #include "qapi/error.h" -#include "exec/jtagstub.h" #include "exec/memattrs.h" #include "hw/boards.h" #include "hw/core/cpu.h" #include "hw/irq.h" +#include "hw/jtag/tap_ctrl.h" #include "hw/loader.h" #include "hw/misc/pulp_rv_dm.h" #include "hw/opentitan/ot_alert.h" diff --git a/hw/opentitan/ot_dm_tl.c b/hw/opentitan/ot_dm_tl.c index 3ee6d9db169a9..1b831792782b4 100644 --- a/hw/opentitan/ot_dm_tl.c +++ b/hw/opentitan/ot_dm_tl.c @@ -26,7 +26,6 @@ */ #include "qemu/osdep.h" -#include #include "qapi/error.h" #include "exec/memory.h" #include "hw/opentitan/ot_address_space.h" @@ -45,6 +44,7 @@ struct OtDMTLState { hwaddr tl_offset; uint32_t value; MemTxAttrs attrs; + char *dev_name; bool dtm_ok; /* DTM is available */ RISCVDTMState *dtm; @@ -65,8 +65,13 @@ ot_dm_tl_write_rq(RISCVDebugDeviceState *dev, uint32_t addr, uint32_t value) { OtDMTLState *dmtl = OT_DM_TL(dev); + if (!dmtl->dtm_ok) { + trace_ot_dm_tl_dtm_not_available(dmtl->dev_name); + return RISCV_DEBUG_FAILED; + } + if (addr >= dmtl->dmi_size) { - trace_ot_dm_tl_invalid_addr(addr); + trace_ot_dm_tl_invalid_addr(dmtl->dev_name, addr); return RISCV_DEBUG_FAILED; } @@ -78,7 +83,7 @@ ot_dm_tl_write_rq(RISCVDebugDeviceState *dev, uint32_t addr, uint32_t value) res = address_space_rw(dmtl->as, dmtl->tl_base + dmtl->tl_offset, dmtl->attrs, &value, sizeof(value), true); - trace_ot_dm_tl_update(addr, value, "write", res); + trace_ot_dm_tl_update(dmtl->dev_name, addr, value, "write", res); return (res == MEMTX_OK) ? RISCV_DEBUG_NOERR : RISCV_DEBUG_FAILED; } @@ -88,8 +93,13 @@ ot_dm_tl_read_rq(RISCVDebugDeviceState *dev, uint32_t addr) { OtDMTLState *dmtl = OT_DM_TL(dev); + if (!dmtl->dtm_ok) { + trace_ot_dm_tl_dtm_not_available(dmtl->dev_name); + return RISCV_DEBUG_FAILED; + } + if (addr >= dmtl->dmi_size) { - trace_ot_dm_tl_invalid_addr(addr); + trace_ot_dm_tl_invalid_addr(dmtl->dev_name, addr); return RISCV_DEBUG_FAILED; } @@ -102,7 +112,7 @@ ot_dm_tl_read_rq(RISCVDebugDeviceState *dev, uint32_t addr) address_space_rw(dmtl->as, dmtl->tl_base + dmtl->tl_offset, dmtl->attrs, &dmtl->value, sizeof(dmtl->value), false); - trace_ot_dm_tl_update(addr, 0, "read", res); + trace_ot_dm_tl_update(dmtl->dev_name, addr, 0, "read", res); return (res == MEMTX_OK) ? RISCV_DEBUG_NOERR : RISCV_DEBUG_FAILED; } @@ -113,7 +123,7 @@ static uint32_t ot_dm_tl_read_value(RISCVDebugDeviceState *dev) uint32_t value = dmtl->value; - trace_ot_dm_tl_capture(dmtl->tl_offset, value); + trace_ot_dm_tl_capture(dmtl->dev_name, dmtl->tl_offset, value); return value; } @@ -137,9 +147,11 @@ static void ot_dm_tl_reset(DeviceState *dev) g_assert(dmtl->dtm != NULL); g_assert(dmtl->dmi_size); - dmtl->dtm_ok = - riscv_dtm_register_dm(DEVICE(dmtl->dtm), RISCV_DEBUG_DEVICE(dev), - dmtl->dmi_addr, dmtl->dmi_size); + if (!dmtl->dtm_ok) { + dmtl->dtm_ok = + riscv_dtm_register_dm(DEVICE(dmtl->dtm), RISCV_DEBUG_DEVICE(dev), + dmtl->dmi_addr, dmtl->dmi_size); + } if (dmtl->dtm_ok) { Object *soc = OBJECT(dev)->parent; @@ -165,6 +177,12 @@ static void ot_dm_tl_realize(DeviceState *dev, Error **errp) } else { dmtl->attrs = MEMTXATTRS_WITH_ROLE(dmtl->role); } + + if (dmtl->tl_dev) { + dmtl->dev_name = g_strdup(object_get_typename(OBJECT(dmtl->tl_dev))); + } else { + dmtl->dev_name = g_strdup(""); + } } static void ot_dm_tl_init(Object *obj) diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 981592270b208..53d1756451745 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -99,9 +99,10 @@ ot_dev_proxy_write_reg(const char *prefix, unsigned devix, unsigned offset, unsi # ot_dm_tl.c -ot_dm_tl_capture(uint32_t addr, uint32_t value) "0x%x: 0x%08x" -ot_dm_tl_invalid_addr(uint32_t addr) "0x%x" -ot_dm_tl_update(uint32_t addr, uint32_t value, const char *msg, uint32_t res) "0x%x: 0x%x (%s): %u" +ot_dm_tl_capture(const char *devname, uint32_t addr, uint32_t value) "%s: @ 0x%x: 0x%08x" +ot_dm_tl_invalid_addr(const char *devname, uint32_t addr) "%s: 0x%x" +ot_dm_tl_dtm_not_available(const char *devname) "%s" +ot_dm_tl_update(const char *devname, uint32_t addr, uint32_t value, const char *msg, uint32_t res) "%s @ 0x%x: 0x%x (%s): %u" # ot_dma.c diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index c17a363f0c7d8..2f44c1c338703 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -51,6 +51,7 @@ config OT_DARJEELING select RISCV_DTM select SIFIVE_PLIC select SPLIT_IRQ + select TAP_CTRL_RBB config OT_EARLGREY bool @@ -87,6 +88,7 @@ config OT_EARLGREY select RISCV_DM select RISCV_DTM select SIFIVE_PLIC + select TAP_CTRL_RBB config IBEXDEMO bool @@ -100,6 +102,7 @@ config IBEXDEMO select RISCV_DM select RISCV_DTM select ST7735 + select TAP_CTRL_RBB select UNIMP config MICROCHIP_PFSOC @@ -153,6 +156,7 @@ config RISCV_DM bool config RISCV_DTM + select TAP_CTRL bool config SHAKTI_C diff --git a/hw/riscv/dm.c b/hw/riscv/dm.c index f8072989712b2..8ad187f510bff 100644 --- a/hw/riscv/dm.c +++ b/hw/riscv/dm.c @@ -47,33 +47,23 @@ */ #include "qemu/osdep.h" -#include "qemu/bitmap.h" #include "qemu/compiler.h" -#include "qemu/guest-random.h" #include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/timer.h" #include "qemu/typedefs.h" #include "qapi/error.h" #include "disas/dis-asm.h" -#include "exec/address-spaces.h" #include "exec/cpu_ldst.h" -#include "exec/jtagstub.h" #include "hw/boards.h" #include "hw/core/cpu.h" -#include "hw/irq.h" -#include "hw/qdev-properties-system.h" +#include "hw/jtag/tap_ctrl.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/debug.h" #include "hw/riscv/dm.h" #include "hw/riscv/dtm.h" -#include "hw/sysbus.h" -#include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "target/riscv/cpu.h" -#include "trace-target_riscv.h" #include "trace.h" diff --git a/hw/riscv/dtm.c b/hw/riscv/dtm.c index 66c734b54afd1..cdf45d28dc424 100644 --- a/hw/riscv/dtm.c +++ b/hw/riscv/dtm.c @@ -38,35 +38,18 @@ */ #include "qemu/osdep.h" -#include "qemu/bitmap.h" #include "qemu/compiler.h" -#include "qemu/guest-random.h" #include "qemu/log.h" -#include "qemu/main-loop.h" -#include "qemu/timer.h" #include "qemu/typedefs.h" #include "qapi/error.h" -#include "disas/dis-asm.h" -#include "exec/address-spaces.h" -#include "exec/cpu_ldst.h" -#include "exec/jtagstub.h" -#include "hw/boards.h" -#include "hw/core/cpu.h" -#include "hw/irq.h" -#include "hw/qdev-properties-system.h" +#include "hw/jtag/tap_ctrl.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/riscv/debug.h" #include "hw/riscv/dtm.h" -#include "hw/sysbus.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" #include "sysemu/runstate.h" -#include "target/riscv/cpu.h" -#include "trace-target_riscv.h" #include "trace.h" - /* * Register definitions */ @@ -122,9 +105,9 @@ struct RISCVDTMState { uint32_t address; /* last updated address */ RISCVDebugResult dmistat; /* Operation result */ bool cmd_busy; /* A command is being executed */ - bool jtag_ok; /* JTAG server initialized */ /* properties */ + DeviceState *tap_ctrl; unsigned abits; /* address bit count */ }; @@ -136,10 +119,10 @@ static void riscv_dtm_reset(DeviceState *dev); static RISCVDebugModule* riscv_dtm_get_dm(RISCVDTMState *s, uint32_t addr); static void riscv_dtm_sort_dms(RISCVDTMState *s); -static void riscv_dtm_tap_dtmcs_capture(TAPDataHandler *tdh); -static void riscv_dtm_tap_dtmcs_update(TAPDataHandler *tdh); -static void riscv_dtm_tap_dmi_capture(TAPDataHandler *tdh); -static void riscv_dtm_tap_dmi_update(TAPDataHandler *tdh); +static void riscv_dtm_tap_dtmcs_capture(TapDataHandler *tdh); +static void riscv_dtm_tap_dtmcs_update(TapDataHandler *tdh); +static void riscv_dtm_tap_dmi_capture(TapDataHandler *tdh); +static void riscv_dtm_tap_dmi_update(TapDataHandler *tdh); /* * Constants @@ -149,7 +132,7 @@ static void riscv_dtm_tap_dmi_update(TAPDataHandler *tdh); #define RISCVDMI_DTMCS_IR 0x10u #define RISCVDMI_DMI_IR 0x11u -static const TAPDataHandler RISCVDMI_DTMCS = { +static const TapDataHandler RISCVDMI_DTMCS = { .name = "dtmcs", .length = 32u, .value = RISCV_DEBUG_DMI_VERSION, /* abits updated at runtime */ @@ -157,7 +140,7 @@ static const TAPDataHandler RISCVDMI_DTMCS = { .update = &riscv_dtm_tap_dtmcs_update, }; -static const TAPDataHandler RISCVDMI_DMI = { +static const TapDataHandler RISCVDMI_DMI = { .name = "dmi", /* data, op; abits updated at runtime */ .length = R_DMI_OP_LENGTH + R_DMI_DATA_LENGTH, @@ -210,6 +193,17 @@ bool riscv_dtm_register_dm(DeviceState *dev, RISCVDebugDeviceState *dbgdev, s->abits); } + if (!s->tap_ctrl) { + xtrace_riscv_dtm_info("TAP controller not available", 0); + return false; + } + + TapCtrlIfClass *tapcls = TAP_CTRL_IF_GET_CLASS(s->tap_ctrl); + TapCtrlIf *tap = TAP_CTRL_IF(s->tap_ctrl); + + /* may fail if TAP controller is not active */ + bool tap_ok = tapcls->is_enabled(tap); + RISCVDebugModule *node; unsigned count = 0; QLIST_FOREACH(node, &s->dms, entry) { @@ -217,7 +211,7 @@ bool riscv_dtm_register_dm(DeviceState *dev, RISCVDebugDeviceState *dbgdev, if ((node->dev == dbgdev) && (node->base == base_addr) && (node->size == size)) { /* already registered */ - return s->jtag_ok; + return tap_ok; } if (base_addr > (node->base + node->size - 1u)) { continue; @@ -241,18 +235,18 @@ bool riscv_dtm_register_dm(DeviceState *dev, RISCVDebugDeviceState *dbgdev, s->last_dm = dm; trace_riscv_dtm_register_dm(count, base_addr, base_addr + size - 1u, - s->jtag_ok); + tap_ok); riscv_dtm_sort_dms(s); - return s->jtag_ok; + return tap_ok; } /* -------------------------------------------------------------------------- */ /* DTMCS/DMI implementation */ /* -------------------------------------------------------------------------- */ -static void riscv_dtm_tap_dtmcs_capture(TAPDataHandler *tdh) +static void riscv_dtm_tap_dtmcs_capture(TapDataHandler *tdh) { RISCVDTMState *s = tdh->opaque; @@ -260,7 +254,7 @@ static void riscv_dtm_tap_dtmcs_capture(TAPDataHandler *tdh) ((uint64_t)s->dmistat << 10u); /* see DMI op result */ } -static void riscv_dtm_tap_dtmcs_update(TAPDataHandler *tdh) +static void riscv_dtm_tap_dtmcs_update(TapDataHandler *tdh) { RISCVDTMState *s = tdh->opaque; if (tdh->value & (1u << 16u)) { @@ -274,7 +268,7 @@ static void riscv_dtm_tap_dtmcs_update(TAPDataHandler *tdh) } } -static void riscv_dtm_tap_dmi_capture(TAPDataHandler *tdh) +static void riscv_dtm_tap_dmi_capture(TapDataHandler *tdh) { RISCVDTMState *s = tdh->opaque; @@ -304,7 +298,7 @@ static void riscv_dtm_tap_dmi_capture(TAPDataHandler *tdh) ((uint64_t)(s->dmistat & 0b11)); } -static void riscv_dtm_tap_dmi_update(TAPDataHandler *tdh) +static void riscv_dtm_tap_dmi_update(TapDataHandler *tdh) { RISCVDTMState *s = tdh->opaque; @@ -357,25 +351,32 @@ static void riscv_dtm_tap_dmi_update(TAPDataHandler *tdh) static void riscv_dtm_register_tap_handlers(RISCVDTMState *s) { + if (!s->tap_ctrl) { + return; + } + + TapCtrlIfClass *tapcls = TAP_CTRL_IF_GET_CLASS(s->tap_ctrl); + TapCtrlIf *tap = TAP_CTRL_IF(s->tap_ctrl); + /* * copy the template to update the opaque value * no lifetime issue as the data handler is copied by the TAP controller */ - TAPDataHandler tdh; + TapDataHandler tdh; - memcpy(&tdh, &RISCVDMI_DTMCS, sizeof(TAPDataHandler)); + memcpy(&tdh, &RISCVDMI_DTMCS, sizeof(TapDataHandler)); tdh.value |= s->abits << 4u; /* add address bit count */ tdh.opaque = s; - if (jtag_register_handler(RISCVDMI_DTMCS_IR, &tdh)) { + if (tapcls->register_instruction(tap, RISCVDMI_DTMCS_IR, &tdh)) { xtrace_riscv_dtm_error("cannot register DMTCS"); return; } - memcpy(&tdh, &RISCVDMI_DMI, sizeof(TAPDataHandler)); + memcpy(&tdh, &RISCVDMI_DMI, sizeof(TapDataHandler)); tdh.length += s->abits; /* add address bit count */ tdh.opaque = s; /* the data handler is copied by the TAP controller */ - if (jtag_register_handler(RISCVDMI_DMI_IR, &tdh)) { + if (tapcls->register_instruction(tap, RISCVDMI_DMI_IR, &tdh)) { xtrace_riscv_dtm_error("cannot register DMI"); return; } @@ -455,6 +456,8 @@ static void riscv_dtm_sort_dms(RISCVDTMState *s) static Property riscv_dtm_properties[] = { DEFINE_PROP_UINT32("abits", RISCVDTMState, abits, 0x7u), + DEFINE_PROP_LINK("tap_ctrl", RISCVDTMState, tap_ctrl, TYPE_DEVICE, + DeviceState *), DEFINE_PROP_END_OF_LIST(), }; @@ -475,12 +478,7 @@ static void riscv_dtm_realize(DeviceState *dev, Error **errp) return; } - /* may fail if no JTAG server is active */ - s->jtag_ok = jtag_tap_enabled(); - - if (s->jtag_ok) { - (void)riscv_dtm_register_tap_handlers(s); - } + riscv_dtm_register_tap_handlers(s); } static void riscv_dtm_init(Object *obj) diff --git a/hw/riscv/ibexdemo.c b/hw/riscv/ibexdemo.c index b611c0c26ec69..eba20dafe468b 100644 --- a/hw/riscv/ibexdemo.c +++ b/hw/riscv/ibexdemo.c @@ -20,14 +20,11 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qemu/units.h" #include "qapi/error.h" #include "qapi/qmp/qlist.h" +#include "chardev/chardev-internal.h" #include "cpu.h" -#include "elf.h" #include "exec/address-spaces.h" -#include "exec/jtagstub.h" #include "hw/boards.h" #include "hw/display/st7735.h" #include "hw/ibexdemo/ibexdemo_gpio.h" @@ -35,7 +32,8 @@ #include "hw/ibexdemo/ibexdemo_spi.h" #include "hw/ibexdemo/ibexdemo_timer.h" #include "hw/ibexdemo/ibexdemo_uart.h" -#include "hw/loader.h" +#include "hw/jtag/tap_ctrl.h" +#include "hw/jtag/tap_ctrl_rbb.h" #include "hw/misc/pulp_rv_dm.h" #include "hw/misc/unimp.h" #include "hw/qdev-properties.h" @@ -56,6 +54,8 @@ static void ibexdemo_soc_gpio_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ibexdemo_soc_hart_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); +static void ibexdemo_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ibexdemo_soc_uart_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); @@ -89,8 +89,9 @@ enum IbexDemoSocDevice { IBEXDEMO_SOC_DEV_HART, IBEXDEMO_SOC_DEV_PWM, IBEXDEMO_SOC_DEV_RV_DM, - IBEXDEMO_SOC_DEV_SIMCTRL, + IBEXDEMO_SOC_DEV_SIM_CTRL, IBEXDEMO_SOC_DEV_SPI, + IBEXDEMO_SOC_DEV_TAP_CTRL, IBEXDEMO_SOC_DEV_TIMER, IBEXDEMO_SOC_DEV_UART, }; @@ -138,8 +139,19 @@ static const IbexDeviceDef ibexdemo_soc_devices[] = { PULP_RV_DM_ROM_BASE + PULP_RV_DM_EXCEPTION_OFFSET) ), }, + [IBEXDEMO_SOC_DEV_TAP_CTRL] = { + .type = TYPE_TAP_CTRL_RBB, + .cfg = &ibexdemo_soc_tap_ctrl_configure, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("ir_length", IBEX_TAP_IR_LENGTH), + IBEX_DEV_UINT_PROP("idcode", IBEXDEMO_TAP_IDCODE) + ), + }, [IBEXDEMO_SOC_DEV_DTM] = { .type = TYPE_RISCV_DTM, + .link = IBEXDEVICELINKDEFS( + IBEXDEMO_SOC_DEVLINK("tap_ctrl", TAP_CTRL) + ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("abits", 7u) ), @@ -183,7 +195,7 @@ static const IbexDeviceDef ibexdemo_soc_devices[] = { IBEXDEMO_DM_CONNECTION(IBEXDEMO_SOC_DEV_DM, 3) ), }, - [IBEXDEMO_SOC_DEV_SIMCTRL] = { + [IBEXDEMO_SOC_DEV_SIM_CTRL] = { .type = TYPE_IBEXDEMO_SIMCTRL, .memmap = MEMMAPENTRIES( { .base = 0x00020000u, .size = 0x0400u } @@ -291,6 +303,20 @@ static void ibexdemo_soc_hart_configure( qdev_prop_set_uint64(dev, "resetvec", s->resetvec); } +static void ibexdemo_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("taprbb"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + static void ibexdemo_soc_uart_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { @@ -354,8 +380,6 @@ static void ibexdemo_soc_init(Object *obj) { IbexDemoSoCState *s = RISCV_IBEXDEMO_SOC(obj); - jtag_configure_tap(IBEX_TAP_IR_LENGTH, IBEXDEMO_TAP_IDCODE); - s->devices = ibex_create_devices(ibexdemo_soc_devices, ARRAY_SIZE(ibexdemo_soc_devices), DEVICE(s)); diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index cc0cbb172a93d..5daee967c006b 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -29,10 +29,11 @@ #include "qapi/qmp/qlist.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/jtagstub.h" #include "hw/boards.h" #include "hw/core/split-irq.h" #include "hw/intc/sifive_plic.h" +#include "hw/jtag/tap_ctrl.h" +#include "hw/jtag/tap_ctrl_rbb.h" #include "hw/misc/pulp_rv_dm.h" #include "hw/misc/unimp.h" #include "hw/opentitan/ot_address_space.h" @@ -89,6 +90,8 @@ static void ot_dj_soc_hart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_dj_soc_otp_ctrl_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); +static void ot_dj_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_dj_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); @@ -152,6 +155,7 @@ enum OtDjSocDevice { OT_DJ_SOC_DEV_SRAM_MAIN, OT_DJ_SOC_DEV_SRAM_MBX, OT_DJ_SOC_DEV_SRAM_RET, + OT_DJ_SOC_DEV_TAP_CTRL, OT_DJ_SOC_DEV_TIMER, OT_DJ_SOC_DEV_UART0, /* IRQ splitters, i.e. 1-to-N signal dispatchers */ @@ -488,8 +492,19 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { IBEX_DEV_BOOL_PROP("start-powered-off", true) ), }, + [OT_DJ_SOC_DEV_TAP_CTRL] = { + .type = TYPE_TAP_CTRL_RBB, + .cfg = &ot_dj_soc_tap_ctrl_configure, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("ir_length", IBEX_TAP_IR_LENGTH), + IBEX_DEV_UINT_PROP("idcode", DARJEELING_TAP_IDCODE) + ), + }, [OT_DJ_SOC_DEV_DTM] = { .type = TYPE_RISCV_DTM, + .link = IBEXDEVICELINKDEFS( + OT_DJ_SOC_DEVLINK("tap_ctrl", TAP_CTRL) + ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("abits", 12u) ), @@ -1439,6 +1454,20 @@ static void ot_dj_soc_otp_ctrl_configure( } } +static void ot_dj_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("taprbb"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + static void ot_dj_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { @@ -1602,8 +1631,6 @@ static void ot_dj_soc_init(Object *obj) { OtDjSoCState *s = RISCV_OT_DJ_SOC(obj); - jtag_configure_tap(IBEX_TAP_IR_LENGTH, DARJEELING_TAP_IDCODE); - s->devices = ibex_create_devices(ot_dj_soc_devices, ARRAY_SIZE(ot_dj_soc_devices), DEVICE(s)); } diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 21b16c0717dc4..db6f3bfa80451 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -28,9 +28,10 @@ #include "qapi/qmp/qlist.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/jtagstub.h" #include "hw/boards.h" #include "hw/intc/sifive_plic.h" +#include "hw/jtag/tap_ctrl.h" +#include "hw/jtag/tap_ctrl_rbb.h" #include "hw/misc/pulp_rv_dm.h" #include "hw/misc/unimp.h" #include "hw/opentitan/ot_aes.h" @@ -81,6 +82,8 @@ static void ot_eg_soc_hart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_eg_soc_otp_ctrl_configure( DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); +static void ot_eg_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); @@ -131,6 +134,7 @@ enum OtEGSocDevice { OT_EG_SOC_DEV_SPI_HOST1, OT_EG_SOC_DEV_SRAM_MAIN_CTRL, OT_EG_SOC_DEV_SYSRST_CTRL, + OT_EG_SOC_DEV_TAP_CTRL, OT_EG_SOC_DEV_TIMER, OT_EG_SOC_DEV_UART0, OT_EG_SOC_DEV_UART1, @@ -269,8 +273,19 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { IBEX_DEV_BOOL_PROP("start-powered-off", true) ), }, + [OT_EG_SOC_DEV_TAP_CTRL] = { + .type = TYPE_TAP_CTRL_RBB, + .cfg = &ot_eg_soc_tap_ctrl_configure, + .prop = IBEXDEVICEPROPDEFS( + IBEX_DEV_UINT_PROP("ir_length", IBEX_TAP_IR_LENGTH), + IBEX_DEV_UINT_PROP("idcode", EG_TAP_IDCODE) + ), + }, [OT_EG_SOC_DEV_DTM] = { .type = TYPE_RISCV_DTM, + .link = IBEXDEVICELINKDEFS( + OT_EG_SOC_DEVLINK("tap_ctrl", TAP_CTRL) + ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("abits", 7u) ), @@ -1063,6 +1078,20 @@ static void ot_eg_soc_otp_ctrl_configure( } } +static void ot_eg_soc_tap_ctrl_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("taprbb"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { @@ -1143,8 +1172,6 @@ static void ot_eg_soc_init(Object *obj) { OtEGSoCState *s = RISCV_OT_EG_SOC(obj); - jtag_configure_tap(IBEX_TAP_IR_LENGTH, EG_TAP_IDCODE); - s->devices = ibex_create_devices(ot_eg_soc_devices, ARRAY_SIZE(ot_eg_soc_devices), DEVICE(s)); } diff --git a/include/hw/jtag/jtagstub.h b/include/hw/jtag/tap_ctrl.h similarity index 61% rename from include/hw/jtag/jtagstub.h rename to include/hw/jtag/tap_ctrl.h index e4175a95a2694..60ea9fa478c0c 100644 --- a/include/hw/jtag/jtagstub.h +++ b/include/hw/jtag/tap_ctrl.h @@ -1,5 +1,5 @@ /* - * QEMU OpenTitan JTAG TAP controller + * QEMU JTAG TAP controller * * Copyright (c) 2022-2024 Rivos, Inc. * Author(s): @@ -24,21 +24,21 @@ * THE SOFTWARE. */ -#ifndef JTAGSTUB_H -#define JTAGSTUB_H +#ifndef HW_JTAG_TAP_CTRL_H +#define HW_JTAG_TAP_CTRL_H -#include "qemu/osdep.h" +#include "qom/object.h" -struct _TAPDataHandler; -typedef struct _TAPDataHandler TAPDataHandler; +struct _TapDataHandler; +typedef struct _TapDataHandler TapDataHandler; -struct _TAPDataHandler { +struct _TapDataHandler { const char *name; /**< Name */ size_t length; /**< Data register length */ uint64_t value; /**< Capture/Update value */ void *opaque; /**< Arbitrary data */ - void (*capture)(TAPDataHandler *tdh); - void (*update)(TAPDataHandler *tdh); + void (*capture)(TapDataHandler *tdh); + void (*update)(TapDataHandler *tdh); }; #define JTAG_MEMTX_REQUESTER_ID UINT16_MAX @@ -63,39 +63,32 @@ struct _TAPDataHandler { #define JEDEC_MANUFACTURER_ID(_tbl_, _id_) \ (((((_tbl_) - 1u) & 0xfu) << 7u) | ((_id_) & 0x7fu)) -/* - * Start the JTAG server - * @port_or_device: connection spec for JTAG - */ -int jtagserver_start(const char *port_or_device); +#define TYPE_TAP_CTRL_IF "tap-ctrl-interface" +typedef struct TapCtrlIfClass TapCtrlIfClass; +DECLARE_CLASS_CHECKERS(TapCtrlIfClass, TAP_CTRL_IF, TYPE_TAP_CTRL_IF) +#define TAP_CTRL_IF(_obj_) INTERFACE_CHECK(TapCtrlIf, (_obj_), TYPE_TAP_CTRL_IF) -/* - * Exit JTAG server - */ -void jtagserver_exit(void); +typedef struct TapCtrlIf TapCtrlIf; -/* - * Configure the JTAG TAP controller - * - * @irlength the length in bits of the instruction register - * @idcode the unique identifier code of the device - */ -void jtag_configure_tap(size_t irlength, uint32_t idcode); +struct TapCtrlIfClass { + InterfaceClass parent_class; -/** - * Report whether TAP is configured and available. - * - * @return @c true if the TAP can be used. - */ -bool jtag_tap_enabled(void); + /** + * Report whether TAP controller is enabled. + * + * @return @c true if the TAP can be used. + */ + bool (*is_enabled)(TapCtrlIf *dev); -/* - * Register TAP data handler - * - * @code instruction code for which to register the handler - * @tdh TAP data handler to register - * @return non-zero on error - */ -int jtag_register_handler(unsigned code, const TAPDataHandler *tdh); + /* + * Register instruction support on the TAP controller + * + * @code instruction code for which to register the handler + * @tdh TAP data handler to register + * @return non-zero on error + */ + int (*register_instruction)(TapCtrlIf *dev, unsigned code, + const TapDataHandler *tdh); +}; -#endif // JTAGSTUB_H +#endif // HW_JTAG_TAP_CTRL_H diff --git a/include/hw/jtag/tap_ctrl_rbb.h b/include/hw/jtag/tap_ctrl_rbb.h new file mode 100644 index 0000000000000..a08caaa4f0b3d --- /dev/null +++ b/include/hw/jtag/tap_ctrl_rbb.h @@ -0,0 +1,35 @@ +/* + * QEMU JTAG TAP controller for the OpenOCD/Spike Remote BigBang protocol + * + * Copyright (c) 2024 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_JTAG_TAP_CTRL_RBB_H +#define HW_JTAG_TAP_CTRL_RBB_H + +#include "qom/object.h" + +#define TYPE_TAP_CTRL_RBB "tap-ctrl-rbb" +OBJECT_DECLARE_SIMPLE_TYPE(TapCtrlRbbState, TAP_CTRL_RBB) + +#endif /* HW_JTAG_TAP_CTRL_RBB_H */ From b4f5a0d405c43b0f8d3babe52b7d985b11858870 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 22 May 2024 16:04:47 +0200 Subject: [PATCH 32/65] [ot] docs/opentitan: update JTAG/TAP controller related documentation Signed-off-by: Emmanuel Blot --- docs/opentitan/dtm.md | 7 ++++--- docs/opentitan/jtag-dm.md | 14 +++----------- docs/opentitan/jtagmbx.md | 14 +++----------- docs/opentitan/lc_ctrl_dmi.md | 14 +++----------- docs/opentitan/otpdm.md | 7 ++++--- 5 files changed, 17 insertions(+), 39 deletions(-) diff --git a/docs/opentitan/dtm.md b/docs/opentitan/dtm.md index af1b26e0d7e17..9a8a1c6df8762 100644 --- a/docs/opentitan/dtm.md +++ b/docs/opentitan/dtm.md @@ -82,8 +82,8 @@ Extras: operation, `--file` argument is mandatory. The content of the binary file is copied into the memory, starting at the `--address`. See also the `--elf` option for uploading applications. -* `-P` specify the TCP port of the JTAG server in the QEMU VM, should match the port part of `-jtag` - option for invoking QEMU. +* `-P` specify the TCP port of the JTAG server in the QEMU VM, should follow the TCP setting of the + `-chardev socket,id=taprbb,...` option for invoking QEMU. * `-Q` do not send QEMU a request for termination when this script exits. @@ -106,7 +106,8 @@ Extras: ### Examples -Running QEMU VM with the `-jtag tcp::3335` option: +Running QEMU VM with the `-chardev socket,id=taprbb,host=localhost,port=3335,server=on,wait=off` +option: * Retrieve JTAG/DTM/DM information and `mtvec` CSR value ````sh diff --git a/docs/opentitan/jtag-dm.md b/docs/opentitan/jtag-dm.md index b1bd5f9951b92..a5636edc296b0 100644 --- a/docs/opentitan/jtag-dm.md +++ b/docs/opentitan/jtag-dm.md @@ -46,7 +46,8 @@ See also [JTAG mailbox](jtagmbx.md) and [Life Controller](lc_ctrl_dmi.md) for ot The JTAG server supports the Remote Bitbang Protocol which is compatible with other tools such as Spike and [OpenOCD](https://github.com/riscv/riscv-openocd). -QEMU should be started with an option such as `-jtag tcp::3335` so that the JTAG server is +QEMU should be started with an option such as: +`-chardev socket,id=taprbb,host=localhost,port=3335,server=on,wait=off` so that the JTAG server is instantiated and listens for incoming connection on TCP port 3335. #### Remote termination feature @@ -58,16 +59,7 @@ example. It is nevertheless possible to disable this feature and ignore the remote exit request so that multiple JTAG sessions can be run without terminating the QEMU VM. -To disable this feature, use the `quit` option: `-jtag tcp::3335,quit=off`. - -#### macOS - -If you want to avoid the boring pop-up window from macOS -``` -Do you want the application “qemu-system-riscv32” to accept incoming network connections? -``` -restrict the listening interfaces to the localhost with `-jtag tcp:localhost:3335` as QEMU defaults -to listening on all interfaces, _i.e._ 0.0.0.0 +To disable this feature, use the `quit` option: `-global tap-ctrl-rbb,quit=off`. #### Implementation diff --git a/docs/opentitan/jtagmbx.md b/docs/opentitan/jtagmbx.md index 12b87aa0a26cf..782cb099e2805 100644 --- a/docs/opentitan/jtagmbx.md +++ b/docs/opentitan/jtagmbx.md @@ -28,17 +28,9 @@ where: `P` is the private OT bus `D` is the debug bus -QEMU should be started with an option such as `-jtag tcp::3335` so that the JTAG server is -instantiated and listen for incoming connection on TCP port 3335. - -#### macOS - -If you want to avoid the boring pop-up window from macOS -``` -Do you want the application “qemu-system-riscv32” to accept incoming network connections? -``` -restrict the listening interfaces to the localhost with `-jtag tcp:localhost:3335` as QEMU defaults -to listening on all interfaces, _i.e._ 0.0.0.0 +QEMU should be started with an option such as: +`-chardev socket,id=taprbb,host=localhost,port=3335,server=on,wait=off` so that the JTAG server is +instantiated and listens for incoming connection on TCP port 3335. ## Communicating with JTAG server and JTAG MailBox using Python diff --git a/docs/opentitan/lc_ctrl_dmi.md b/docs/opentitan/lc_ctrl_dmi.md index f6bcb235ffe73..c55e9b44e5089 100644 --- a/docs/opentitan/lc_ctrl_dmi.md +++ b/docs/opentitan/lc_ctrl_dmi.md @@ -25,17 +25,9 @@ where: `P` is the private OT bus `D` is the debug bus -QEMU should be started with an option such as `-jtag tcp::3335` so that the JTAG server is -instantiated and listen for incoming connection on TCP port 3335. - -#### macOS - -If you want to avoid the boring pop-up window from macOS -``` -Do you want the application “qemu-system-riscv32” to accept incoming network connections? -``` -restrict the listening interfaces to the localhost with `-jtag tcp:localhost:3335` as QEMU defaults -to listening on all interfaces, _i.e._ 0.0.0.0 +QEMU should be started with an option such as: +`-chardev socket,id=taprbb,host=localhost,port=3335,server=on,wait=off` so that the JTAG server is +instantiated and listens for incoming connection on TCP port 3335. ## Communicating with JTAG server and Life Cycle controller using Python diff --git a/docs/opentitan/otpdm.md b/docs/opentitan/otpdm.md index 26502cf0f3970..f6a33ee407137 100644 --- a/docs/opentitan/otpdm.md +++ b/docs/opentitan/otpdm.md @@ -67,8 +67,8 @@ Extras: * `-l` specify the length of the TAP instruction register length. -* `-P` specify the TCP port of the JTAG server in the QEMU VM, should match the port part of `-jtag` - option for invoking QEMU. +* `-P` specify the TCP port of the JTAG server in the QEMU VM, should follow the TCP setting of the + `-chardev socket,id=taprbb,...` option for invoking QEMU. * `-p` select a partition using its name. See option `-L` to get a list of valid partition names. Requires option `-j`. @@ -85,7 +85,8 @@ Extras: ### Examples -Running QEMU VM with the `-jtag tcp::3335` option: +Running QEMU VM with the `-chardev socket,id=taprbb,host=localhost,port=33355,server=on,wait=off` +option: * List all supported partitions ````sh From 55958b3fe345cf25451a4640169ea367f4d3f06b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 22 May 2024 16:07:17 +0200 Subject: [PATCH 33/65] [ot] scripts/jtag: bitbang.py: rename logger Signed-off-by: Emmanuel Blot --- scripts/jtag/bitbang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jtag/bitbang.py b/scripts/jtag/bitbang.py index f515de69b1663..922346dc515df 100644 --- a/scripts/jtag/bitbang.py +++ b/scripts/jtag/bitbang.py @@ -58,7 +58,7 @@ class JtagBitbangController(JtagController): """JTAG bitbang code to quit.""" def __init__(self, sock: socket, link_log: bool = False): - self._log = getLogger('jtag.ctrl') + self._log = getLogger('jtag.rbb') self._sock = sock self._link_log = link_log self._last: Optional[bool] = None # Last deferred TDO bit From 60d635bdf05b9cbee2f1649f5912f3e5cdbb1e15 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 21 May 2024 19:38:27 +0200 Subject: [PATCH 34/65] [ot] .gitlab-ci.d: update JTAG server configuration Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index 1ef24f5d3af07..18b63275a408b 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240521-1" + BAREMETAL_REF: "240522-1" QEMU_BUILD_OPTS: "" include: From ea73c596c394856019aa16ebba03feb1c6e7861a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 08:49:01 +0200 Subject: [PATCH 35/65] [ot] scripts/opentitan: pyot.py: replace deprecated Typing items Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 75 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index bbc8281f1a5ac..dfc9286646b8a 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -10,7 +10,7 @@ from argparse import ArgumentParser, FileType, Namespace from atexit import register -from collections import defaultdict +from collections import defaultdict, deque from csv import reader as csv_reader, writer as csv_writer from fnmatch import fnmatchcase from glob import glob @@ -33,8 +33,7 @@ from tempfile import mkdtemp, mkstemp from time import time as now from traceback import format_exc -from typing import (Any, Deque, Dict, Iterator, List, NamedTuple, Optional, Set, - Tuple) +from typing import Any, Iterator, NamedTuple, Optional from ot.util.log import configure_loggers @@ -96,10 +95,10 @@ def show(self, spacing: bool = False) -> None: if spacing: print('') - def _show_line(self, widths: List[int], csep: str) -> None: + def _show_line(self, widths: list[int], csep: str) -> None: print(f'+{"+".join([csep * (w+2) for w in widths])}+') - def _show_row(self, widths: List[int], cols: List[str]) -> None: + def _show_row(self, widths: list[int], cols: list[str]) -> None: line = '|'.join([f' {c:{">" if p else "<"}{w}s} ' for p, (w, c) in enumerate(zip(widths, cols))]) print(f'|{line}|') @@ -134,7 +133,7 @@ class QEMUWrapper: LOG_LEVELS = {'D': DEBUG, 'I': INFO, 'E': ERROR} """OpenTitan log levels.""" - def __init__(self, tcpdev: Tuple[str, int], debug: bool): + def __init__(self, tcpdev: tuple[str, int], debug: bool): # self._mterm: Optional[MiniTerm] = None self._device = tcpdev self._debug = debug @@ -142,9 +141,9 @@ def __init__(self, tcpdev: Tuple[str, int], debug: bool): self._qlog = getLogger('pyot.qemu') self._otlog = getLogger('pyot.ot') - def run(self, qemu_args: List[str], timeout: int, name: str, + def run(self, qemu_args: list[str], timeout: int, name: str, ctx: Optional['QEMUContext'], start_delay: float) -> \ - Tuple[int, ExecTime, str]: + tuple[int, ExecTime, str]: """Execute the specified QEMU command, aborting execution if QEMU does not exit after the specified timeout. @@ -207,7 +206,7 @@ def run(self, qemu_args: List[str], timeout: int, name: str, # queue, which is popped and logged to the local logger on each # loop. Note that Popen's communicate() also relies on threads to # perform stdout/stderr read out. - log_q = Deque() + log_q = deque() Thread(target=self._qemu_logger, name='qemu_out_logger', args=(proc, log_q, True)).start() Thread(target=self._qemu_logger, name='qemu_err_logger', @@ -348,7 +347,7 @@ def classify_log(cls, line: str, default: int = ERROR) -> int: return DEBUG return default - def _qemu_logger(self, proc: Popen, queue: Deque, err: bool): + def _qemu_logger(self, proc: Popen, queue: deque, err: bool): # worker thread, blocking on VM stdout/stderr stream = proc.stderr if err else proc.stdout while proc.poll() is None: @@ -394,11 +393,11 @@ class QEMUFileManager: def __init__(self, keep_temp: bool = False): self._log = getLogger('pyot.file') self._keep_temp = keep_temp - self._in_fly: Set[str] = set() - self._otp_files: Dict[str, Tuple[str, int]] = {} - self._env: Dict[str, str] = {} - self._transient_vars: Set[str] = set() - self._dirs: Dict[str, str] = {} + self._in_fly: set[str] = set() + self._otp_files: dict[str, tuple[str, int]] = {} + self._env: dict[str, str] = {} + self._transient_vars: set[str] = set() + self._dirs: dict[str, str] = {} register(self._cleanup) @property @@ -411,14 +410,14 @@ def keep_temporary(self) -> bool: return self._keep_temp def set_qemu_src_dir(self, path: str) -> None: - """Set the QEMU "source" directory. + """set the QEMU "source" directory. :param path: the path to the QEMU source directory """ self._env['QEMU_SRC_DIR'] = abspath(path) def set_qemu_bin_dir(self, path: str) -> None: - """Set the QEMU executable directory. + """set the QEMU executable directory. :param path: the path to the QEMU binary directory """ @@ -450,7 +449,7 @@ def replace(smo: Match) -> str: self._log.debug('Interpolate %s with %s', value, nvalue) return nvalue - def define(self, aliases: Dict[str, Any]) -> None: + def define(self, aliases: dict[str, Any]) -> None: """Store interpolation variables into a local dictionary. Variable values are interpolated before being stored. @@ -471,7 +470,7 @@ def replace(smo: Match) -> str: self._env[name.upper()] = value self._log.debug('Store %s as %s', name.upper(), value) - def define_transient(self, aliases: Dict[str, Any]) -> None: + def define_transient(self, aliases: dict[str, Any]) -> None: """Add short-lived aliases that are all discarded when cleanup_transient is called. @@ -640,7 +639,7 @@ def _configure_logger(self, tool) -> None: def _cleanup(self) -> None: """Remove a generated, temporary flash image file. """ - removed: Set[str] = set() + removed: set[str] = set() for tmpfile in self._in_fly: if not isfile(tmpfile): removed.add(tmpfile) @@ -659,7 +658,7 @@ def _cleanup(self) -> None: f'removed') for tmpfile in self._in_fly: self._log.warning('Temporary file %s not suppressed', tmpfile) - removed: Set[str] = set() + removed: set[str] = set() if not self._keep_temp: for tmpname, tmpdir in self._dirs.items(): if not isdir(tmpdir): @@ -685,11 +684,11 @@ class QEMUContextWorker: """Background task for QEMU context. """ - def __init__(self, cmd: str, env: Dict[str, str]): + def __init__(self, cmd: str, env: dict[str, str]): self._log = getLogger('pyot.cmd') self._cmd = cmd self._env = env - self._log_q = Deque() + self._log_q = deque() self._resume = False self._thread: Optional[Thread] = None self._ret = None @@ -788,8 +787,8 @@ class QEMUContext: """ def __init__(self, test_name: str, qfm: QEMUFileManager, - qemu_cmd: List[str], context: Dict[str, List[str]], - env: Optional[Dict[str, str]] = None): + qemu_cmd: list[str], context: dict[str, list[str]], + env: Optional[dict[str, str]] = None): # pylint: disable=too-many-arguments self._clog = getLogger('pyot.ctx') self._test_name = test_name @@ -797,7 +796,7 @@ def __init__(self, test_name: str, qfm: QEMUFileManager, self._qemu_cmd = qemu_cmd self._context = context self._env = env or {} - self._workers: List[Popen] = [] + self._workers: list[Popen] = [] def execute(self, ctx_name: str, code: int = 0) -> None: """Execute all commands, in order, for the selected context. @@ -929,15 +928,15 @@ class QEMUExecuter: virtual UART port. """ - def __init__(self, qfm: QEMUFileManager, config: Dict[str, any], + def __init__(self, qfm: QEMUFileManager, config: dict[str, any], args: Namespace): self._log = getLogger('pyot.exec') self._qfm = qfm self._config = config self._args = args - self._argdict: Dict[str, Any] = {} - self._qemu_cmd: List[str] = [] - self._vcp: Optional[Tuple[str, int]] = None + self._argdict: dict[str, Any] = {} + self._qemu_cmd: list[str] = [] + self._vcp: Optional[tuple[str, int]] = None self._suffixes = [] if hasattr(self._args, 'opts'): setattr(self._args, 'global_opts', getattr(self._args, 'opts')) @@ -1091,7 +1090,7 @@ def get_namespace_arg(cls, args: Namespace, name: str) -> Optional[str]: return args.__dict__.get(name) @staticmethod - def flatten(lst: List) -> List: + def flatten(lst: list) -> list: """Flatten a list. """ return [item for sublist in lst for item in sublist] @@ -1103,7 +1102,7 @@ def abspath(path: str) -> str: return normpath(path) return normpath(joinpath(getcwd(), path)) - def _cleanup_temp_files(self, storage: Dict[str, Set[str]]) -> None: + def _cleanup_temp_files(self, storage: dict[str, set[str]]) -> None: if self._qfm.keep_temporary: return for kind, files in storage.items(): @@ -1112,8 +1111,8 @@ def _cleanup_temp_files(self, storage: Dict[str, Set[str]]) -> None: delete_file(filename) def _build_qemu_command(self, args: Namespace, - opts: Optional[List[str]] = None) \ - -> Tuple[List[str], Tuple[str, int], Dict[str, Set[str]], float]: + opts: Optional[list[str]] = None) \ + -> tuple[list[str], tuple[str, int], dict[str, set[str]], float]: """Build QEMU command line from argparser values. :param args: the parsed arguments @@ -1234,7 +1233,7 @@ def _build_qemu_command(self, args: Namespace, return qemu_args, tcpdev, temp_files, start_delay def _build_qemu_test_command(self, filename: str) -> \ - Tuple[List[str], Namespace, int, Dict[str, Set[str]], QEMUContext, + tuple[list[str], Namespace, int, dict[str, set[str]], QEMUContext, float, int]: test_name = self.get_test_radix(filename) args, opts, timeout, texp = self._build_test_args(test_name) @@ -1243,7 +1242,7 @@ def _build_qemu_test_command(self, filename: str) -> \ ctx = self._build_test_context(test_name) return qemu_cmd, args, timeout, temp_files, ctx, sdelay, texp - def _build_test_list(self, alphasort: bool = True) -> List[str]: + def _build_test_list(self, alphasort: bool = True) -> list[str]: pathnames = set() testdir = normpath(self._qfm.interpolate(self._config.get('testdir', curdir))) @@ -1314,7 +1313,7 @@ def _enumerate_from(self, config_entry: str) -> Iterator[str]: testfile = joinpath(incf_dir, testfile) yield normpath(testfile) - def _build_config_list(self, config_entry: str) -> List: + def _build_config_list(self, config_entry: str) -> list: cfglist = [] items = self._config.get(config_entry) if not items: @@ -1343,7 +1342,7 @@ def _build_config_list(self, config_entry: str) -> List: return cfglist def _build_test_args(self, test_name: str) \ - -> Tuple[Namespace, List[str], int]: + -> tuple[Namespace, list[str], int]: tests_cfg = self._config.get('tests', {}) if not isinstance(tests_cfg, dict): raise ValueError('Invalid tests sub-section') From 2060a663dda5ca036c64f01cd1c70f2d7e6e0c99 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 15:32:41 +0200 Subject: [PATCH 36/65] [ot] scripts/opentitan: pyot.py: add an option to list test to execute. Also detects the type of test (SPI flash image or ELF) Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 4 +++- scripts/opentitan/pyot.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index e7e3243ce78f0..3a15a0bdef8f8 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -8,7 +8,7 @@ usage: pyot.py [-h] [-D DELAY] [-i N] [-L LOG_FILE] [-M LOG] [-m MACHINE] [-Q OPTS] [-q QEMU] [-p DEVICE] [-t TRACE] [-S FIRST_SOC] [-s] [-U] [-b file] [-c JSON] [-e] [-f RAW] [-K] [-l file] [-O RAW] [-o VMEM] [-r ELF] [-w CSV] [-x file] [-X] [-F TEST] - [-k SECONDS] [-R] [-T FACTOR] [-Z] [-v] [-d] + [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] OpenTitan QEMU unit test sequencer. @@ -57,6 +57,7 @@ Execution: run tests with matching filter, prefix with "!" to exclude matching tests -k SECONDS, --timeout SECONDS exit after the specified seconds (default: 60 secs) + -z, --list show a list of tests to execute and exit -R, --summary show a result summary -T FACTOR, --timeout-factor FACTOR timeout factor @@ -154,6 +155,7 @@ This tool may be used in two ways, which can be combined: * `-Z`, `--zero` do not report an error if no test can be executed with the specified filters and detected test applications. Default behavior is to report an error should such a condition arise, as it likely comes from a misconfiguration or build issue. +* `-z` / `--list` list all tests to be executed and exit ### File options diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index dfc9286646b8a..47fbf30465dc9 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -957,6 +957,14 @@ def build(self) -> None: raise ValueError('Invalid suffixes sub-section') self._suffixes.extend(suffixes) + def enumerate_tests(self) -> Iterator[str]: + """Enumerate tests to execute. + """ + self._argdict = dict(self._args.__dict__) + for tst in sorted(self._build_test_list()): + ttype = self.guess_test_type(tst) + yield f'{basename(tst)} ({ttype})' + def run(self, debug: bool, allow_no_test: bool) -> int: """Execute all requested tests. @@ -1102,6 +1110,20 @@ def abspath(path: str) -> str: return normpath(path) return normpath(joinpath(getcwd(), path)) + @staticmethod + def guess_test_type(filepath: str) -> str: + """Guess a test file type from its contents. + + :return: identified content + """ + with open(filepath, 'rb') as bfp: + header = bfp.read(4) + if header == b'\x7fELF': + return 'elf' + if header == b'OTPT': + return 'spiflash' + return 'bin' + def _cleanup_temp_files(self, storage: dict[str, set[str]]) -> None: if self._qfm.keep_temporary: return @@ -1494,6 +1516,8 @@ def main(): exe.add_argument('-k', '--timeout', metavar='SECONDS', type=int, help=f'exit after the specified seconds ' f'(default: {DEFAULT_TIMEOUT} secs)') + exe.add_argument('-z', '--list', action='store_true', + help='show a list of tests to execute and exit') exe.add_argument('-R', '--summary', action='store_true', help='show a result summary') exe.add_argument('-T', '--timeout-factor', type=float, metavar='FACTOR', @@ -1589,6 +1613,10 @@ def main(): qfm.set_qemu_src_dir(qemu_dir) qfm.set_qemu_bin_dir(dirname(args.qemu)) qexc = QEMUExecuter(qfm, json, args) + if args.list: + for tst in qexc.enumerate_tests(): + print(tst) + sysexit(0) try: qexc.build() except ValueError as exc: From 25647e756df9ad6e51576d8fa05d714bcbdc7414 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 15:33:17 +0200 Subject: [PATCH 37/65] [ot] scripts/opentitan: pyot.py: add support for multiple ROMs Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 4 +-- scripts/opentitan/pyot.py | 60 +++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index 3a15a0bdef8f8..bce0f7d093c40 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -47,7 +47,7 @@ Files: -O RAW, --otp-raw RAW OTP image file -o VMEM, --otp VMEM OTP VMEM file - -r ELF, --rom ELF ROM file + -r ELF, --rom ELF ROM file (can be repeated, in load order) -w CSV, --result CSV path to output result file -x file, --exec file application to load -X, --rom-exec load application as ROM image (default: as kernel) @@ -135,7 +135,7 @@ This tool may be used in two ways, which can be combined: * `-r` / `--rom` specify a ROM ELF file. Without a ROM file, it is unlikely to start up any regular application since the emulated lowRISC vCPU is preconfigured with a locked PMP, as the real HW. When no ROM is specified, test applications are executed immediately, as a replacement of the ROM - executable. + executable. This option may be repeated, to load several ROMs, in the same order as specified * `-w` / `--result` specify an output CSV report file where the result of all the QEMU sessions, one per test, are reported. * `-x` / ` --exec` specify a ROM extension, an application or a test to execute. This option is diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 47fbf30465dc9..53d52b0362a79 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1152,33 +1152,49 @@ def _build_qemu_command(self, args: Namespace, args.machine ] rom_exec = bool(args.rom_exec) - rom_count = int(rom_exec) - if args.rom: - rom_count += 1 - rom_path = self.abspath(args.rom) + multi_rom = (len(args.rom) + int(rom_exec)) > 1 + # generate pre-application ROM option + rom_count = 0 + for rom in args.rom: + rom_path = self._qfm.interpolate(rom) + if not isfile(rom_path): + raise ValueError(f'Unable to find ROM file {rom_path}') rom_ids = [] if args.first_soc: rom_ids.append(args.first_soc) rom_ids.append('rom') - if rom_count > 1: - rom_ids.append('0') + if multi_rom: + rom_ids.append(f'{rom_count}') rom_id = ''.join(rom_ids) rom_opt = f'ot-rom-img,id={rom_id},file={rom_path},digest=fake' qemu_args.extend(('-object', rom_opt)) + rom_count += 1 + xtype = None if args.exec: exec_path = self.abspath(args.exec) - if rom_exec: - rom_ids = [] - if args.first_soc: - rom_ids.append(args.first_soc) - rom_ids.append('rom') - if rom_count > 1: - rom_ids.append('1') - rom_id = ''.join(rom_ids) - rom_opt = f'ot-rom-img,id={rom_id},file={exec_path},digest=fake' - qemu_args.extend(('-object', rom_opt)) + xtype = self.guess_test_type(exec_path) + if xtype == 'spiflash': + qemu_args.extend(('-drive', + f'if=mtd,format=raw,file={exec_path}')) else: - qemu_args.extend(('-kernel', exec_path)) + if xtype != 'elf': + raise ValueError(f'No support for test type: {xtype} ' + f'({basename(exec_path)})') + if rom_exec: + # generate ROM option for the application itself + rom_ids = [] + if args.first_soc: + rom_ids.append(args.first_soc) + rom_ids.append('rom') + if multi_rom: + rom_ids.append(f'{rom_count}') + rom_id = ''.join(rom_ids) + rom_opt = (f'ot-rom-img,id={rom_id},file={exec_path},' + f'digest=fake') + qemu_args.extend(('-object', rom_opt)) + rom_count += 1 + else: + qemu_args.extend(('-kernel', exec_path)) temp_files = defaultdict(set) if all((args.otp, args.otp_raw)): raise ValueError('OTP VMEM and RAW options are mutually exclusive') @@ -1194,6 +1210,8 @@ def _build_qemu_command(self, args: Namespace, qemu_args.extend(('-drive', f'if=pflash,file={otp_raw_path},format=raw')) if args.flash: + if xtype == 'spiflash': + raise ValueError('Cannot use a flash file with a flash test') if not isfile(args.flash): raise ValueError(f'No such flash file: {args.flash}') if any((args.exec, args.boot)): @@ -1268,7 +1286,6 @@ def _build_test_list(self, alphasort: bool = True) -> list[str]: pathnames = set() testdir = normpath(self._qfm.interpolate(self._config.get('testdir', curdir))) - rom = self._argdict.get('rom') self._qfm.define({'testdir': testdir}) cfilters = self._args.filter or [] pfilters = [f for f in cfilters if not f.startswith('!')] @@ -1300,8 +1317,8 @@ def _build_test_list(self, alphasort: bool = True) -> list[str]: pathnames.add(testfile) if not pathnames: return [] - if rom: - pathnames -= {normpath(rom)} + roms = self._argdict.get('rom') + pathnames -= {normpath(rom) for rom in roms} xtfilters = [f[1:].strip() for f in cfilters if f.startswith('!')] exc_filters = self._build_config_list('exclude') xtfilters.extend(exc_filters) @@ -1501,7 +1518,8 @@ def main(): files.add_argument('-O', '--otp-raw', metavar='RAW', help='OTP image file') files.add_argument('-o', '--otp', metavar='VMEM', help='OTP VMEM file') - files.add_argument('-r', '--rom', metavar='ELF', help='ROM file') + files.add_argument('-r', '--rom', metavar='ELF', action='append', + help='ROM file (can be repeated, in load order)') files.add_argument('-w', '--result', metavar='CSV', help='path to output result file') files.add_argument('-x', '--exec', metavar='file', From c71d57aa3e9acf9ab4bfc9b2e1e08775f04514c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 16:01:08 +0200 Subject: [PATCH 38/65] [ot] scripts/opentitan: pyot.py: discard ANSI escape sequence from QEMU VCP Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 53d52b0362a79..4c6e60fc2ac37 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -123,6 +123,9 @@ class QEMUWrapper: such as SIGABRT. """ + ANSI_CRE = re_compile(rb'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') + """ANSI escape sequences.""" + GUEST_ERROR_OFFSET = 40 """Offset for guest errors. Should be larger than the host max signal value. """ @@ -261,6 +264,7 @@ def run(self, qemu_args: list[str], timeout: int, name: str, lines = vcp_buf.split(b'\n') vcp_buf = bytearray(lines[-1]) for line in lines[:-1]: + line = self.ANSI_CRE.sub(b'', line) xmo = xre.search(line) if xmo: xend = now() From 2bd98fefeacf37703fbb3f531d367720b29987e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 18:59:11 +0200 Subject: [PATCH 39/65] [ot] scripts/opentitan: pyot.py: add a text-based trigger matching feature. When a `with` context is defined. i.e. one or more apps that should execute once the VM is up and running and usually communicate with the VM over a dedicated channel, it is now possible to postpone the context execution when a specific string is matched on the VM default virtual comm port. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 84 ++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 4c6e60fc2ac37..989cb5d4cdd71 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -24,12 +24,12 @@ from os import close, curdir, environ, getcwd, linesep, pardir, sep, unlink from os.path import (abspath, basename, dirname, exists, isabs, isdir, isfile, join as joinpath, normpath, relpath) -from re import Match, compile as re_compile, sub as re_sub +from re import Match, compile as re_compile, error as re_error, sub as re_sub from shutil import rmtree from socket import socket, timeout as LegacyTimeoutError from subprocess import Popen, PIPE, TimeoutExpired from sys import argv, exit as sysexit, modules, stderr -from threading import Thread +from threading import Event, Thread from tempfile import mkdtemp, mkstemp from time import time as now from traceback import format_exc @@ -145,7 +145,8 @@ def __init__(self, tcpdev: tuple[str, int], debug: bool): self._otlog = getLogger('pyot.ot') def run(self, qemu_args: list[str], timeout: int, name: str, - ctx: Optional['QEMUContext'], start_delay: float) -> \ + ctx: Optional['QEMUContext'], start_delay: float, + trigger: str = None) -> \ tuple[int, ExecTime, str]: """Execute the specified QEMU command, aborting execution if QEMU does not exit after the specified timeout. @@ -156,6 +157,8 @@ def run(self, qemu_args: list[str], timeout: int, name: str, :param name: the tested application name :param ctx: execution context, if any :param start_delay: start up delay + :param trigger: if not empty, the string to wait for triggering + 'with' context execution :return: a 3-uple of exit code, execution time, and last guest error """ # stdout and stderr belongs to QEMU VM @@ -164,6 +167,24 @@ def run(self, qemu_args: list[str], timeout: int, name: str, xre = re_compile(self.EXIT_ON) otre = r'^([' + ''.join(self.LOG_LEVELS.keys()) + r'])\d{5}\s' lre = re_compile(otre) + if trigger: + sync_event = Event() + if trigger.startswith("r'") and trigger.endswith("'"): + try: + tmo = re_compile(trigger[2:-1].encode()) + except re_error as exc: + raise ValueError('Invalid trigger regex: {exc}') from exc + + def trig_match(bline): + return tmo.match(bline) + else: + btrigger = trigger.encode() + + def trig_match(bline): + return bline.find(btrigger) >= 0 + else: + sync_event = None + trig_match = None ret = None proc = None sock = None @@ -215,9 +236,10 @@ def run(self, qemu_args: list[str], timeout: int, name: str, Thread(target=self._qemu_logger, name='qemu_err_logger', args=(proc, log_q, False)).start() xstart = now() + # sync_event.set() if ctx: try: - ctx.execute('with') + ctx.execute('with', sync=sync_event) except OSError as exc: ret = exc.errno last_error = exc.strerror @@ -251,8 +273,8 @@ def run(self, qemu_args: list[str], timeout: int, name: str, xend = now() ret = xret if ret != 0: - self._log.critical('Abnormal QEMU termination: %d ' - 'for "%s"', ret, name) + log.critical('Abnormal QEMU termination: %d for "%s"', + ret, name) break try: data = sock.recv(4096) @@ -265,6 +287,13 @@ def run(self, qemu_args: list[str], timeout: int, name: str, vcp_buf = bytearray(lines[-1]) for line in lines[:-1]: line = self.ANSI_CRE.sub(b'', line) + if trig_match and trig_match(line): + self._log.info('Trigger pattern detected') + # reset timeout from this event + abstimeout = float(timeout) + now() + log.debug('Resuming for %.0f secs', timeout) + sync_event.set() + trig_match = None xmo = xre.search(line) if xmo: xend = now() @@ -688,10 +717,12 @@ class QEMUContextWorker: """Background task for QEMU context. """ - def __init__(self, cmd: str, env: dict[str, str]): + def __init__(self, cmd: str, env: dict[str, str], + sync: Optional[Event] = None): self._log = getLogger('pyot.cmd') self._cmd = cmd self._env = env + self._sync = sync self._log_q = deque() self._resume = False self._thread: Optional[Thread] = None @@ -727,6 +758,13 @@ def command(self) -> str: def _run(self): self._resume = True + if self._sync and not self._sync.is_set(): + self._log.info('Waiting for sync') + while self._resume: + if self._sync.wait(0.1): + self._log.info('Sync triggered') + break + self._sync.clear() # pylint: disable=consider-using-with proc = Popen(self._cmd, bufsize=1, stdout=PIPE, stderr=PIPE, shell=True, env=self._env, encoding='utf-8', @@ -802,7 +840,8 @@ def __init__(self, test_name: str, qfm: QEMUFileManager, self._env = env or {} self._workers: list[Popen] = [] - def execute(self, ctx_name: str, code: int = 0) -> None: + def execute(self, ctx_name: str, code: int = 0, + sync: Optional[Event] = None) -> None: """Execute all commands, in order, for the selected context. Synchronous commands are executed in order. If one command fails, @@ -811,7 +850,10 @@ def execute(self, ctx_name: str, code: int = 0) -> None: Background commands are started in order, but a failure does not stop other commands. + :param ctx_name: the name of the execution context :param code: a previous error completion code, if any + :param sync: an optional synchronisation event to start up the + execution """ ctx = self._context.get(ctx_name, None) if ctx_name == 'post' and code: @@ -835,10 +877,12 @@ def execute(self, ctx_name: str, code: int = 0) -> None: rcmd = cmd self._clog.debug('Execute "%s" in background for [%s] ' 'context', rcmd, ctx_name) - worker = QEMUContextWorker(cmd, env) + worker = QEMUContextWorker(cmd, env, sync) worker.run() self._workers.append(worker) else: + if sync: + self._clog.debug('Synchronization ignored') cmd = normpath(cmd.rstrip()) rcmd = relpath(cmd) if rcmd.startswith(pardir): @@ -846,7 +890,7 @@ def execute(self, ctx_name: str, code: int = 0) -> None: self._clog.debug('Execute "%s" in sync for [%s] context', rcmd, ctx_name) # pylint: disable=consider-using-with - proc = Popen(cmd, bufsize=1, stdout=PIPE, stderr=PIPE, + proc = Popen(cmd, bufsize=1, stdout=PIPE, stderr=PIPE, shell=True, env=env, encoding='utf-8', errors='ignore', text=True) ret = 0 @@ -1019,11 +1063,11 @@ def run(self, debug: bool, allow_no_test: bool) -> int: 'UTFILE': basename(test), }) test_name = self.get_test_radix(test) - qemu_cmd, targs, timeout, temp_files, ctx, sdelay, texp = \ + qemu_cmd, targs, timeout, temp_files, ctx, sdelay, texp, trig = \ self._build_qemu_test_command(test) ctx.execute('pre') tret, xtime, err = qot.run(qemu_cmd, timeout, test_name, - ctx, sdelay) + ctx, sdelay, trig) cret = ctx.finalize() ctx.execute('post', tret) if texp != 0: @@ -1138,7 +1182,8 @@ def _cleanup_temp_files(self, storage: dict[str, set[str]]) -> None: def _build_qemu_command(self, args: Namespace, opts: Optional[list[str]] = None) \ - -> tuple[list[str], tuple[str, int], dict[str, set[str]], float]: + -> tuple[list[str], tuple[str, int], dict[str, set[str]], float, + str]: """Build QEMU command line from argparser values. :param args: the parsed arguments @@ -1146,7 +1191,8 @@ def _build_qemu_command(self, args: Namespace, :return: a tuple of a list of QEMU command line, the TCP device descriptor to connect to the QEMU VCP, a dictionary of generated temporary files and the start - delay + delay, the start delay and a trigger string which may be + empty """ if args.qemu is None: raise ValueError('QEMU path is not defined') @@ -1257,6 +1303,7 @@ def _build_qemu_command(self, args: Namespace, raise ValueError(f'Invalid start up delay {args.start_delay}') \ from exc start_delay *= args.timeout_factor + trigger = getattr(args, 'trigger', '') device = args.device devdesc = device.split(':') try: @@ -1274,17 +1321,18 @@ def _build_qemu_command(self, args: Namespace, qemu_args.extend(args.global_opts or []) if opts: qemu_args.extend((str(o) for o in opts)) - return qemu_args, tcpdev, temp_files, start_delay + return qemu_args, tcpdev, temp_files, start_delay, trigger def _build_qemu_test_command(self, filename: str) -> \ tuple[list[str], Namespace, int, dict[str, set[str]], QEMUContext, - float, int]: + float, int, str]: test_name = self.get_test_radix(filename) args, opts, timeout, texp = self._build_test_args(test_name) setattr(args, 'exec', filename) - qemu_cmd, _, temp_files, sdelay = self._build_qemu_command(args, opts) + qemu_cmd, _, temp_files, sdelay, trig = self._build_qemu_command(args, + opts) ctx = self._build_test_context(test_name) - return qemu_cmd, args, timeout, temp_files, ctx, sdelay, texp + return qemu_cmd, args, timeout, temp_files, ctx, sdelay, texp, trig def _build_test_list(self, alphasort: bool = True) -> list[str]: pathnames = set() From 82ad7df6eec49035362a57da13026609c6b2000d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:32:49 +0200 Subject: [PATCH 40/65] [ot] scripts/opentitan: log.py: enable fine grained log configuration Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 49 +++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 7f459e0c16e1b..0ac26b29c19a2 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -6,14 +6,18 @@ :author: Emmanuel Blot """ -from logging import (Formatter, Logger, StreamHandler, CRITICAL, DEBUG, INFO, - ERROR, WARNING, getLogger) from os import isatty from sys import stderr from typing import List, Tuple +import logging -class ColorLogFormatter(Formatter): + +_LEVELS = {k: v for k, v in logging.getLevelNamesMapping().items() + if k not in ('NOTSET', 'WARN')} + + +class ColorLogFormatter(logging.Formatter): """Custom log formatter for ANSI terminals. Colorize log levels. @@ -34,11 +38,11 @@ class ColorLogFormatter(Formatter): FMT_LEVEL = '%(levelname)8s' COLORS = { - DEBUG: GREY, - INFO: WHITE, - WARNING: YELLOW, - ERROR: RED, - CRITICAL: MAGENTA, + logging.DEBUG: GREY, + logging.INFO: WHITE, + logging.WARNING: YELLOW, + logging.ERROR: RED, + logging.CRITICAL: MAGENTA, } def __init__(self, *args, **kwargs): @@ -69,12 +73,12 @@ def __init__(self, *args, **kwargs): def format(self, record): log_fmt = self._color_formats[record.levelno] if self._use_ansi \ else self._plain_format - formatter = Formatter(log_fmt, *self._formatter_args) + formatter = logging.Formatter(log_fmt, *self._formatter_args) return formatter.format(record) def configure_loggers(level: int, *lognames: List[str], **kwargs) \ - -> List[Logger]: + -> List[logging.Logger]: """Configure loggers. :param level: level (stepping: 1) @@ -82,21 +86,32 @@ def configure_loggers(level: int, *lognames: List[str], **kwargs) \ :param kwargs: optional features :return: configured loggers or level change """ - loglevel = ERROR - (10 * (level or 0)) - loglevel = min(ERROR, loglevel) + loglevel = logging.ERROR - (10 * (level or 0)) + loglevel = min(logging.ERROR, loglevel) + loglevels = {} + for lvl in _LEVELS: + lnames = kwargs.pop(lvl.lower(), None) + if lnames: + if isinstance(lnames, str): + lnames = [lnames] + loglevels[lvl] = tuple(lnames) formatter = ColorLogFormatter(**kwargs) - logh = StreamHandler(stderr) + logh = logging.StreamHandler(stderr) logh.setFormatter(formatter) - loggers: List[Logger] = [] - logdefs: List[Tuple[List[str], Logger]] = [] + loggers: List[logging.Logger] = [] + logdefs: List[Tuple[List[str], logging.Logger]] = [] for logdef in lognames: if isinstance(logdef, int): loglevel += -10 * logdef continue - log = getLogger(logdef) - log.setLevel(max(DEBUG, loglevel)) + log = logging.getLogger(logdef) + log.setLevel(max(logging.DEBUG, loglevel)) loggers.append(log) logdefs.append((logdef.split('.'), log)) + for lvl, lnames in loglevels.items(): + for lname in lnames: + log = logging.getLogger(lname) + log.setLevel(_LEVELS[lvl]) logdefs.sort(key=lambda p: len(p[0])) # ensure there is only one handler per logger subtree for _, log in logdefs: From b899c98c80eeb216eb2bb9e059665f919846b8c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:34:06 +0200 Subject: [PATCH 41/65] [ot] scripts/opentitan: pyot.py: improve logger configuration Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 989cb5d4cdd71..e91dbb15a4b20 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -142,7 +142,7 @@ def __init__(self, tcpdev: tuple[str, int], debug: bool): self._debug = debug self._log = getLogger('pyot') self._qlog = getLogger('pyot.qemu') - self._otlog = getLogger('pyot.ot') + self._vcplog = getLogger('pyot.vcp') def run(self, qemu_args: list[str], timeout: int, name: str, ctx: Optional['QEMUContext'], start_delay: float, @@ -316,7 +316,7 @@ def trig_match(bline): last_error = err.strip('"').replace(',', ';') else: level = DEBUG # fall back when no prefix is found - self._otlog.log(level, sline) + self._vcplog.log(level, sline) else: # no match continue @@ -1598,8 +1598,14 @@ def main(): extra = argparser.add_argument_group(title='Extras') extra.add_argument('-v', '--verbose', action='count', help='increase verbosity') - extra.add_argument('-d', '--debug', action='store_true', + extra.add_argument('-d', action='store_true', help='enable debug mode') + extra.add_argument('--debug', action='append', + help='assign debug level to logger(s)') + extra.add_argument('--info', action='append', + help='assign info level to logger(s)') + extra.add_argument('--warn', action='append', + help='assign warning level to logger(s)') try: # all arguments after `--` are forwarded to QEMU @@ -1617,7 +1623,9 @@ def main(): close(tmpfd) args.result = tmp_result - log = configure_loggers(args.verbose, 'pyot')[0] + log = configure_loggers(args.verbose, 'pyot', debug=args.debug, + info=args.info, warning=args.warn, + error=args.error)[0] qfm = QEMUFileManager(args.keep_tmp) From d4f5adc4e07554923b7a52e82dc8865ee3a1e502 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:39:34 +0200 Subject: [PATCH 42/65] [ot] scripts/opentitan: add fine-grained control on loggers Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 16 +++++++++++----- scripts/opentitan/pyot.py | 9 ++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index bce0f7d093c40..a9915782d2a9f 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -8,7 +8,8 @@ usage: pyot.py [-h] [-D DELAY] [-i N] [-L LOG_FILE] [-M LOG] [-m MACHINE] [-Q OPTS] [-q QEMU] [-p DEVICE] [-t TRACE] [-S FIRST_SOC] [-s] [-U] [-b file] [-c JSON] [-e] [-f RAW] [-K] [-l file] [-O RAW] [-o VMEM] [-r ELF] [-w CSV] [-x file] [-X] [-F TEST] - [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] + [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] [--debug DEBUG] [--info INFO] + [--warn WARN] OpenTitan QEMU unit test sequencer. @@ -65,7 +66,10 @@ Execution: Extras: -v, --verbose increase verbosity - -d, --debug enable debug mode + -d enable debug mode + --debug LOGGER assign debug level to logger(s) + --info LOGGER assign info level to logger(s) + --warn LOGGER assign warning level to logger(s) ```` This tool may be used in two ways, which can be combined: @@ -157,11 +161,13 @@ This tool may be used in two ways, which can be combined: as it likely comes from a misconfiguration or build issue. * `-z` / `--list` list all tests to be executed and exit -### File options +### Extras * `-v` / `--verbose` can be repeated to increase verbosity of the script, mostly for debug purpose. -* `-d` / `--debug` only useful to debug the script, reports any Python traceback to the standard - error stream. +* `-d` only useful to debug the script, reports any Python traceback to the standard error stream. +* `--debug` enable the debug level for the selected logger, may be repeated +* `--info` enable the info level for the selected logger, may be repeated +* `--warn` enable the warning level for the selected logger, may be repeated ## Configuration file diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index e91dbb15a4b20..34424950fc302 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1600,11 +1600,11 @@ def main(): help='increase verbosity') extra.add_argument('-d', action='store_true', help='enable debug mode') - extra.add_argument('--debug', action='append', + extra.add_argument('--debug', action='append', metavar='LOGGER', help='assign debug level to logger(s)') - extra.add_argument('--info', action='append', + extra.add_argument('--info', action='append', metavar='LOGGER', help='assign info level to logger(s)') - extra.add_argument('--warn', action='append', + extra.add_argument('--warn', action='append', metavar='LOGGER', help='assign warning level to logger(s)') try: @@ -1624,8 +1624,7 @@ def main(): args.result = tmp_result log = configure_loggers(args.verbose, 'pyot', debug=args.debug, - info=args.info, warning=args.warn, - error=args.error)[0] + info=args.info, warning=args.warn)[0] qfm = QEMUFileManager(args.keep_tmp) From b699a16cfb1fc3beb19df126b10495e6be9d84a9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:44:01 +0200 Subject: [PATCH 43/65] [ot] scripts/opentitan: fix handling of dirname(__file__) Signed-off-by: Emmanuel Blot --- scripts/opentitan/checkregs.py | 5 +++-- scripts/opentitan/gdbreplay.py | 2 +- scripts/opentitan/pyot.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/opentitan/checkregs.py b/scripts/opentitan/checkregs.py index 8587397b8ed3e..71bc3aac1f627 100755 --- a/scripts/opentitan/checkregs.py +++ b/scripts/opentitan/checkregs.py @@ -9,7 +9,8 @@ from argparse import ArgumentParser from logging import DEBUG, ERROR, getLogger, Formatter, StreamHandler from os import pardir, walk -from os.path import basename, dirname, join as joinpath, relpath, splitext +from os.path import (basename, dirname, join as joinpath, normpath, relpath, + splitext) from re import compile as re_compile, sub as re_sub from sys import exit as sysexit, modules, stderr from traceback import format_exc @@ -205,7 +206,7 @@ def fix(self, filename: str, suffix: str, fixes: Dict[ValueLocation, int], def main(): """Main routine""" debug = False - qemu_default_dir = dirname(dirname(dirname(__file__))) + qemu_default_dir = dirname(dirname(dirname(normpath(__file__)))) try: desc = modules[__name__].__doc__.split('.', 1)[0].strip() argparser = ArgumentParser(description=f'{desc}.') diff --git a/scripts/opentitan/gdbreplay.py b/scripts/opentitan/gdbreplay.py index 8ba2ff483ac3c..05124c707497f 100755 --- a/scripts/opentitan/gdbreplay.py +++ b/scripts/opentitan/gdbreplay.py @@ -642,7 +642,7 @@ def _do_query_attached(self, *_) -> str: def main(): """Main routine""" debug = True - qemu_path = normpath(joinpath(dirname(dirname(dirname(__file__))), + qemu_path = normpath(joinpath(dirname(dirname(dirname(normpath(__file__)))), 'build', 'qemu-system-riscv32')) if not isfile(qemu_path): qemu_path = None diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 34424950fc302..42b215f816b18 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1511,7 +1511,7 @@ def _build_test_context(self, test_name: str) -> QEMUContext: def main(): """Main routine""" debug = True - qemu_dir = normpath(joinpath(dirname(dirname(dirname(__file__))))) + qemu_dir = normpath(joinpath(dirname(dirname(dirname(normpath(__file__)))))) qemu_path = normpath(joinpath(qemu_dir, 'build', 'qemu-system-riscv32')) if not isfile(qemu_path): qemu_path = None From 5cda8969d8cb32e21e6adc427f705ae5647b4e99 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:59:14 +0200 Subject: [PATCH 44/65] [ot] scripts/opentitan: fix deprecated Python type hinting syntax Signed-off-by: Emmanuel Blot --- scripts/opentitan/checkregs.py | 16 ++++++++-------- scripts/opentitan/flashgen.py | 12 ++++++------ scripts/opentitan/gdbreplay.py | 16 ++++++++-------- scripts/opentitan/gpiodev.py | 4 ++-- scripts/opentitan/loghelp.py | 4 ++-- scripts/opentitan/mbbdef.py | 10 +++++----- scripts/opentitan/ot/bitfield.py | 6 +++--- scripts/opentitan/ot/devproxy.py | 23 +++++++++++------------ scripts/opentitan/ot/dm/dm.py | 12 ++++++------ scripts/opentitan/ot/dm/otp.py | 14 +++++++------- scripts/opentitan/ot/dtm/dtm.py | 6 +++--- scripts/opentitan/ot/lc_ctrl/lcdmi.py | 6 +++--- scripts/opentitan/ot/mailbox/doe.py | 3 +-- scripts/opentitan/ot/otp/descriptor.py | 6 +++--- scripts/opentitan/ot/otp/image.py | 18 +++++++++--------- scripts/opentitan/ot/otp/map.py | 16 ++++++++-------- scripts/opentitan/ot/otp/partition.py | 8 ++++---- scripts/opentitan/ot/util/elf.py | 6 +++--- scripts/opentitan/ot/util/log.py | 9 ++++----- scripts/opentitan/present.py | 4 ++-- scripts/opentitan/uartmux.py | 14 +++++++------- 21 files changed, 105 insertions(+), 108 deletions(-) diff --git a/scripts/opentitan/checkregs.py b/scripts/opentitan/checkregs.py index 71bc3aac1f627..e076c28a3695e 100755 --- a/scripts/opentitan/checkregs.py +++ b/scripts/opentitan/checkregs.py @@ -14,7 +14,7 @@ from re import compile as re_compile, sub as re_sub from sys import exit as sysexit, modules, stderr from traceback import format_exc -from typing import Dict, NamedTuple, Optional, Set, TextIO, Tuple +from typing import NamedTuple, Optional, TextIO class ValueLocation(NamedTuple): @@ -24,7 +24,7 @@ class ValueLocation(NamedTuple): end: int # end column -RegisterDefs = Dict[str, Tuple[int, ValueLocation]] +RegisterDefs = dict[str, tuple[int, ValueLocation]] """Definition of a register value (name, value, location).""" # pylint: disable=missing-function-docstring @@ -119,13 +119,13 @@ def _parse_ot_qemu(self, qfp: TextIO) -> RegisterDefs: def compare(self, name: str, hdefs: RegisterDefs, qdefs: RegisterDefs, show_all: bool) \ - -> Tuple[int, Dict[ValueLocation, int], Set[int]]: + -> tuple[int, dict[ValueLocation, int], set[int]]: name = basename(name) chdefs = {k: v[0] for k, v in hdefs.items()} cqdefs = {k: v[0] for k, v in qdefs.items()} - deprecated: Set[int] = set() - appendable: Dict[str, int] = {} - fixes: Dict[ValueLocation, int] = {} + deprecated: set[int] = set() + appendable: dict[str, int] = {} + fixes: dict[ValueLocation, int] = {} if chdefs == cqdefs: self._log.info('%s: ok, %d register definitions', name, len(hdefs)) return 0, fixes, deprecated, appendable @@ -166,8 +166,8 @@ def compare(self, name: str, hdefs: RegisterDefs, self._log.error('%s: %d discrepancies', name, mismatch_count) return mismatch_count, fixes, deprecated, appendable - def fix(self, filename: str, suffix: str, fixes: Dict[ValueLocation, int], - deprecated: Set[int], newvalues: Dict[str, int]) \ + def fix(self, filename: str, suffix: str, fixes: dict[ValueLocation, int], + deprecated: set[int], newvalues: dict[str, int]) \ -> None: fix_lines = {loc.line: (loc.start, loc.end, val) for loc, val in fixes.items()} diff --git a/scripts/opentitan/flashgen.py b/scripts/opentitan/flashgen.py index 9ea308ca8ef9e..bd2ffb45cfe64 100755 --- a/scripts/opentitan/flashgen.py +++ b/scripts/opentitan/flashgen.py @@ -19,7 +19,7 @@ from struct import calcsize as scalc, pack as spack, unpack as sunpack from sys import exit as sysexit, modules, stderr, version_info from traceback import format_exc -from typing import Any, BinaryIO, Dict, List, NamedTuple, Optional, Tuple +from typing import Any, BinaryIO, NamedTuple, Optional from ot.util.elf import ElfBlob from ot.util.log import configure_loggers @@ -211,8 +211,8 @@ def logger(self): def info_part_size(cls) -> int: return sum(cls.INFOS) * cls.BYTES_PER_PAGE - def read_boot_info(self) -> Dict[BootLocation, - Dict[str, [int | bytes]]]: + def read_boot_info(self) -> dict[BootLocation, + dict[str, [int | bytes]]]: size = self._boot_header_size fmt = ''.join(self.BOOT_HEADER_FORMAT.values()) boot_entries = {} @@ -361,7 +361,7 @@ def store_bootloader(self, bank: int, dfp: BinaryIO, self._log.warning(msg) self._store_debug_info(ename, elfpath) - def store_ot_files(self, otdescs: List[str]) -> None: + def store_ot_files(self, otdescs: list[str]) -> None: for dpos, otdesc in enumerate(otdescs, start=1): parts = otdesc.rsplit(':', 1) if len(parts) > 1: @@ -447,8 +447,8 @@ def _get_boot_location_offset(self, loc: BootLocation) -> int: loc.page * self.BYTES_PER_PAGE + loc.seq * self._boot_header_size) - def _build_field(self, fmtdict: Dict[str, Any], field: str, value: Any) \ - -> Tuple[int, bytes]: + def _build_field(self, fmtdict: dict[str, Any], field: str, value: Any) \ + -> tuple[int, bytes]: offset = 0 for name, fmt in fmtdict.items(): if name == field: diff --git a/scripts/opentitan/gdbreplay.py b/scripts/opentitan/gdbreplay.py index 05124c707497f..55c24b1d42bfb 100755 --- a/scripts/opentitan/gdbreplay.py +++ b/scripts/opentitan/gdbreplay.py @@ -19,7 +19,7 @@ from string import ascii_uppercase from sys import exit as sysexit, modules, stderr from traceback import format_exc -from typing import BinaryIO, Dict, List, Optional, TextIO, Tuple +from typing import BinaryIO, Optional, TextIO from ot.util.elf import ElfBlob from ot.util.log import configure_loggers @@ -33,7 +33,7 @@ class QEMUMemoryController: def __init__(self): self._log = getLogger('gdbrp.mem') - self._banks: Dict[Tuple(int, int), bytes] = {} + self._banks: dict[tuple(int, int), bytes] = {} def add_memory(self, addr: int, blob: bytes) -> None: """Add a new memory bank. @@ -78,11 +78,11 @@ class QEMUVCPU: def __init__(self, memctrl: QEMUMemoryController): self._log = getLogger('gdbrp.vcpu') - self._seq: List[Tuple[int, str]] = [] - self._regs: List[Optional[int]] = [None] * 33 + self._seq: list[tuple[int, str]] = [] + self._regs: list[Optional[int]] = [None] * 33 self._xpos = 0 self._memctrl = memctrl - self._hwbreaks: List[range] = [] + self._hwbreaks: list[range] = [] def record(self, pc: int, func: Optional[str]) -> None: """Record execution of a single instruction. @@ -209,7 +209,7 @@ def pc(self) -> int: return last_pc + self._get_instruction_length(last_pc) @property - def regs(self) -> List[int]: + def regs(self) -> list[int]: """Return the values of the GPRs. This is a requirement of GDB. @@ -302,7 +302,7 @@ def __init__(self, rv64: Optional[bool] = None): self._select_thread_id: Optional[int] = None self._conn: Optional[socket] = None self._no_ack = False - self._vcpus: Dict[int, QEMUVCPU] = {} + self._vcpus: dict[int, QEMUVCPU] = {} self._memctrl = QEMUMemoryController() @property @@ -313,7 +313,7 @@ def xlen(self) -> int: """ return self._xlen or 4 - def load(self, qfp: TextIO, cpus: Optional[List[int]] = None) -> None: + def load(self, qfp: TextIO, cpus: Optional[list[int]] = None) -> None: """Load a recorded execution stream from a QEMU log file. see QEMU `-d exec` option. diff --git a/scripts/opentitan/gpiodev.py b/scripts/opentitan/gpiodev.py index 5cca77c9dbed5..35803d27a5626 100755 --- a/scripts/opentitan/gpiodev.py +++ b/scripts/opentitan/gpiodev.py @@ -15,7 +15,7 @@ from sys import exit as sysexit, modules, stderr from traceback import format_exc from time import sleep -from typing import Optional, TextIO, Tuple +from typing import Optional, TextIO from ot.util.log import configure_loggers from ot.util.misc import HexInt @@ -65,7 +65,7 @@ def __init__(self, parent: 'GpioChecker'): def __iter__(self): return self - def __next__(self) -> Tuple[str, int]: + def __next__(self) -> tuple[str, int]: self._pos, value = next(self._iter) return value diff --git a/scripts/opentitan/loghelp.py b/scripts/opentitan/loghelp.py index 347c90e5438be..40011d9f846c7 100755 --- a/scripts/opentitan/loghelp.py +++ b/scripts/opentitan/loghelp.py @@ -14,7 +14,7 @@ from re import compile as re_compile, sub as re_sub from sys import exit as sysexit, modules, stderr from traceback import format_exc -from typing import Dict, TextIO, Tuple +from typing import TextIO from ot.util.log import configure_loggers @@ -23,7 +23,7 @@ REG_CRE = re_compile(r'^#define ([A-Z][\w]+)_REG_(OFFSET|RESVAL)\s+' r'((?:0x)?[A-Fa-f0-9]+)(?:\s|$)') -RegisterDefs = Dict[str, Tuple[int, int]] +RegisterDefs = dict[str, tuple[int, int]] def parse_defs(hfp: TextIO) -> RegisterDefs: diff --git a/scripts/opentitan/mbbdef.py b/scripts/opentitan/mbbdef.py index 6e912ef136695..366ec94baaa3a 100755 --- a/scripts/opentitan/mbbdef.py +++ b/scripts/opentitan/mbbdef.py @@ -15,7 +15,7 @@ from pprint import pprint from sys import exit as sysexit, modules, stderr from traceback import format_exc -from typing import Dict, Iterator, List, TextIO +from typing import Iterator, TextIO from ot.util.log import configure_loggers @@ -27,7 +27,7 @@ @staticmethod -def flatten(lst: List) -> List: +def flatten(lst: list) -> list: """Flatten nested list. """ return [item for sublist in lst for item in sublist] @@ -77,18 +77,18 @@ def _parse(self, hfp: TextIO) -> None: pprint(regs) raise - def _enumerate_registers(self, values: [List, Dict]) -> Iterator[Dict]: + def _enumerate_registers(self, values: [list, dict]) -> Iterator[dict]: if isinstance(values, list): for sub in values: yield from self._enumerate_registers(sub) - if isinstance(values, Dict): + if isinstance(values, dict): if 'fields' in values: yield values else: for regs in values.values(): yield from self._enumerate_registers(regs) - def _parse_register(self, ipname: str, reg: Dict) -> None: + def _parse_register(self, ipname: str, reg: dict) -> None: regname = reg.get('name') self._log.debug('Parsing %s.%s', ipname, regname) reg_swaccess = reg.get('swaccess') diff --git a/scripts/opentitan/ot/bitfield.py b/scripts/opentitan/ot/bitfield.py index d1281ef641980..1760d767e58ae 100644 --- a/scripts/opentitan/ot/bitfield.py +++ b/scripts/opentitan/ot/bitfield.py @@ -6,7 +6,7 @@ :author: Emmanuel Blot """ -from typing import Any, Dict +from typing import Any from .util.misc import HexInt @@ -34,7 +34,7 @@ def __init__(self, *args, **kwargs): raise ValueError(f'Invalid selector name: {enum_.__name__}') self._selector = enum_ - def decode(self, value: int) -> Dict[str, Any]: + def decode(self, value: int) -> dict[str, Any]: """Decode a value into a dictionary.""" bits = dict(self._bits) if self._selector: @@ -62,7 +62,7 @@ def decode(self, value: int) -> Dict[str, Any]: values[name] = HexInt(val) return values - def encode(self, *init, **values: Dict[str, Any]) -> HexInt: + def encode(self, *init, **values: dict[str, Any]) -> HexInt: """Encode a dictionary into a value.""" if init: value = init[0] diff --git a/scripts/opentitan/ot/devproxy.py b/scripts/opentitan/ot/devproxy.py index 5053ee11c2503..77884d4866453 100644 --- a/scripts/opentitan/ot/devproxy.py +++ b/scripts/opentitan/ot/devproxy.py @@ -14,8 +14,7 @@ from sys import modules from threading import Event, Thread, get_ident from time import sleep, time as now -from typing import (Any, Callable, Dict, Iterator, List, NamedTuple, Optional, - Tuple, Union) +from typing import Any, Callable, Iterator, NamedTuple, Optional, Union from .mailbox.doe import DOEHeader @@ -135,7 +134,7 @@ def __init__(self, proxy: 'ProxyEngine', name: str, devid: int, addr: int, self._regcount = count self._offset = offset * 4 self._end = offset + count * 4 - self._interrupts: Optional[Dict[str, Tuple[int, int, int]]] = None + self._interrupts: Optional[dict[str, tuple[int, int, int]]] = None self._new() @property @@ -277,7 +276,7 @@ def capture_interrupt(self, irq: int, enable: bool): else: self.release_interrupts(mask) - def enumerate_interrupts(self, out: bool) -> Iterator[Tuple[str, int]]: + def enumerate_interrupts(self, out: bool) -> Iterator[tuple[str, int]]: """Enumerate supported interrupt lines. :param out: True to enumerate output IRQ lines, False for input ones @@ -629,7 +628,7 @@ def get_mem_pointer(self, write: int) -> int: self._log.debug('%s: 0x%08x', reg, res) return res - def get_interrupt_info(self) -> Tuple[int, int]: + def get_interrupt_info(self) -> tuple[int, int]: """Return the system-side interrupt information. :return: address and data values @@ -817,7 +816,7 @@ def write_mbox(self, pcie: DOEHeader, buffer: bytes) -> None: raise ProxyCommandError(0x402, f'Cannot write full buffer {count}/' f'{len(buffer)}') - def read_mbox(self, dwcount: int) -> Tuple[DOEHeader, bytes]: + def read_mbox(self, dwcount: int) -> tuple[DOEHeader, bytes]: """Read a DOE object from the remote mailbox. Each read word is automatically acknowledged on the remote mailbox, @@ -832,7 +831,7 @@ def read_mbox(self, dwcount: int) -> Tuple[DOEHeader, bytes]: self._log.debug('len %d', len(buf)-DOEHeader.SIZE) return pcie, buf[DOEHeader.SIZE:] - def get_interrupt_info(self) -> Tuple[int, int]: + def get_interrupt_info(self) -> tuple[int, int]: """Return the system-side interrupt information. :return: address and data values @@ -1139,12 +1138,12 @@ def __init__(self): self._resp_event = Event() self._rx_uid = 0 self._tx_uid = 0 - self._devices: Dict[str, int] = {} - self._mroots: Dict[int, MemoryRoot] = {} + self._devices: dict[str, int] = {} + self._mroots: dict[int, MemoryRoot] = {} self._request_handler: Optional[RequestHandler] = None - self._request_args: List[Any] = [] + self._request_args: list[Any] = [] self._notifier_handler: Optional[RequestHandler] = None - self._notifier_args: List[Any] = [] + self._notifier_args: list[Any] = [] self._proxies = self._discover_proxies() def connect(self, host: str, port: int) -> None: @@ -1559,7 +1558,7 @@ def _handshake(self): raise ValueError('Unsuppported version: {vmaj}.{vmin}') @classmethod - def _discover_proxies(cls) -> Dict[str, DeviceProxy]: + def _discover_proxies(cls) -> dict[str, DeviceProxy]: """Create a map of device proxies. """ proxymap = {} diff --git a/scripts/opentitan/ot/dm/dm.py b/scripts/opentitan/ot/dm/dm.py index 6348e64c0e1a3..32dd5d090cfd4 100644 --- a/scripts/opentitan/ot/dm/dm.py +++ b/scripts/opentitan/ot/dm/dm.py @@ -10,7 +10,7 @@ from io import SEEK_END from logging import getLogger from time import sleep, time as now -from typing import Any, BinaryIO, Dict, Optional +from typing import Any, BinaryIO, dict, Optional from .regs import CSRS, GPRS from ..bitfield import BitField @@ -174,14 +174,14 @@ def __init__(self, dtm: DebugTransportModule, address: int): self._dmi = dtm['dmi'] self._address = address self._hart: int = 0 # currently selected hart - self._cache: Dict[str, int] = {} + self._cache: dict[str, int] = {} def restart_system(self) -> None: """Restart the remote machine.""" self._dtm.engine.controller.system_reset() @classmethod - def decode(cls, name: str, value: int) -> Dict[str, Any]: + def decode(cls, name: str, value: int) -> dict[str, Any]: """Decode a bitfield register.""" bitfield = cls.BITFIELDS.get(f'{name.upper()}') if not bitfield: @@ -210,21 +210,21 @@ def initialize(self) -> None: self._log.warning('Detected incompatible DM version %r', version) @property - def status(self) -> Dict[str, int]: + def status(self) -> dict[str, int]: """Report debug module status.""" btf = self.BITFIELDS['DMSTATUS'] # TODO would need to check if another hart needs to be selected first return btf.decode(self.dmstatus) @property - def hart_info(self) -> Dict[str, int]: + def hart_info(self) -> dict[str, int]: """Report current hart information.""" btf = self.BITFIELDS['HARTINFO'] # TODO would need to check if another hart needs to be selected first return btf.decode(self.hartinfo) @property - def system_bus_info(self) -> Dict[str, int]: + def system_bus_info(self) -> dict[str, int]: """Report system bus capabilities.""" btf = self.BITFIELDS['SBCS'] return btf.decode(self.sbcs) diff --git a/scripts/opentitan/ot/dm/otp.py b/scripts/opentitan/ot/dm/otp.py index 2236f353c33c7..3f8b34745fdef 100644 --- a/scripts/opentitan/ot/dm/otp.py +++ b/scripts/opentitan/ot/dm/otp.py @@ -10,7 +10,7 @@ from enum import IntEnum from logging import getLogger from time import sleep, time as now -from typing import Dict, Optional, Tuple +from typing import Optional from .dm import DebugModule from ..bitfield import BitField @@ -127,10 +127,10 @@ def __init__(self, dbgmod: DebugModule, base: int): self._base = base self._max_addr = self.BITFIELDS['ADDRESS'].encode(address=-1) self._map: Optional[OtpMap] = None - self._partitions: Dict[str, OtpPartition] = {} - self._item_offsets: Dict[str, # partition name - Dict[str, # item name - Tuple[int, # offset + self._partitions: dict[str, OtpPartition] = {} + self._item_offsets: dict[str, # partition name + dict[str, # item name + tuple[int, # offset int]]] = {} # size def set_map(self, otpmap: 'OtpMap'): @@ -153,8 +153,8 @@ def get_hw_partition_digest(self, partname: str) -> int: self._log.info('%s HW digest %016x', partname, digest) return digest - def get_hw_partition_digests(self) -> Dict[str, int]: - digests: Dict[str, int] = {} + def get_hw_partition_digests(self) -> dict[str, int]: + digests: dict[str, int] = {} for name, part in self._partitions.items(): if not part.hw_digest: continue diff --git a/scripts/opentitan/ot/dtm/dtm.py b/scripts/opentitan/ot/dtm/dtm.py index 69dee91a33884..9dc038f3460a5 100644 --- a/scripts/opentitan/ot/dtm/dtm.py +++ b/scripts/opentitan/ot/dtm/dtm.py @@ -9,7 +9,7 @@ from logging import getLogger from sys import modules -from typing import Dict, Optional, Tuple +from typing import Optional # pylint: disable=import-error from jtag.bits import BitSequence # noqa: E402 @@ -115,7 +115,7 @@ def version(self) -> int: return version @property - def dmi_version(self) -> Tuple[int, int]: + def dmi_version(self) -> tuple[int, int]: """Version of the supported DMI specification.""" try: tversion = {0: (0, 11), 1: (0, 13)}[self.version] @@ -304,7 +304,7 @@ def abits(self) -> int: raise DMIError('Missing DTMCS register') from exc return dtmcs.abits - def _load_registers(self) -> Dict[str, DTMRegister]: + def _load_registers(self) -> dict[str, DTMRegister]: module = modules[__name__] regs = {} for name in dir(module): diff --git a/scripts/opentitan/ot/lc_ctrl/lcdmi.py b/scripts/opentitan/ot/lc_ctrl/lcdmi.py index 4b8f73a11f0dd..425c932407559 100644 --- a/scripts/opentitan/ot/lc_ctrl/lcdmi.py +++ b/scripts/opentitan/ot/lc_ctrl/lcdmi.py @@ -9,7 +9,7 @@ from binascii import Error as BinasciiError, hexlify, unhexlify from logging import getLogger from struct import pack as spack, unpack as sunpack -from typing import Dict, NamedTuple, Tuple +from typing import NamedTuple from . import LifeCycleError from ..dtm import DebugTransportModule @@ -100,7 +100,7 @@ def set_alert_test(self, alert: str) -> None: self._write_reg('alert_test', 1 << alix) @property - def status(self) -> Dict[str, bool]: + def status(self) -> dict[str, bool]: """Retrieve the current status.""" value = self._read_reg('status') status = {self.STATUS[b]: bool(value & (1 << b)) @@ -205,7 +205,7 @@ def transition_token(self, token: [bytes | bytearray | str | None]) -> None: self._write_reg('transition_token', tok, tix) @property - def transition_target(self) -> Tuple[str, int]: + def transition_target(self) -> tuple[str, int]: """Read back the transition target.""" target = self._read_reg('transition_target') starget = self._decode_state(target) diff --git a/scripts/opentitan/ot/mailbox/doe.py b/scripts/opentitan/ot/mailbox/doe.py index 003a9d5538d8a..0579c1764c3b6 100644 --- a/scripts/opentitan/ot/mailbox/doe.py +++ b/scripts/opentitan/ot/mailbox/doe.py @@ -8,7 +8,6 @@ from logging import getLogger from struct import calcsize as scalc, pack as spack, unpack as sunpack -from typing import Tuple from .sysmbox import SysMbox, SysMboxError @@ -134,7 +133,7 @@ def write(self, oid: int, msg: bytes) -> None: if self._mbox.on_error: raise SysMboxError('JTAG mailbox on error') - def read(self) -> Tuple[int, bytes]: + def read(self) -> tuple[int, bytes]: """Receive a DOE message.""" resp = self._mbox.read() if not resp: diff --git a/scripts/opentitan/ot/otp/descriptor.py b/scripts/opentitan/ot/otp/descriptor.py index 6901d23c8bef4..a4281d6aef7bd 100644 --- a/scripts/opentitan/ot/otp/descriptor.py +++ b/scripts/opentitan/ot/otp/descriptor.py @@ -7,7 +7,7 @@ """ from logging import getLogger -from typing import TYPE_CHECKING, List, TextIO, Tuple +from typing import TYPE_CHECKING, TextIO if TYPE_CHECKING: from .map import OtpMap @@ -88,7 +88,7 @@ def _convert_to_bool(cls, value) -> str: return str(value).lower() @classmethod - def _convert_to_buffer(cls, value) -> Tuple[str, bool]: + def _convert_to_buffer(cls, value) -> tuple[str, bool]: return { 'unbuffered': ('buffered', False), 'buffered': ('buffered', True), @@ -100,7 +100,7 @@ def _convert_to_wlock(cls, value) -> bool: return value == 'digest' @classmethod - def _convert_to_rlock(cls, value) -> List[Tuple[str, bool]]: + def _convert_to_rlock(cls, value) -> list[tuple[str, bool]]: value = value.lower() if value == 'csr': return [('read_lock_csr', True), ('read_lock', True)] diff --git a/scripts/opentitan/ot/otp/image.py b/scripts/opentitan/ot/otp/image.py index 0fd4245504da5..ceda6cfea582d 100644 --- a/scripts/opentitan/ot/otp/image.py +++ b/scripts/opentitan/ot/otp/image.py @@ -11,7 +11,7 @@ from logging import getLogger from re import match as re_match, sub as re_sub from struct import calcsize as scalc, pack as spack, unpack as sunpack -from typing import Any, BinaryIO, Dict, List, Optional, Set, TextIO +from typing import Any, BinaryIO, Optional, TextIO from .map import OtpMap from .partition import OtpPartition, OtpLifecycleExtension @@ -48,7 +48,7 @@ class OtpImage: def __init__(self, ecc_bits: Optional[int] = None): self._log = getLogger('otptool.img') - self._header: Dict[str, Any] = {} + self._header: dict[str, Any] = {} self._magic = b'' self._data = b'' self._ecc = b'' @@ -59,7 +59,7 @@ def __init__(self, ecc_bits: Optional[int] = None): self._ecc_granule = 0 self._digest_iv: Optional[int] = None self._digest_constant: Optional[int] = None - self._partitions: List[OtpPartition] = [] + self._partitions: list[OtpPartition] = [] @property def version(self) -> int: @@ -77,7 +77,7 @@ def is_opentitan(self) -> bool: return self._magic == b'vOTP' @classproperty - def vmem_kinds(cls) -> List[str]: + def vmem_kinds(cls) -> list[str]: """Reports the supported content kinds of VMEM files.""" # pylint: disable=no-self-argument return ['auto'] + list(cls.KINDS.values()) @@ -111,10 +111,10 @@ def save_raw(self, rfp: BinaryIO) -> None: def load_vmem(self, vfp: TextIO, vmem_kind: Optional[str] = None, swap: bool = True): """Parse a VMEM '24' text stream.""" - data_buf: List[bytes] = [] - ecc_buf: List[bytes] = [] + data_buf: list[bytes] = [] + ecc_buf: list[bytes] = [] last_addr = 0 - granule_sizes: Set[int] = set() + granule_sizes: set[int] = set() vkind: Optional[str] = None row_count = 0 byte_count = 0 @@ -226,7 +226,7 @@ def verify(self, show: bool = False) -> bool: """Verify the partition digests, if any.""" if any(c is None for c in (self._digest_iv, self._digest_constant)): raise RuntimeError('Missing Present constants') - results: Dict[str, Optional[bool]] = {} + results: dict[str, Optional[bool]] = {} for part in self._partitions: if not part.hw_digest: continue @@ -258,7 +258,7 @@ def decode(self, decode: bool = True, wide: int = 0, for part in self._partitions: part.decode(decode, wide, ofp) - def _load_header(self, bfp: BinaryIO) -> Dict[str, Any]: + def _load_header(self, bfp: BinaryIO) -> dict[str, Any]: hfmt = self.HEADER_FORMAT fhfmt = ''.join(hfmt.values()) # hlength is the length of header minus the two first items (T, L) diff --git a/scripts/opentitan/ot/otp/map.py b/scripts/opentitan/ot/otp/map.py index 76ca22d38b0d0..cb0e54df1d72b 100644 --- a/scripts/opentitan/ot/otp/map.py +++ b/scripts/opentitan/ot/otp/map.py @@ -7,7 +7,7 @@ """ from logging import getLogger -from typing import Any, Dict, Iterator, List, Optional, TextIO, Tuple +from typing import Any, Iterator, Optional, TextIO try: # try to load HJSON if available @@ -39,9 +39,9 @@ class OtpMap: def __init__(self): self._log = getLogger('otptool.map') - self._map: Dict = {} + self._map: dict = {} self._otp_size = 0 - self._partitions: List[OtpPartition] = [] + self._partitions: list[OtpPartition] = [] def load(self, hfp: TextIO) -> None: """Parse a HJSON configuration file, typically otp_ctrl_mmap.hjson @@ -55,12 +55,12 @@ def load(self, hfp: TextIO) -> None: self._compute_locations() @property - def partitions(self) -> Dict[str, Any]: + def partitions(self) -> dict[str, Any]: """Return the partitions (in any)""" return {p['name']: p for p in self._map.get('partitions', [])} @classmethod - def part_offset(cls, part: Dict[str, Any]) -> int: + def part_offset(cls, part: dict[str, Any]) -> int: """Get the offset of a partition.""" # expect a KeyError if missing return int(part['offset']) @@ -136,10 +136,10 @@ def _generate_partitions(self) -> None: {'name': name, '__doc__': desc}) self._partitions.append(newpart(part)) - def _check_keymgr_materials(self, partname: str, items: Dict[str, Dict]) \ - -> Optional[Tuple[str, bool]]: + def _check_keymgr_materials(self, partname: str, items: dict[str, dict]) \ + -> Optional[tuple[str, bool]]: """Check partition for key manager material fields.""" - kms: Dict[str, bool] = {} + kms: dict[str, bool] = {} kmprefix = 'iskeymgr' for props in items.values(): for prop, value in props.items(): diff --git a/scripts/opentitan/ot/otp/partition.py b/scripts/opentitan/ot/otp/partition.py index 342c3addd4a04..54cb6f30fba1f 100644 --- a/scripts/opentitan/ot/otp/partition.py +++ b/scripts/opentitan/ot/otp/partition.py @@ -12,7 +12,7 @@ from os.path import basename from re import match as re_match from textwrap import fill -from typing import BinaryIO, Dict, List, Optional, TextIO +from typing import BinaryIO, Optional, TextIO try: # try to load Present if available @@ -185,7 +185,7 @@ class OtpLifecycleExtension(OtpPartitionDecoder): def __init__(self): self._log = getLogger('otptool.lc') - self._tables: Dict[str, Dict[str, str]] = {} + self._tables: dict[str, dict[str, str]] = {} def decode(self, category: str, seq: str) -> Optional[str | int]: return self._tables.get(category, {}).get(seq, None) @@ -198,8 +198,8 @@ def load(self, svp: TextIO): ab_re = (r"\s*parameter\s+logic\s+\[\d+:\d+\]\s+" r"([ABCD]\d+|ZRO)\s+=\s+\d+'(b(?:[01]+)|h(?:[0-9a-fA-F]+));") tbl_re = r"\s*Lc(St|Cnt)(\w+)\s+=\s+\{([^\}]+)\}\s*,?" - codes: Dict[str, int] = {} - sequences: Dict[str, List[str]] = {} + codes: dict[str, int] = {} + sequences: dict[str, list[str]] = {} for line in svp: cmt = line.find('//') if cmt >= 0: diff --git a/scripts/opentitan/ot/util/elf.py b/scripts/opentitan/ot/util/elf.py index 59e9e427c30f3..706341382e329 100644 --- a/scripts/opentitan/ot/util/elf.py +++ b/scripts/opentitan/ot/util/elf.py @@ -8,7 +8,7 @@ from io import BytesIO from logging import getLogger -from typing import BinaryIO, Iterator, Optional, Tuple +from typing import BinaryIO, Iterator, Optional try: # note: pyelftools package is an OpenTitan toolchain requirement, see @@ -107,7 +107,7 @@ def blob(self) -> bytes: return self._payload @property - def code_span(self) -> Tuple[int, int]: + def code_span(self) -> tuple[int, int]: """Report the extent of the executable portion of the ELF file. :return: (start address, end address) @@ -154,7 +154,7 @@ def _loadable_segments(self) -> Iterator['Segment']: continue yield segment - def _parse_segments(self) -> Tuple[int, int]: + def _parse_segments(self) -> tuple[int, int]: """Parse ELF segments and extract physical location and size. :return: the location of the first byte and the overall payload size diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 0ac26b29c19a2..997ef508220e2 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -8,7 +8,6 @@ from os import isatty from sys import stderr -from typing import List, Tuple import logging @@ -77,8 +76,8 @@ def format(self, record): return formatter.format(record) -def configure_loggers(level: int, *lognames: List[str], **kwargs) \ - -> List[logging.Logger]: +def configure_loggers(level: int, *lognames: list[str], **kwargs) \ + -> list[logging.Logger]: """Configure loggers. :param level: level (stepping: 1) @@ -98,8 +97,8 @@ def configure_loggers(level: int, *lognames: List[str], **kwargs) \ formatter = ColorLogFormatter(**kwargs) logh = logging.StreamHandler(stderr) logh.setFormatter(formatter) - loggers: List[logging.Logger] = [] - logdefs: List[Tuple[List[str], logging.Logger]] = [] + loggers: list[logging.Logger] = [] + logdefs: list[tuple[list[str], logging.Logger]] = [] for logdef in lognames: if isinstance(logdef, int): loglevel += -10 * logdef diff --git a/scripts/opentitan/present.py b/scripts/opentitan/present.py index 0dfd20491c908..a02d363a05084 100644 --- a/scripts/opentitan/present.py +++ b/scripts/opentitan/present.py @@ -42,7 +42,7 @@ '0x123456789abcdef' """ -from typing import List +from typing import list def _tinvert(tpl): @@ -107,7 +107,7 @@ def decrypt(self, block: int) -> int: return decipher @classmethod - def _generate_roundkeys(cls, key: int, rounds: int) -> List[int]: + def _generate_roundkeys(cls, key: int, rounds: int) -> list[int]: """Generate the roundkeys for a 128-bit key :param key: the key as a 128-bit integer diff --git a/scripts/opentitan/uartmux.py b/scripts/opentitan/uartmux.py index c6dfc27ae284f..42e8ba911996b 100755 --- a/scripts/opentitan/uartmux.py +++ b/scripts/opentitan/uartmux.py @@ -15,7 +15,7 @@ from sys import exit as sysexit, modules, stderr, stdout from threading import Event, Lock, Thread from traceback import format_exc -from typing import Deque, List, Optional, Set, TextIO, Tuple +from typing import Optional, TextIO from ot.util.log import configure_loggers @@ -87,7 +87,7 @@ class UartMuxer(ThreadingTCPServer): COLOR_SUFFIX = ';1m' RESET = '\x1b[0m' - def __init__(self, addr: Tuple[str, int], out: TextIO, debug: bool = False, + def __init__(self, addr: tuple[str, int], out: TextIO, debug: bool = False, separator: Optional[str] = None): super().__init__(addr, UartHandler) self._out = out @@ -95,13 +95,13 @@ def __init__(self, addr: Tuple[str, int], out: TextIO, debug: bool = False, self._log = getLogger('mux.muxer') self._runner = Thread(target=self._pop, daemon=True) self._resume = False - self._que: Deque[Tuple[int, bytes]] = deque() + self._que: deque[tuple[int, bytes]] = deque() self._evt = Event() self._lock = Lock() - self._handlers: List[UartHandler] = [] - self._discarded: Set[UartHandler] = set() + self._handlers: list[UartHandler] = [] + self._discarded: set[UartHandler] = set() self._channel_count = 1 - self._channels: List[str] = [] + self._channels: list[str] = [] self._use_color = out.isatty() if separator: sep = separator if ' ' in separator else (separator * 80)[:80] @@ -129,7 +129,7 @@ def resume(self, value: bool): else: self._resume = value - def run(self, channel_count: int, channels: List[str]) -> None: + def run(self, channel_count: int, channels: list[str]) -> None: """Start listening on QEMU streams.""" self._channel_count = min(channel_count, 8) self._channels = channels From fe4c96dcc8f4a9d8a85cc881ae4ace84c72e50cc Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 23 May 2024 19:59:25 +0200 Subject: [PATCH 45/65] [ot] scripts/jtag: fix deprecated Python type hinting syntax Signed-off-by: Emmanuel Blot --- scripts/jtag/bits.py | 8 ++++---- scripts/jtag/jtag.py | 10 +++++----- scripts/opentitan/ot/dm/dm.py | 2 +- scripts/opentitan/present.py | 2 -- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/jtag/bits.py b/scripts/jtag/bits.py index 9ce2906f73f19..23153134f8eb2 100644 --- a/scripts/jtag/bits.py +++ b/scripts/jtag/bits.py @@ -9,7 +9,7 @@ Imported from pyftdi project. """ -from typing import Any, Iterable, List, Union +from typing import Any, Iterable, Union class BitSequenceError(Exception): @@ -258,7 +258,7 @@ def to_bytes(self) -> bytes: """ return bytes((int(b) for b in self)) - def to_bool_list(self) -> List[bool]: + def to_bool_list(self) -> list[bool]: """Convert the sequence into a list of boolean values. >>> BitSequence(0xC4, 8).to_bool_list() @@ -280,7 +280,7 @@ def to_bytestream(self, msb: bool = False, msby: bool = False) -> bytes: >>> hexlify(BitSequence(0xC4A5D0, 24).to_bytestream(False, False)) b'0ba523' """ - out: List[int] = [] + out: list[int] = [] bseq = BitSequence(self) if not msb: bseq.reverse() @@ -758,7 +758,7 @@ def __getitem__(self, index) -> 'BitSequence': [0, 1, 0] """ if isinstance(index, slice): - bits: List[int] = [] + bits: list[int] = [] for bpos in range(index.start or 0, index.stop or 0, index.step or 1): if bpos >= self._width: diff --git a/scripts/jtag/jtag.py b/scripts/jtag/jtag.py index 3c64a5a9e062b..769c00475425f 100644 --- a/scripts/jtag/jtag.py +++ b/scripts/jtag/jtag.py @@ -10,7 +10,7 @@ """ from logging import getLogger -from typing import Dict, List, Tuple, Union +from typing import Union from .bits import BitSequence @@ -26,7 +26,7 @@ class JtagState: :param modes: categories to which the state belongs """ - def __init__(self, name: str, modes: Tuple[str, str]): + def __init__(self, name: str, modes: tuple[str, str]): self.name = name self.modes = modes self.exits = [self, self] # dummy value before initial configuration @@ -99,7 +99,7 @@ def __init__(self): self['exit_2_ir'].setx(self['shift_ir'], self['update_ir']) self['update_ir'].setx(self['run_test_idle'], self['select_dr_scan']) self._current = self['test_logic_reset'] - self._tr_cache: Dict[Tuple[str, # current state name + self._tr_cache: dict[tuple[str, # current state name int, # event length int], # event value JtagState] = {} # new state @@ -122,7 +122,7 @@ def reset(self): def find_path(self, target: Union[JtagState, str], source: Union[JtagState, str, None] = None) \ - -> List[JtagState]: + -> list[JtagState]: """Find the shortest event sequence to move from source state to target state. If source state is not specified, used the current state. @@ -241,7 +241,7 @@ def __init__(self, ctrl: 'JtagController'): self._ctrl = ctrl self._log = getLogger('jtag.eng') self._fsm = JtagStateMachine() - self._tr_cache: Dict[Tuple[str, # from state + self._tr_cache: dict[tuple[str, # from state str], # to state BitSequence] = {} # TMS sequence self._seq = bytearray() diff --git a/scripts/opentitan/ot/dm/dm.py b/scripts/opentitan/ot/dm/dm.py index 32dd5d090cfd4..910b85cf8ea4d 100644 --- a/scripts/opentitan/ot/dm/dm.py +++ b/scripts/opentitan/ot/dm/dm.py @@ -10,7 +10,7 @@ from io import SEEK_END from logging import getLogger from time import sleep, time as now -from typing import Any, BinaryIO, dict, Optional +from typing import Any, BinaryIO, Optional from .regs import CSRS, GPRS from ..bitfield import BitField diff --git a/scripts/opentitan/present.py b/scripts/opentitan/present.py index a02d363a05084..9bf67a7e691e4 100644 --- a/scripts/opentitan/present.py +++ b/scripts/opentitan/present.py @@ -42,8 +42,6 @@ '0x123456789abcdef' """ -from typing import list - def _tinvert(tpl): return tuple(tpl.index(x) for x in range(len(tpl))) From cb6bfa664f59107a3e64b6367bea45fabd3989b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 09:29:26 +0200 Subject: [PATCH 46/65] [ot] scripts/opentitan: pyot.py: simplify argument management Count of arguments exchanged between a couple of methods in pyot.py as grown to a hardly readable list. Replace it with a dictionary which is easier to debug and extend with new features. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 120 ++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 42b215f816b18..6280928cac4e2 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -36,6 +36,7 @@ from typing import Any, Iterator, NamedTuple, Optional from ot.util.log import configure_loggers +from ot.util.misc import EasyDict DEFAULT_MACHINE = 'ot-earlgrey' @@ -144,21 +145,26 @@ def __init__(self, tcpdev: tuple[str, int], debug: bool): self._qlog = getLogger('pyot.qemu') self._vcplog = getLogger('pyot.vcp') - def run(self, qemu_args: list[str], timeout: int, name: str, - ctx: Optional['QEMUContext'], start_delay: float, - trigger: str = None) -> \ - tuple[int, ExecTime, str]: + def run(self, tdef: EasyDict[str, Any]) -> tuple[int, ExecTime, str]: """Execute the specified QEMU command, aborting execution if QEMU does not exit after the specified timeout. - :param qemu_args: QEMU argument list (first arg is the path to QEMU) - :param timeout: the delay in seconds after which the QEMU session - is aborted - :param name: the tested application name - :param ctx: execution context, if any - :param start_delay: start up delay - :param trigger: if not empty, the string to wait for triggering - 'with' context execution + :param tdef: test definition and parameters + - command, a list of strings defining the QEMU command to + execute with all its options + - timeout, the allowed time for the command to execute, + specified as a real number + - expect_result, the expected outcome of QEMU (exit code). Some + tests may expect that QEMU terminates with a non-zero + exit code + - context, an option QEMUContextWorker instance, to execute + concurrently with the QEMU process. Many tests + expect to communicate with the QEMU process. + - trigger, a string to match on the QEMU virtual comm port + output to trigger the context execution. It may be + defined as a regular expression. + - start_delay, the delay to wait before starting the execution + of the context once QEMU command has been started. :return: a 3-uple of exit code, execution time, and last guest error """ # stdout and stderr belongs to QEMU VM @@ -167,18 +173,18 @@ def run(self, qemu_args: list[str], timeout: int, name: str, xre = re_compile(self.EXIT_ON) otre = r'^([' + ''.join(self.LOG_LEVELS.keys()) + r'])\d{5}\s' lre = re_compile(otre) - if trigger: + if tdef.trigger: sync_event = Event() - if trigger.startswith("r'") and trigger.endswith("'"): + if tdef.trigger.startswith("r'") and tdef.trigger.endswith("'"): try: - tmo = re_compile(trigger[2:-1].encode()) + tmo = re_compile(tdef.trigger[2:-1].encode()) except re_error as exc: raise ValueError('Invalid trigger regex: {exc}') from exc def trig_match(bline): return tmo.match(bline) else: - btrigger = trigger.encode() + btrigger = tdef.trigger.encode() def trig_match(bline): return bline.find(btrigger) >= 0 @@ -193,22 +199,22 @@ def trig_match(bline): log = self._log last_error = '' try: - workdir = dirname(qemu_args[0]) - log.debug('Executing QEMU as %s', ' '.join(qemu_args)) + workdir = dirname(tdef.command[0]) + log.debug('Executing QEMU as %s', ' '.join(tdef.command)) # pylint: disable=consider-using-with - proc = Popen(qemu_args, bufsize=1, cwd=workdir, stdout=PIPE, + proc = Popen(tdef.command, bufsize=1, cwd=workdir, stdout=PIPE, stderr=PIPE, encoding='utf-8', errors='ignore', text=True) try: # ensure that QEMU starts and give some time for it to set up # its VCP before attempting to connect to it - self._log.debug('Waiting %.1fs for VM init', start_delay) - proc.wait(start_delay) + self._log.debug('Waiting %.1fs for VM init', tdef.start_delay) + proc.wait(tdef.start_delay) except TimeoutExpired: pass else: ret = proc.returncode - log.error('QEMU bailed out: %d for "%s"', ret, name) + log.error('QEMU bailed out: %d for "%s"', ret, tdef.test_name) raise OSError() sock = socket() log.debug('Connecting QEMU VCP as %s:%d', *self._device) @@ -221,7 +227,7 @@ def trig_match(bline): raise # timeout for communicating over VCP sock.settimeout(0.05) - log.debug('Execute QEMU for %.0f secs', timeout) + log.debug('Execute QEMU for %.0f secs', tdef.timeout) vcp_buf = bytearray() # unfortunately, subprocess's stdout calls are blocking, so the # only way to get near real-time output from QEMU is to use a @@ -236,10 +242,9 @@ def trig_match(bline): Thread(target=self._qemu_logger, name='qemu_err_logger', args=(proc, log_q, False)).start() xstart = now() - # sync_event.set() - if ctx: + if tdef.context: try: - ctx.execute('with', sync=sync_event) + tdef.context.execute('with', sync=sync_event) except OSError as exc: ret = exc.errno last_error = exc.strerror @@ -249,7 +254,7 @@ def trig_match(bline): ret = 126 last_error = str(exc) raise - abstimeout = float(timeout) + now() + abstimeout = float(tdef.timeout) + now() while now() < abstimeout: while log_q: err, qline = log_q.popleft() @@ -261,8 +266,8 @@ def trig_match(bline): else: level = INFO self._qlog.log(level, qline) - if ctx: - wret = ctx.check_error() + if tdef.context: + wret = tdef.context.check_error() if wret: ret = wret last_error = 'Fail to execute worker' @@ -274,7 +279,7 @@ def trig_match(bline): ret = xret if ret != 0: log.critical('Abnormal QEMU termination: %d for "%s"', - ret, name) + ret, tdef.test_name) break try: data = sock.recv(4096) @@ -290,8 +295,8 @@ def trig_match(bline): if trig_match and trig_match(line): self._log.info('Trigger pattern detected') # reset timeout from this event - abstimeout = float(timeout) + now() - log.debug('Resuming for %.0f secs', timeout) + abstimeout = float(tdef.timeout) + now() + log.debug('Resuming for %.0f secs', tdef.timeout) sync_event.set() trig_match = None xmo = xre.search(line) @@ -323,7 +328,7 @@ def trig_match(bline): # match break if ret is None: - log.warning('Execution timed out for "%s"', name) + log.warning('Execution timed out for "%s"', tdef.test_name) ret = 124 # timeout except (OSError, ValueError) as exc: if ret is None: @@ -997,7 +1002,9 @@ def build(self) -> None: :raise ValueError: if some argument is invalid """ - self._qemu_cmd, self._vcp = self._build_qemu_command(self._args)[:2] + exec_info = self._build_qemu_command(self._args) + self._qemu_cmd = exec_info.command + self._vcp = exec_info.connection self._argdict = dict(self._args.__dict__) self._suffixes = [] suffixes = self._config.get('suffixes', []) @@ -1063,15 +1070,14 @@ def run(self, debug: bool, allow_no_test: bool) -> int: 'UTFILE': basename(test), }) test_name = self.get_test_radix(test) - qemu_cmd, targs, timeout, temp_files, ctx, sdelay, texp, trig = \ - self._build_qemu_test_command(test) - ctx.execute('pre') - tret, xtime, err = qot.run(qemu_cmd, timeout, test_name, - ctx, sdelay, trig) - cret = ctx.finalize() - ctx.execute('post', tret) - if texp != 0: - if tret == texp: + exec_info = self._build_qemu_test_command(test) + exec_info.test_name = test_name + exec_info.context.execute('pre') + tret, xtime, err = qot.run(exec_info) + cret = exec_info.context.finalize() + exec_info.context.execute('post', tret) + if exec_info.expect_result != 0: + if tret == exec_info.expect_result: self._log.info('QEMU failed with expected error, ' 'assume success') tret = 0 @@ -1182,17 +1188,12 @@ def _cleanup_temp_files(self, storage: dict[str, set[str]]) -> None: def _build_qemu_command(self, args: Namespace, opts: Optional[list[str]] = None) \ - -> tuple[list[str], tuple[str, int], dict[str, set[str]], float, - str]: + -> EasyDict[str, Any]: """Build QEMU command line from argparser values. :param args: the parsed arguments :param opts: any QEMU-specific additional options - :return: a tuple of a list of QEMU command line, - the TCP device descriptor to connect to the QEMU VCP, - a dictionary of generated temporary files and the start - delay, the start delay and a trigger string which may be - empty + :return: a dictionary defining how to execute the command """ if args.qemu is None: raise ValueError('QEMU path is not defined') @@ -1321,18 +1322,21 @@ def _build_qemu_command(self, args: Namespace, qemu_args.extend(args.global_opts or []) if opts: qemu_args.extend((str(o) for o in opts)) - return qemu_args, tcpdev, temp_files, start_delay, trigger + return EasyDict(command=qemu_args, connection=tcpdev, + tmpfiles=temp_files, start_delay=start_delay, + trigger=trigger) - def _build_qemu_test_command(self, filename: str) -> \ - tuple[list[str], Namespace, int, dict[str, set[str]], QEMUContext, - float, int, str]: + def _build_qemu_test_command(self, filename: str) -> EasyDict[str, Any]: test_name = self.get_test_radix(filename) args, opts, timeout, texp = self._build_test_args(test_name) setattr(args, 'exec', filename) - qemu_cmd, _, temp_files, sdelay, trig = self._build_qemu_command(args, - opts) - ctx = self._build_test_context(test_name) - return qemu_cmd, args, timeout, temp_files, ctx, sdelay, texp, trig + exec_info = self._build_qemu_command(args, opts) + exec_info.pop('connection', None) + exec_info.args = args + exec_info.context = self._build_test_context(test_name) + exec_info.timeout = timeout + exec_info.expect_result = texp + return exec_info def _build_test_list(self, alphasort: bool = True) -> list[str]: pathnames = set() From 7512635e28de7f2853185a666c7b6e789aaadb5d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 09:30:38 +0200 Subject: [PATCH 47/65] [ot] scripts/opentitan: pyot.py: fix the 'debug' option. debug may be None, overriding the pre-parsed default setting. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 6280928cac4e2..9951032660ec3 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1621,7 +1621,8 @@ def main(): opts = [] cli_opts = list(opts) args = argparser.parse_args(sargv) - debug = args.debug + if args.debug is not None: + debug = args.debug if args.summary and not args.result: tmpfd, tmp_result = mkstemp(suffix='.csv') close(tmpfd) From 66b2f339f87b9c8698f0f203994ffcde94400b10 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 09:49:57 +0200 Subject: [PATCH 48/65] [ot] scripts/opentitan: pyot.py: improve handling of expected failures - do not use critical log level for expected failure - post context execution should be performed if failure matches expected one Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 9951032660ec3..1a507741d117c 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -278,8 +278,12 @@ def trig_match(bline): xend = now() ret = xret if ret != 0: - log.critical('Abnormal QEMU termination: %d for "%s"', - ret, tdef.test_name) + if ret != tdef.expect_result: + logfn = getattr(log, 'critical') + else: + logfn = getattr(log, 'warning') + logfn('Abnormal QEMU termination: %d for "%s"', + ret, tdef.test_name) break try: data = sock.recv(4096) @@ -1075,7 +1079,6 @@ def run(self, debug: bool, allow_no_test: bool) -> int: exec_info.context.execute('pre') tret, xtime, err = qot.run(exec_info) cret = exec_info.context.finalize() - exec_info.context.execute('post', tret) if exec_info.expect_result != 0: if tret == exec_info.expect_result: self._log.info('QEMU failed with expected error, ' @@ -1087,6 +1090,7 @@ def run(self, debug: bool, allow_no_test: bool) -> int: tret = 98 if tret == 0 and cret != 0: tret = 99 + exec_info.context.execute('post', tret) # pylint: disable=broad-except except Exception as exc: self._log.critical('%s', str(exc)) From 9b66e1e85bf1644ed7e808f8ed18fa78f3d8beae Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 10:03:23 +0200 Subject: [PATCH 49/65] [ot] scripts/opentitan: .pylintrc: discard just another 'too-many-*` warning Signed-off-by: Emmanuel Blot --- scripts/opentitan/.pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/opentitan/.pylintrc b/scripts/opentitan/.pylintrc index 4ee3afd2c601d..7ee6e793e253c 100644 --- a/scripts/opentitan/.pylintrc +++ b/scripts/opentitan/.pylintrc @@ -4,6 +4,7 @@ disable= too-few-public-methods, too-many-arguments, too-many-branches, + too-many-function-args, too-many-instance-attributes, too-many-lines, too-many-locals, From 4b41715f8f7a66825327196527944679ec79ed52 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 09:54:28 +0200 Subject: [PATCH 50/65] [ot] .gitlab-ci.d: update with latest test configuration Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index 18b63275a408b..fd7823203baaf 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240522-1" + BAREMETAL_REF: "240529-1" QEMU_BUILD_OPTS: "" include: From f54adbc1906059bfc996fb5c775b3c2332c04058 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 24 May 2024 14:59:23 +0200 Subject: [PATCH 51/65] [ot] docs/opentitan: add a dedicated doc file for Python modules Signed-off-by: Emmanuel Blot --- docs/opentitan/pymod.md | 20 ++++++++++++++++++++ docs/opentitan/tools.md | 4 +--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 docs/opentitan/pymod.md diff --git a/docs/opentitan/pymod.md b/docs/opentitan/pymod.md new file mode 100644 index 0000000000000..77a2ca11b2618 --- /dev/null +++ b/docs/opentitan/pymod.md @@ -0,0 +1,20 @@ +# Python modules for OpenTitan machines + +The communication ports of the OpenTitan machines exposed through CharDev devices can be used +to access the devices from the local host (or also from a remote host when the CharDev is created +using a TCP socket). + +* `scripts/jtag`: JTAG / TAP controller client, using the _Remote BitBang Protocol_ +* `scripts/opentitan/ot`: OpenTitan tools (common to OpenTitan and Rivos SCS machines) + * `dtm`: Debug Transport Module support, + * `dm`: RISC-V Debug Module support, + * `lc_ctrl`: [Life Cycle controller](lc_ctrl_dmi.md) over JTAG/DMI support, + * `mailbox`: support for accessing the responder and the requester sides of the DOE mailbox. Also + support the [JTAG mailbox](jtagmbx.md) for accessing the mailbox from a JTAG/DMI link. + * `otp`: support for parsing and verifing OTP VMEM images, as well as generating and decoding QEMU + RAW image files. + * `util`: miscellaneous utililies such as ELF format tools and logging utilities + * `devproxy`: implementation of the communication channel with the QEMU devproxy device. + +Please check the [Python tools](tools,md) documentation for details and scripts that rely +on these APIs. diff --git a/docs/opentitan/tools.md b/docs/opentitan/tools.md index b2ecdfadc3b72..82c85bac38dfe 100644 --- a/docs/opentitan/tools.md +++ b/docs/opentitan/tools.md @@ -50,9 +50,7 @@ directory to help with these tasks. ## Python modules -* Available from `scripts/jtag` and `scripts/opentitan/ot` -* [JTAG mailbox](jtagmbx.md) provides an API to access the system side of the mailbox over JTAG/DMI -* [LC DMI](lc_ctrl_dmi.md) provides an API to control the Life Cycle controller over JTAG/DMI +See [Python modules](pymod.md) documentation. ## Configuration files From b04552524c28d7f731e73f075725baaaa4d62892 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 May 2024 12:06:00 +0200 Subject: [PATCH 52/65] [ot] hw/opentitan: ot_mbx: fix invalid IRQ count Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_mbx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_mbx.c b/hw/opentitan/ot_mbx.c index 2636d684fc140..864e6feb84a9b 100644 --- a/hw/opentitan/ot_mbx.c +++ b/hw/opentitan/ot_mbx.c @@ -51,6 +51,7 @@ /* ------------------------------------------------------------------------ */ #define PARAM_NUM_ALERTS 2u +#define PARAM_NUM_IRQS 3u /* clang-format off */ @@ -127,7 +128,6 @@ REG32(SYS_INTR_MSG_DATA, 0x1cu) #define HOST_INTR_MASK \ (INTR_MBX_READY_MASK | INTR_MBX_ABORT_MASK | INTR_MBX_ERROR_MASK) -#define HOST_INTR_COUNT (HOST_INTR_MASK - 1u) #define HOST_ALERT_TEST_MASK \ (R_HOST_ALERT_TEST_FATAL_FAULT_MASK | R_HOST_ALERT_TEST_RECOV_FAULT_MASK) #define HOST_CONTROL_MASK \ @@ -189,7 +189,7 @@ enum { typedef struct { MemoryRegion mmio; - IbexIRQ irqs[HOST_INTR_COUNT]; + IbexIRQ irqs[PARAM_NUM_IRQS]; IbexIRQ alerts[PARAM_NUM_ALERTS]; uint32_t regs[REGS_HOST_COUNT]; } OtMbxHost; @@ -217,7 +217,7 @@ static void ot_mbx_host_update_irqs(OtMbxState *s) uint32_t levels = hregs[R_HOST_INTR_STATE] & hregs[R_HOST_INTR_ENABLE]; - for (unsigned ix = 0; ix < ARRAY_SIZE(s->host.irqs); ix++) { + for (unsigned ix = 0; ix < PARAM_NUM_IRQS; ix++) { int level = (int)(bool)(levels & (1u << ix)); if (level != ibex_irq_get_level(&host->irqs[ix])) { trace_ot_mbx_host_update_irq(ibex_irq_get_level(&host->irqs[ix]), @@ -783,9 +783,9 @@ static void ot_mbx_init(Object *obj) OtMbxState *s = OT_MBX(obj); memory_region_init_io(&s->host.mmio, obj, &ot_mbx_host_regs_ops, s, - TYPE_OT_MBX, REGS_HOST_SIZE); + TYPE_OT_MBX "-host", REGS_HOST_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->host.mmio); - for (unsigned ix = 0; ix < ARRAY_SIZE(s->host.irqs); ix++) { + for (unsigned ix = 0; ix < PARAM_NUM_IRQS; ix++) { ibex_sysbus_init_irq(obj, &s->host.irqs[ix]); } for (unsigned ix = 0; ix < PARAM_NUM_ALERTS; ix++) { @@ -793,7 +793,7 @@ static void ot_mbx_init(Object *obj) } memory_region_init_io(&s->sys.mmio, obj, &ot_mbx_sys_regs_ops, s, - TYPE_OT_MBX, REGS_SYS_SIZE); + TYPE_OT_MBX "-sys", REGS_SYS_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->sys.mmio); } From 23e913b8643d26a115449787fd7afe26f0937ae3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 28 May 2024 17:40:44 +0200 Subject: [PATCH 53/65] [ot] hw/riscv: ot_darjeeling: fix board reset Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 5daee967c006b..a2374b413557e 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -1669,6 +1669,14 @@ type_init(ot_dj_soc_register_types); /* Board */ /* ------------------------------------------------------------------------ */ +static void ot_dj_board_reset(DeviceState *dev) +{ + OtDjBoardState *s = RISCV_OT_DJ_BOARD(dev); + + resettable_reset(OBJECT(s->devices[OT_DJ_BOARD_DEV_DEV_PROXY]), + RESET_TYPE_COLD); +} + static void ot_dj_board_realize(DeviceState *dev, Error **errp) { OtDjBoardState *board = RISCV_OT_DJ_BOARD(dev); @@ -1742,6 +1750,7 @@ static void ot_dj_board_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); (void)data; + dc->reset = &ot_dj_board_reset; dc->realize = &ot_dj_board_realize; } @@ -1839,6 +1848,13 @@ static void ot_dj_machine_init(MachineState *state) DeviceState *dev = qdev_new(TYPE_RISCV_OT_DJ_BOARD); object_property_add_child(OBJECT(state), "board", OBJECT(dev)); + + /* + * any object not part of the default system bus hiearchy is never reset + * otherwise + */ + qemu_register_reset(resettable_cold_reset_fn, dev); + qdev_realize(dev, NULL, &error_fatal); } From 0b4a00b48d096723b19b03ffbc546bcf0986a3e8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 24 May 2024 16:54:13 +0200 Subject: [PATCH 54/65] [ot] hw/opentitan: ot_dev_proxy: rework device management Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 197 ++++++++++++++++++++++-------------- 1 file changed, 120 insertions(+), 77 deletions(-) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index 6c3a7e8fcc05e..a34c6e6163ba6 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 Rivos, Inc. + * Copyright (c) 2023-2024 Rivos, Inc. * * Author(s): * Emmanuel Blot @@ -149,6 +149,13 @@ struct OtDevProxyState { guint watch_tag; /* tracker for comm device change */ }; +typedef void ot_dev_proxy_register_device_fn(GArray *array, Object *obj); + +typedef struct { + const char *typename; + ot_dev_proxy_register_device_fn *reg_dev; +} OtDevProxyDevice; + enum OtDevProxyErr { /* No error */ PE_NO_ERROR, @@ -183,6 +190,26 @@ enum OtDevProxyErr { #define PROXY_MAKE_UID(_uid_, _req_) \ (((_uid_) & ~(1u << 31u)) | (((uint32_t)(bool)(_req_)) << 31u)) +static void ot_dev_proxy_reg_mr(GArray *array, Object *obj); +static void ot_dev_proxy_reg_mbx(GArray *array, Object *obj); +static void ot_dev_proxy_reg_soc_proxy(GArray *array, Object *obj); +static void ot_dev_proxy_reg_sram_ctrl(GArray *array, Object *obj); + +static OtDevProxyDevice SUPPORTED_DEVICES[] = { + { + .typename = TYPE_OT_MBX, + .reg_dev = &ot_dev_proxy_reg_mbx, + }, + { + .typename = TYPE_OT_SOC_PROXY, + .reg_dev = &ot_dev_proxy_reg_soc_proxy, + }, + { + .typename = TYPE_OT_SRAM_CTRL, + .reg_dev = &ot_dev_proxy_reg_sram_ctrl, + }, +}; + static void ot_dev_proxy_send(OtDevProxyState *s, unsigned uid, int dir, uint16_t command, const void *payload, size_t length) @@ -284,12 +311,6 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) }; g_assert(sizeof(struct entry) == 7u * sizeof(uint32_t)); - static const char *SUPPORTED_DEVICE_TYPES[] = { - TYPE_OT_MBX, - TYPE_OT_SOC_PROXY, - TYPE_OT_SRAM_CTRL, - }; - struct entry *entries = g_new0(struct entry, s->dev_count); unsigned count = 0; unsigned mrcount = 0; @@ -301,9 +322,9 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) memset(entry, 0, sizeof(*entry)); memset(desc, 0, sizeof(desc)); const char *oid = NULL; - for (unsigned dix = 0; dix < ARRAY_SIZE(SUPPORTED_DEVICE_TYPES); - dix++) { - if (object_dynamic_cast(item->obj, SUPPORTED_DEVICE_TYPES[dix])) { + 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); (void)snprintf(desc, sizeof(desc), "%s%s", item->prefix, oid); break; @@ -1437,79 +1458,101 @@ static void ot_dev_proxy_reset(DeviceState *dev) static int ot_dev_proxy_discover_device(Object *child, void *opaque) { GArray *array = opaque; - if (object_dynamic_cast(child, TYPE_OT_MBX)) { - SysBusDevice *sysdev = SYS_BUS_DEVICE(child); - g_assert(sysdev->num_mmio == 2u); + + for (unsigned ix = 0; ix < ARRAY_SIZE(SUPPORTED_DEVICES); ix++) { + const OtDevProxyDevice *pd = &SUPPORTED_DEVICES[ix]; + if (object_dynamic_cast(child, pd->typename)) { + (*pd->reg_dev)(array, child); + return 0; + } + } + if (object_dynamic_cast(child, TYPE_MEMORY_REGION)) { + ot_dev_proxy_reg_mr(array, child); + return 0; + } + + return 0; +} + +static void ot_dev_proxy_reg_mr(GArray *array, Object *obj) +{ + MemoryRegion *mr = MEMORY_REGION(obj); + if (mr->ram && obj->parent) { + if (!object_dynamic_cast(obj->parent, TYPE_OT_SRAM_CTRL)) { + OtDevProxyItem *item = g_new0(OtDevProxyItem, 1); + object_ref(obj); + item->obj = obj; + item->caps.mr = mr; + g_assert(item->caps.mr); + item->caps.reg_count = int128_getlo(mr->size) / sizeof(uint32_t); + item->prefix = "M/"; + g_array_append_val(array, item); + } + } +} + +static void ot_dev_proxy_reg_mbx(GArray *array, Object *obj) +{ + SysBusDevice *sysdev = SYS_BUS_DEVICE(obj); + g_assert(sysdev->num_mmio == 2u); + OtDevProxyItem *item; + /* host side */ + item = g_new0(OtDevProxyItem, 1); + object_ref(obj); + item->obj = obj; + item->caps.mr = sysdev->mmio[0u].memory; /* 0: host */ + item->caps.reg_count = OT_MBX_HOST_REGS_COUNT; + item->caps.irq_mask = UINT32_MAX; /* all IRQs can be routed */ + item->prefix = "MBH/"; + g_array_append_val(array, item); + /* sys side */ + item = g_new0(OtDevProxyItem, 1); + object_ref(obj); + item->obj = obj; + item->caps.mr = sysdev->mmio[1u].memory; /* 1: sys */ + item->caps.reg_count = OT_MBX_SYS_REGS_COUNT; + item->caps.irq_mask = 0; /* no IRQ on sys side */ + item->prefix = "MBS/"; + g_array_append_val(array, item); +} + +static void ot_dev_proxy_reg_soc_proxy(GArray *array, Object *obj) +{ + OtDevProxyItem *item = g_new0(OtDevProxyItem, 1); + object_ref(obj); + item->obj = obj; + SysBusDevice *sysdev = SYS_BUS_DEVICE(obj); + g_assert(sysdev->num_mmio == 1u); + item->caps.mr = sysdev->mmio[0u].memory; + item->caps.reg_count = OT_SOC_PROXY_REGS_COUNT; /* per slot */ + item->caps.irq_mask = UINT32_MAX; /* all IRQs can be routed */ + item->prefix = "SOC/"; + g_array_append_val(array, item); +} + +static void ot_dev_proxy_reg_sram_ctrl(GArray *array, Object *obj) +{ + SysBusDevice *sysdev = SYS_BUS_DEVICE(obj); + if (sysdev->mmio[0].memory && /* ctrl */ + sysdev->mmio[1].memory /* mem */) { OtDevProxyItem *item; - /* host side */ item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - item->caps.mr = sysdev->mmio[0u].memory; /* 0: host */ - item->caps.reg_count = OT_MBX_HOST_REGS_COUNT; - item->caps.irq_mask = UINT32_MAX; /* all IRQs can be routed */ - item->prefix = "MBH/"; + object_ref(obj); + item->obj = obj; + item->caps.mr = sysdev->mmio[0].memory; + item->caps.reg_count = + int128_getlo(item->caps.mr->size) / sizeof(uint32_t); + item->prefix = "SRC/"; /* SRAM control */ g_array_append_val(array, item); - /* sys side */ item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - item->caps.mr = sysdev->mmio[1u].memory; /* 1: sys */ - item->caps.reg_count = OT_MBX_SYS_REGS_COUNT; - item->caps.irq_mask = 0; /* no IRQ on sys side */ - item->prefix = "MBS/"; - g_array_append_val(array, item); - } else if (object_dynamic_cast(child, TYPE_OT_SOC_PROXY)) { - OtDevProxyItem *item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - SysBusDevice *sysdev = SYS_BUS_DEVICE(child); - g_assert(sysdev->num_mmio == 1u); - item->caps.mr = sysdev->mmio[0u].memory; - item->caps.reg_count = OT_SOC_PROXY_REGS_COUNT; /* per slot */ - item->caps.irq_mask = UINT32_MAX; /* all IRQs can be routed */ - item->prefix = "SOC/"; + object_ref(obj); + item->obj = obj; + item->caps.mr = sysdev->mmio[1].memory; + item->caps.reg_count = + int128_getlo(item->caps.mr->size) / sizeof(uint32_t); + item->prefix = "SRM/"; /* SRAM memory */ g_array_append_val(array, item); - } else if (object_dynamic_cast(child, TYPE_OT_SRAM_CTRL)) { - SysBusDevice *sysdev = SYS_BUS_DEVICE(child); - if (sysdev->mmio[0].memory && /* ctrl */ - sysdev->mmio[1].memory /* mem */) { - OtDevProxyItem *item; - item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - item->caps.mr = sysdev->mmio[0].memory; - item->caps.reg_count = - int128_getlo(item->caps.mr->size) / sizeof(uint32_t); - item->prefix = "SRC/"; /* SRAM control */ - g_array_append_val(array, item); - item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - item->caps.mr = sysdev->mmio[1].memory; - item->caps.reg_count = - int128_getlo(item->caps.mr->size) / sizeof(uint32_t); - item->prefix = "SRM/"; /* SRAM memory */ - g_array_append_val(array, item); - } - } else if (object_dynamic_cast(child, TYPE_MEMORY_REGION)) { - MemoryRegion *mr = MEMORY_REGION(child); - if (mr->ram && child->parent) { - if (!object_dynamic_cast(child->parent, TYPE_OT_SRAM_CTRL)) { - OtDevProxyItem *item = g_new0(OtDevProxyItem, 1); - object_ref(child); - item->obj = child; - item->caps.mr = mr; - g_assert(item->caps.mr); - item->caps.reg_count = - int128_getlo(mr->size) / sizeof(uint32_t); - item->prefix = "M/"; - g_array_append_val(array, item); - } - } } - - return 0; } static int ot_dev_proxy_discover_memory_root(Object *child, void *opaque) From 1f91ec40e30153d9bcca3489dffa7741163018fd Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 May 2024 11:22:34 +0200 Subject: [PATCH 55/65] [ot] hw/opentitan: ot_dev_proxy: add traces for interrupt management Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 37 +++++++++++++++++++++++++++++++++++++ hw/opentitan/trace-events | 3 +++ 2 files changed, 40 insertions(+) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index a34c6e6163ba6..ed3c58ef5c8fe 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -947,6 +947,13 @@ static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) return; } + const char *dev_name = object_get_typename(item->obj); + Error *errp = NULL; + char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); + if (errp) { + error_free(errp); + } + /* reroute all marked IRQs */ irqs = s->rx_buffer[1u]; while (irqs) { @@ -961,6 +968,8 @@ static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) old_irq = qdev_intercept_gpio_out(dev, dest_irq, SYSBUS_DEVICE_GPIO_IRQ, ix); item->intctrl_irqs[ix] = old_irq; + trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", ix, + enable); } } else { /* release intercepted interrupt */ @@ -968,10 +977,14 @@ static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) qdev_intercept_gpio_out(dev, item->intctrl_irqs[ix], SYSBUS_DEVICE_GPIO_IRQ, ix); item->intctrl_irqs[ix] = NULL; + trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", ix, + enable); } } } + g_free(dev_id); + ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', enable ? 'i' : 'r'), NULL, 0); } @@ -1023,9 +1036,20 @@ static void ot_dev_proxy_signal_interrupt(OtDevProxyState *s) return; } + const char *dev_name = object_get_typename(item->obj); + Error *errp = NULL; + char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); + if (errp) { + error_free(errp); + } + + trace_ot_dev_proxy_signal_irq(dev_name, dev_id ?: "?", irq_num, irq_level); + qemu_irq irq = gl->in[irq_num]; qemu_set_irq(irq, irq_level); + g_free(dev_id); + ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', 's'), NULL, 0); } @@ -1246,8 +1270,21 @@ static void ot_dev_proxy_intercepted_irq(void *opaque, int irq, int level) OtDevProxyIrq *proxy_irq = &s->proxy_irq_map[irq]; g_assert(proxy_irq->dev_num < s->dev_count); + OtDevProxyItem *item = &s->items[proxy_irq->dev_num]; + + const char *dev_name = object_get_typename(item->obj); + Error *errp = NULL; + char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); + if (errp) { + error_free(errp); + } + + trace_ot_dev_proxy_route_irq(dev_name, dev_id, proxy_irq->irq_num, level); + ot_dev_proxy_signal(s, PROXY_COMMAND('^', 'W'), proxy_irq->dev_num, proxy_irq->irq_num, level); + + g_free(dev_id); } static void ot_dev_proxy_notify_mmio_access( diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 53d1756451745..ea13a4ef4805d 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -89,9 +89,12 @@ ot_csrng_try_schedule_genbits(unsigned slot, bool ready, bool queued, unsigned r ot_dev_proxy_dispatch_request(char a, char b) "%c%c" ot_dev_proxy_fe_error(int err) "error: %d" +ot_dev_proxy_intercept_irq(const char *dname, const char *did, unsigned irq, bool enable) "%s (%s) %u: enable %u" ot_dev_proxy_read_buffer(const char *prefix, unsigned devix, bool mbx, unsigned offset, unsigned count) "%s #%u mbx:%u 0x%02x %u" ot_dev_proxy_read_memory(const char *prefix, unsigned devix, unsigned offset, unsigned count) "%s #%u 0x%08x 0x%x" ot_dev_proxy_read_reg(const char *prefix, unsigned devix, unsigned offset) "%s #%u 0x%08x" +ot_dev_proxy_route_irq(const char *dname, const char *did, unsigned irq, int level) "%s (%s) %u: level %d" +ot_dev_proxy_signal_irq(const char *dname, const char *did, unsigned irq, int level) "%s (%s) %u: level %d" ot_dev_proxy_uid_error(const char *msg, unsigned expuid, unsigned realuid) "%s: expected %u, received %u" ot_dev_proxy_write_buffer(const char *prefix, unsigned devix, bool mbx, unsigned offset, unsigned count) "%s #%u mbx:%u 0x%02x %u" ot_dev_proxy_write_memory(const char *prefix, unsigned devix, unsigned offset, unsigned count) "%s #%u 0x%08x 0x%x" From 021e07f35ddc5483a372013c0cecdcb5a3181c9a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 May 2024 11:23:02 +0200 Subject: [PATCH 56/65] [ot] hw/opentitan: ot_dev_proxy: rework interrupt interception. Update protocol and implementation to generalize interrupt interception to all qemu_irq of a device, not only the system bus IRQs. Signed-off-by: Emmanuel Blot --- docs/opentitan/devproxy.md | 209 +++++++------ hw/opentitan/ot_dev_proxy.c | 520 +++++++++++++++++++------------ hw/opentitan/trace-events | 2 +- scripts/opentitan/ot/devproxy.py | 269 ++++++++++++---- 4 files changed, 629 insertions(+), 371 deletions(-) diff --git a/docs/opentitan/devproxy.md b/docs/opentitan/devproxy.md index e9164f8734a0f..903a209854feb 100644 --- a/docs/opentitan/devproxy.md +++ b/docs/opentitan/devproxy.md @@ -137,8 +137,12 @@ Not applicable +------------+---------------+----------------------------------+ | 0x404 | Local | Incomplete write | +------------+---------------+----------------------------------+ +| 0x405 | Local | Out of resources | ++------------+---------------+----------------------------------+ | 0x801 | Internal | Unsupported device | +------------+---------------+----------------------------------+ +| 0x802 | Internal | Duplicated unique identifier | ++------------+---------------+----------------------------------+ ``` * `Error Message` is an optional error message to help diagnose the actual error. The length of the @@ -182,7 +186,7 @@ Only initiated by the application. +---------------+---------------+---------------+---------------+ ``` -The current version for this documentation is v0.13. +The current version for this documentation is v0.14. Note that semantic versionning does not apply for v0 series. @@ -268,7 +272,7 @@ Reponse contains 0 up to N devices, each device is described with a 28-byte entr The count of device entries can be retrieved from the `LENGTH` field. -#### Enumerate Memory Spaces [enumerate-region] +#### Enumerate Memory Spaces [enumerate-mr-spaces] Enumerate should be called by the Application to retrieve the list of remote memory spaces that can be used from the Application over the communication link. @@ -295,11 +299,11 @@ Each memory space is a top-level memory region, _i.e._ a root memory region. +---------------+---------------+---------------+---------------+ |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'es' | 0..7*4N | +| 'es' | 0..11*4N | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| - | MSpc | +| - | MSpc | +---------------+---------------+---------------+---------------+ | Start Address | +---------------+---------------+---------------+---------------+ @@ -311,7 +315,7 @@ Each memory space is a top-level memory region, _i.e._ a root memory region. | | | | +---------------+---------------+---------------+---------------+ -| - | MSpc | +| - | MSpc | +---------------+---------------+---------------+---------------+ | Start Address | +---------------+---------------+---------------+---------------+ @@ -325,7 +329,7 @@ Each memory space is a top-level memory region, _i.e._ a root memory region. +---------------+---------------+---------------+---------------+ | .... | +---------------+---------------+---------------+---------------+ -| - | MSpc | +| - | MSpc | +---------------+---------------+---------------+---------------+ | Start Address | +---------------+---------------+---------------+---------------+ @@ -338,13 +342,13 @@ Each memory space is a top-level memory region, _i.e._ a root memory region. | | +---------------+---------------+---------------+---------------+ ``` -Reponse contains 0 up to N devices, each device is described with a 28-byte entry, where: +Reponse contains 0 up to N devices, each device is described with a 44-byte entry, where: * `MSpc` is a unique address space identifier than can be used as a selector in other proxy commands. * `Start Address` is the lowest valid address in the address space. * `Size` is the size (in bytes) of the address space -* `Identifier` is an arbitrary 16-character string that describes the memory space. +* `Identifier` is an arbitrary 32-character string that describes the memory space. The count of address spaces can be retrieved from the `LENGTH` field. @@ -631,7 +635,7 @@ The Application is in charge of formatting the payload to ensure that it contain Read the content of a memory device, where -* `Address` is the index of the first 32-bit word +* `Address` is the address in bytes of the first 32-bit word * `Role` is the initiator role to use to access the device * `Device` is the device to access (see [Enumerate](#enumerate-devices)) * `Count` is the number of 32-bit word to be read. @@ -678,9 +682,9 @@ Read the content of a memory device, where Write a buffer to a memory device -* `Address` is the index of the first 32-bit word to be written +* `Address` is the address in bytes of the first 32-bit word to be written * `Role` is the initiator role to use to access the device -* `Device` is the device to access (see [Enumerate](#enumerate-devices)) +* `Device` is the device to access (see [Enumerate Devices](#enumerate-devices)) * `Value` are the values to write in memory word. ##### Request @@ -751,7 +755,6 @@ When VM is started in stod mode `-S`, proxy can be used to configure the VM befo +---------------+---------------+---------------+---------------+ ``` - #### Quit Stop QEMU emulation and return an error code to the caller. @@ -784,26 +787,24 @@ Stop QEMU emulation and return an error code to the caller. This is the last command, as QEMU should exit upon receiving this request. -#### Intercept Interrupts [intercept-interrupt] - -Route one or more device output interrupt to the proxy (vs. the internal PLIC) +#### Enumerate Device Interrupt [enumerate-irq] -* `Device` is the device to access (see [Enumerate](#enumerate-devices)) -* `Interrupt mask` define which interrupt should be routed (1 bit per interrupt) +Enumerate can be called by the Application to retrieve the list of interrupt groups of a supported +device. The group in the response can be further used with the [Signal Interrupt API](#signal-interrupt), +[Intercept Interrupts](#intercept-interrupt) and [Release Interrupts](#release-interrupt). ##### Request ``` +---------------+---------------+---------------+---------------+ | 0 | 1 | 2 | 3 | ++---------------+---------------+---------------+---------------+ |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'II' | 8 | +| 'IE' | 4 | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| 0 | Device | - | -+---------------+---------------+---------------+---------------+ -| Interrupt mask | +| - | Device | - | +---------------+---------------+---------------+---------------+ ``` @@ -811,21 +812,57 @@ Route one or more device output interrupt to the proxy (vs. the internal PLIC) ``` +---------------+---------------+---------------+---------------+ | 0 | 1 | 2 | 3 | ++---------------+---------------+---------------+---------------+ |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'ii' | 0 | +| 'ie' | 0..36N | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ +| IRQ count | Group |O| - | ++---------------+---------------+---------------+---------------+ +| | +| | +| Identifier | +| | +| | ++---------------+---------------+---------------+---------------+ +| IRQ count | Group |O| - | ++---------------+---------------+---------------+---------------+ +| | +| | +| Identifier | +| | +| | ++---------------+---------------+---------------+---------------+ +| .... | ++---------------+---------------+---------------+---------------+ +| IRQ count | Group |O| - | ++---------------+---------------+---------------+---------------+ +| | +| | +| Identifier | +| | +| | ++---------------+---------------+---------------+---------------+ ``` +Reponse contains 0 up to N interrupt groups, each group is described with a 36-byte entry, where: -#### Release Interrupts +* `IRQ count` is the count of the input interrupts for this group, +* `O` is set if IRQ in this group are output interrupts or or not set if they are input interrupts, +* `Group` is the interrupt group identifier identifier. +* `Identifier` defines the interrupt group name -Revert any previous interception, reconnecting selected IRQ to their original -destination device. +The count of address spaces can be retrieved from the `LENGTH` field. -* `Device` is the device to access (see [Enumerate](#enumerate-devices)) -* `Interrupt mask` define which interrupt should be released (1 bit per interrupt) +#### Intercept Interrupts [intercept-interrupt] + +Route one or more device output interrupts to the proxy (vs. the internal PLIC) + +* `Device` is the device to access (see [Enumerate Device](#enumerate-devices)) +* `Group` is the IRQ group in the device (see [Enumerate Interrupt](#enumerate-irq)) +* `Interrupt mask` define which interrupts should be released (1 bit per interrupt). The count of + 32-bit interrupt mask word can be retrieved from the header length. ##### Request ``` @@ -833,13 +870,15 @@ destination device. | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'IR' | 8 | +| 'II' | 8.. | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| 0 | Device | - | +| Group | - | Device | - | +---------------+---------------+---------------+---------------+ +| | | Interrupt mask | +| | +---------------+---------------+---------------+---------------+ ``` @@ -849,24 +888,21 @@ destination device. | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'ir' | 0 | +| 'ii' | 0 | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ ``` -#### Signal Interrupt [signal-interrupt] +#### Release Interrupts [release-interrupt] -Set or Reset an input interrupt line. +Revert any previous interception, reconnecting selected IRQ to their original +destination device. -* `Device` is the device to access (see [Enumerate](#enumerate-devices)) -* `GID` the identifier of the IRQ group. - * The group identifier can be retrieved using the [Enumerate Device Interrupt](#enumerate-irq) - API. -* `Interrupt line` the number of the interrupt line to signal within the group. The interrupt line - should range between 0 and the input IRQ count for this group. -* `Level` the new interrupt line level. Usually `1` to assert/set (1) or `0` to deassert/release, - even if any 32-bit value is accepted. +* `Device` is the device to access (see [Enumerate Device](#enumerate-devices)) +* `Group` is the IRQ group in the device (see [Enumerate Interrupt](#enumerate-irq)) +* `Interrupt mask` define which interrupts should be released (1 bit per interrupt). The count of + 32-bit interrupt mask word can be retrieved from the header length. ##### Request ``` @@ -874,15 +910,15 @@ Set or Reset an input interrupt line. | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'IS' | 12 | +| 'IR' | 8.. | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| GID | Device | - | -+---------------+---------------+---------------+---------------+ -| Interrupt line | - | +| Group | - | Device | - | +---------------+---------------+---------------+---------------+ -| Level | +| | +| Interrupt mask | +| | +---------------+---------------+---------------+---------------+ ``` @@ -892,31 +928,38 @@ Set or Reset an input interrupt line. | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'is' | 0 | +| 'ir' | 0 | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ ``` -#### Enumerate Device Interrupt [enumerate-irq] +#### Signal Interrupt [signal-interrupt] + +Set or Reset an input interrupt line. -Enumerate can be called by the Application to retrieve the list of interrupt -group of a supported device. The group position in the response can be further -use with the [Signal Interrupt API](#signal-interrupt) to set the level of -each individual IRQ line. +* `Device` is the device to access (see [Enumerate Device](#enumerate-devices)), +* `Group` the identifier of the IRQ group (see [Enumerate Interrupt](#enumerate-irq)), +* `Interrupt line` the index of the interrupt line to signal within the group. The interrupt line + should range between 0 and the input IRQ count for this group, +* `Level` the new interrupt line level. Usually `1` to assert/set (1) or `0` to deassert/release, + even if any 32-bit value is accepted. ##### Request ``` +---------------+---------------+---------------+---------------+ | 0 | 1 | 2 | 3 | -+---------------+---------------+---------------+---------------+ |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'IE' | 4 | +| 'IS' | 12 | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| - | Device | - | +| Group | Device | - | ++---------------+---------------+---------------+---------------+ +| Interrupt line | - | ++---------------+---------------+---------------+---------------+ +| Level | +---------------+---------------+---------------+---------------+ ``` @@ -924,49 +967,15 @@ each individual IRQ line. ``` +---------------+---------------+---------------+---------------+ | 0 | 1 | 2 | 3 | -+---------------+---------------+---------------+---------------+ |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| 'ie' | 0..20N | +| 'is' | 0 | +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -| IRQ input count | IRQ output count | -+---------------+---------------+---------------+---------------+ -| | -| | -| Identifier | -| | -| | -+---------------+---------------+---------------+---------------+ -| IRQ input count | IRQ output count | -+---------------+---------------+---------------+---------------+ -| | -| | -| Identifier | -| | -| | -+---------------+---------------+---------------+---------------+ -| .... | -+---------------+---------------+---------------+---------------+ -| IRQ input count | IRQ output count | -+---------------+---------------+---------------+---------------+ -| | -| | -| Identifier | -| | -| | -+---------------+---------------+---------------+---------------+ ``` -Reponse contains 0 up to N interrupt groups, each group is described with a 20-byte entry, where: -* `IRQ input count` is the count of the input interrupts for this group, -* `IRQ output count` is the count of the output interrupts for this group, -* `Identifier` is an arbitrary 16-character string that describes this group. - -The count of address spaces can be retrieved from the `LENGTH` field. - -#### Intercept arbitrary MMIO region +#### Intercept arbitrary MMIO region [intercept-region] It is possible to get intercept access to a memory region. Any intercepted region cannot be any longer accessed by the remote vCPU. @@ -984,7 +993,7 @@ intercepted location should be kept small. +---------------+---------------+---------------+---------------+ | UID |0| +---------------+---------------+---------------+---------------+ -|R|W| Priority | - | Stop | 0xfff | MSpc | +|R|W| Priority | - | Stop | - | MSpc | +---------------+---------------+---------------+---------------+ | Address | +---------------+---------------+---------------+---------------+ @@ -997,7 +1006,7 @@ intercepted location should be kept small. * `Priority` is the priority order (increasing order). 0 is reserved. If not sure, use 1. * `Stop` auto-stop count. If non-zero, only the specified count of notifications are reported, after which the MMIO location intercepter is automatically discarded. -* `MSpc` is the memory space of the region to select (see [Enumerate](#enumerate-regions)) +* `MSpc` is the memory space of the region to select (see [Enumerate Memory Spaces](#enumerate-mr-spaces)) * `Address` is the byte address of the first byte to intercept in the selected memory space * `Size` is the width in bytes of the region to intercept @@ -1064,18 +1073,21 @@ interrupt(s) to receive. | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| '^W' | 8 | +| '^W' | 12 | +---------------+---------------+---------------+---------------+ | UID |1| +---------------+---------------+---------------+---------------+ -| Channel | Device | - | +| - | Device | - | ++---------------+---------------+---------------+---------------+ +| Channel | Group |O| - | +---------------+---------------+---------------+---------------+ | Value | +---------------+---------------+---------------+---------------+ ``` * `Device` that triggered the interrupt -* `Channel` which interrupt channel on the device has been triggered +* `Group` which interrupt group has been triggered +* `Channel` which interrupt channel for the group has been triggered * `Value` the new interrupt value. For binary IRQs, `1` when IRQ is raised, `0` when it is lowered. #### MSI Interrupt @@ -1098,12 +1110,15 @@ To be defined. ``` #### Region Access + +See [Intercept arbitrary MMIO region](#intercept-region) to register a watcher + ``` +---------------+---------------+---------------+---------------+ | 0 | 1 | 2 | 3 | |0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F| +---------------+---------------+---------------+---------------+ -| '^L' | 12 | +| '^R' | 12 | +---------------+---------------+---------------+---------------+ | UID |1| +---------------+---------------+---------------+---------------+ @@ -1115,7 +1130,7 @@ To be defined. +---------------+---------------+---------------+---------------+ ``` -* `Device` that triggered the notification +* `Region` the region watcher identifier, as returned in the interception registration response * `R` read access * `W` write access * `Width` the width in byte of the MMIO access (1/2/4) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index ed3c58ef5c8fe..9bd9e9b91fb33 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -93,16 +93,14 @@ typedef struct { Object *obj; /* proxied object */ OtDevProxyCaps caps; /* object capabilities */ const char *prefix; /* prefix name for idenfifying the device */ - /* the following fields are only meaningful for SUS_BUS_DEVICE items */ - qemu_irq *intctrl_irqs; /* original IRQ destination (to QEMU devices) */ - qemu_irq *routed_irqs; /* rerouted IRQ (to proxy) */ - unsigned irq_count; /* count of IRQ for the device */ - unsigned irq_route_base; /* offset of the first IRQ in the proxy */ + GHashTable *iirq_ht; /* intercepted IRQs, may be NULL */ } OtDevProxyItem; typedef struct { + qemu_irq irq_orig; /* original IRQ destination (to QEMU device) */ unsigned dev_num; /* device number (in device array) */ - unsigned irq_num; /* irq number (in proxied device) */ + uint16_t irq_num; /* IRQ number (in proxied device) */ + uint8_t grp_num; /* IRQ group (in proxied device) */ } OtDevProxyIrq; typedef struct { @@ -131,12 +129,11 @@ struct OtDevProxyState { DeviceState parent_obj; OtDevProxyItem *items; /* proxied devices */ - OtDevProxyIrq *proxy_irq_map; /* IRQ mapping */ - unsigned proxy_irq_count; /* count of IRQ slots in proxy_irq_map */ - unsigned dev_count; /* count of IRQ in itms */ + OtDevProxyIrq *proxy_irq_map; /* IRQ interception mapping */ OtDevProxySystem *subsys; /*subsystem array */ - unsigned subsys_count; /* count of memory roots */ QSIMPLEQ_HEAD(, OtDevProxyWatcherState) watchers; + unsigned dev_count; /* count of IRQ in items */ + unsigned subsys_count; /* count of memory roots */ unsigned last_wid; Fifo8 rx_fifo; /* input FIFO */ @@ -176,12 +173,16 @@ enum OtDevProxyErr { PE_CANNOT_WRITE_DEVICE, PE_TRUNCATED_RESPONSE, PE_INCOMPLETE_WRITE, + PE_OOM, /* out of resources */ /* internal error */ PE_UNSUPPORTED_DEVICE = 0x801, }; #define PROXY_VER_MAJ 0 -#define PROXY_VER_MIN 13u +#define PROXY_VER_MIN 14u + +#define PROXY_IRQ_INTERCEPT_COUNT 32u +#define PROXY_IRQ_INTERCEPT_NAME "irq-intercept" #define PROXY_DISABLED_ROLE 0xfu @@ -256,12 +257,12 @@ static void ot_dev_proxy_reply_payload(OtDevProxyState *s, uint16_t command, } static void ot_dev_proxy_signal(OtDevProxyState *s, uint16_t command, - unsigned device, unsigned channel, int value) + const OtDevProxyIrq *proxy_irq, int value) { - uint32_t buffer[2u] = { - (uint32_t)((channel & 0xffffu) | ((device & 0x3fu) << 24u)), - (uint32_t)value - }; + uint32_t buffer[3u] = { ((uint32_t)(proxy_irq->dev_num & 0xfffu) << 16u), + ((uint32_t)(proxy_irq->irq_num)) | + ((uint32_t)(proxy_irq->grp_num) << 16u), + (uint32_t)value }; ot_dev_proxy_send(s, s->initiator_uid, 1, command, buffer, sizeof(buffer)); @@ -396,16 +397,16 @@ static void ot_dev_proxy_enumerate_memory_spaces(OtDevProxyState *s) uint32_t header; uint32_t address; uint32_t size; - char desc[16u]; + char desc[32u]; }; - g_assert(sizeof(struct entry) == 7u * sizeof(uint32_t)); + g_assert(sizeof(struct entry) == 11u * sizeof(uint32_t)); struct entry *entries = g_new0(struct entry, s->subsys_count); unsigned count = 0; for (unsigned ix = 0; ix < s->subsys_count; ix++) { const OtDevProxySystem *subsys = &s->subsys[ix]; struct entry *entry = &entries[count]; - entry->header = ix << 28u; + entry->header = ix << 24u; entry->address = subsys->mr->addr; uint64_t size = int128_getlo(subsys->mr->size); entry->size = (uint32_t)MIN(size, UINT32_MAX); @@ -418,7 +419,8 @@ static void ot_dev_proxy_enumerate_memory_spaces(OtDevProxyState *s) memcpy(entry->desc, name, namelen); } count++; - if (ix >= 15u) { + if (ix >= 0xffu) { + /* only 256 root regions are supported for now */ break; } } @@ -428,6 +430,76 @@ static void ot_dev_proxy_enumerate_memory_spaces(OtDevProxyState *s) g_free(entries); } +static void ot_dev_proxy_enumerate_interrupts(OtDevProxyState *s) +{ + if (s->rx_hdr.length != 1u * sizeof(uint32_t)) { + ot_dev_proxy_reply_error(s, PE_INVALID_COMMAND_LENGTH, NULL); + return; + } + + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; + + if (devix >= s->dev_count) { + ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, NULL); + return; + } + + OtDevProxyItem *item = &s->items[devix]; + + if (!object_dynamic_cast(item->obj, TYPE_DEVICE)) { + ot_dev_proxy_reply_error(s, PE_UNSUPPORTED_DEVICE, NULL); + return; + } + + DeviceState *dev = DEVICE(item->obj); + + unsigned group_count = 0; + NamedGPIOList *ngl; + QLIST_FOREACH(ngl, &dev->gpios, node) { + group_count++; + } + + struct irq_id { + uint16_t count; + uint8_t group; + uint8_t dir; + char name[32u]; + }; + static_assert(sizeof(struct irq_id) == 9u * sizeof(uint32_t), + "invalid struct irq_id, need packing"); + + struct irq_id *entries; + + if (group_count) { + entries = g_new0(struct irq_id, group_count); + struct irq_id *irq_id = entries; + unsigned group = 0; + QLIST_FOREACH(ngl, &dev->gpios, node) { + if (group > UINT8_MAX) { + /* cannot handle more groups (unlikely) */ + break; + } + if (ngl->num_out) { + irq_id->count = ngl->num_out; + irq_id->dir = (1u << 7u); + } else { + irq_id->count = ngl->num_in; + irq_id->dir = (0u << 7u); + } + /* input sysbus IRQs are typically unnamed */ + strncpy(irq_id->name, ngl->name ?: "", sizeof(irq_id->name) - 1u); + irq_id->group = group++; + irq_id++; + } + } else { + entries = NULL; + } + + ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', 'e'), entries, + group_count * sizeof(struct irq_id)); + g_free(entries); +} + static void ot_dev_proxy_read_reg(OtDevProxyState *s) { if (s->rx_hdr.length != sizeof(uint32_t)) { @@ -437,7 +509,7 @@ static void ot_dev_proxy_read_reg(OtDevProxyState *s) hwaddr reg = (hwaddr)(s->rx_buffer[0] & 0xffffu); unsigned role = s->rx_buffer[0] >> 28u; - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; if (devix >= s->dev_count) { ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, NULL); @@ -494,7 +566,7 @@ static void ot_dev_proxy_write_reg(OtDevProxyState *s) hwaddr reg = (hwaddr)(s->rx_buffer[0] & 0xffffu); unsigned role = s->rx_buffer[0] >> 28u; - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; uint32_t value = s->rx_buffer[1u]; uint32_t mask = s->rx_buffer[2u]; @@ -573,7 +645,7 @@ static void ot_dev_proxy_read_buffer(OtDevProxyState *s, bool mbx_mode) hwaddr reg = (hwaddr)(s->rx_buffer[0] & 0xffffu); unsigned role = s->rx_buffer[0] >> 28u; - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; unsigned count = s->rx_buffer[1u]; if (devix >= s->dev_count) { @@ -686,7 +758,7 @@ static void ot_dev_proxy_write_buffer(OtDevProxyState *s, bool mbx_mode) hwaddr reg = (hwaddr)(s->rx_buffer[0] & 0xffffu); unsigned role = s->rx_buffer[0] >> 28u; - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; unsigned count = s->rx_hdr.length / sizeof(uint32_t) - 1u; if (devix >= s->dev_count) { @@ -806,7 +878,7 @@ static void ot_dev_proxy_read_memory(OtDevProxyState *s) return; } - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; unsigned offset = s->rx_buffer[1u]; unsigned count = s->rx_buffer[2u]; @@ -818,10 +890,11 @@ static void ot_dev_proxy_read_memory(OtDevProxyState *s) OtDevProxyItem *item = &s->items[devix]; OtDevProxyCaps *caps = &item->caps; Object *obj = item->obj; - if (offset > item->caps.reg_count) { + unsigned woffset = offset / sizeof(uint32_t); + if (woffset > item->caps.reg_count) { count = 0; } else { - unsigned maxcount = item->caps.reg_count - offset; + unsigned maxcount = item->caps.reg_count - woffset; if (count > maxcount) { count = maxcount; } @@ -860,7 +933,7 @@ static void ot_dev_proxy_write_memory(OtDevProxyState *s) return; } - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; unsigned offset = s->rx_buffer[1]; unsigned count = s->rx_hdr.length / sizeof(uint32_t) - 2u; const uint32_t *buffer = &s->rx_buffer[2]; @@ -873,10 +946,11 @@ static void ot_dev_proxy_write_memory(OtDevProxyState *s) OtDevProxyItem *item = &s->items[devix]; OtDevProxyCaps *caps = &item->caps; Object *obj = item->obj; - if (offset > item->caps.reg_count) { + unsigned woffset = offset / sizeof(uint32_t); + if (woffset > item->caps.reg_count) { count = 0; } else { - unsigned maxcount = item->caps.reg_count - offset; + unsigned maxcount = item->caps.reg_count - woffset; if (count > maxcount) { count = maxcount; } @@ -907,14 +981,102 @@ static void ot_dev_proxy_write_memory(OtDevProxyState *s) ot_dev_proxy_reply_payload(s, PROXY_COMMAND('w', 'm'), &obuf, sizeof(obuf)); } +static void +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 *irq_name = g_strdup_printf("%s[%u]", group, irq_n); + + OtDevProxyIrq *proxy_irq = + item->iirq_ht ? g_hash_table_lookup(item->iirq_ht, irq_name) : NULL; + /* do not reroute IRQ if it is already routed */ + if (proxy_irq) { + g_free(irq_name); + g_free(dev_id); + return; + } + + unsigned six; + for (six = 0; six < PROXY_IRQ_INTERCEPT_COUNT; six++) { + if (!s->proxy_irq_map[six].irq_orig) { + proxy_irq = &s->proxy_irq_map[six]; + break; + } + } + /* caller should have verified that there are enough free slots */ + g_assert(proxy_irq); + + DeviceState *dev = DEVICE(item->obj); + + qemu_irq icpt_irq; + icpt_irq = + qdev_get_gpio_in_named(DEVICE(s), PROXY_IRQ_INTERCEPT_NAME, (int)six); + proxy_irq->irq_orig = + qdev_intercept_gpio_out(dev, icpt_irq, group, (int)irq_n); + proxy_irq->dev_num = (unsigned)(uintptr_t)(item - s->items); + proxy_irq->grp_num = grp_n; + proxy_irq->irq_num = irq_n; + trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", irq_name, true); + if (!item->iirq_ht) { + /* + * delete key (g_char strings), but never delete value that are + * persisent, reassigned items + */ + item->iirq_ht = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + char *ht_key = g_strdup(irq_name); + g_hash_table_insert(item->iirq_ht, ht_key, proxy_irq); + + g_free(irq_name); + g_free(dev_id); +} + +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 *irq_name = g_strdup_printf("%s[%u]", group, irq_n); + + if (!item->iirq_ht) { + warn_report("Cannot restore interrupt, none intercepted: %s %s %s", + dev_name, dev_id ?: "?", irq_name); + g_free(irq_name); + g_free(dev_id); + return; + } + + OtDevProxyIrq *proxy_irq = g_hash_table_lookup(item->iirq_ht, irq_name); + if (proxy_irq) { + DeviceState *dev = DEVICE(item->obj); + + /* irq_orig == NULL is a valid use case */ + qdev_intercept_gpio_out(dev, proxy_irq->irq_orig, group, (int)irq_n); + /* hash table takes care of deleting the key */ + g_hash_table_remove(item->iirq_ht, irq_name); + memset(proxy_irq, 0, sizeof(*proxy_irq)); /* mark as free_slot */ + trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", irq_name, + false); + } else { + warn_report("Cannot restore interrupt, not intercepted: %s %s %s", + dev_name, dev_id ?: "?", irq_name); + } + + g_free(irq_name); + g_free(dev_id); +} + static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) { - if (s->rx_hdr.length != 2u * sizeof(uint32_t)) { + if (s->rx_hdr.length < 2u * sizeof(uint32_t)) { ot_dev_proxy_reply_error(s, PE_INVALID_COMMAND_LENGTH, NULL); return; } - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; if (devix >= s->dev_count) { ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, NULL); @@ -927,64 +1089,89 @@ static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) return; } + unsigned group = s->rx_buffer[0] & 0xffu; + + /* check that the group identifier is actually valid for the device */ DeviceState *dev = DEVICE(item->obj); - SysBusDevice *sysdev = SYS_BUS_DEVICE(dev); + NamedGPIOList *ngl = NULL; + NamedGPIOList *tngl; + unsigned grp = 0; + QLIST_FOREACH(tngl, &dev->gpios, node) { + if (!tngl->name) { + /* anonymous IRQs are ignored, see enumerate_interrupts */ + continue; + } + if (grp++ < group) { + continue; + } + ngl = tngl; + break; + } - /* validate all marked IRQs first */ - uint32_t irqs = s->rx_buffer[1u]; + if (!ngl) { + ot_dev_proxy_reply_error(s, PE_INVALID_IRQ, NULL); + return; + } + + /* check that all selected interrupts exits for the selected group */ + unsigned mask_count; + mask_count = (s->rx_hdr.length - sizeof(uint32_t)) / sizeof(uint32_t); + unsigned max_irq = 0; unsigned irq_count = 0; - while (irq_count < 32u) { - if (!sysbus_has_irq(sysdev, (int)irq_count)) { - break; + uint32_t *irqbms = &s->rx_buffer[1u]; + for (unsigned ix = 0; ix < mask_count; ix++) { + uint32_t bm = irqbms[ix]; + if (bm) { + unsigned hi = ctz32(bm); + max_irq = ix * 32u + hi; + while (bm) { + irq_count += 1; + bm &= ~(1u << ctz32(bm)); + } } - irq_count += 1; } - if (irq_count) { - irqs &= ~((1u << irq_count) - 1u); + + /* + * count how many IRQ can be intercepted and tracked. Already intercepted + * IRQs may be counted twice, remote peer should be more careful. + */ + unsigned free_slot = 0; + for (unsigned ix = 0; ix < PROXY_IRQ_INTERCEPT_COUNT; ix++) { + if (!s->proxy_irq_map[ix].irq_orig) { + free_slot += 1; + } } - if (irqs) { - ot_dev_proxy_reply_error(s, PE_INVALID_IRQ, NULL); + if (irq_count > free_slot) { + warn_report("IRQ interception slots exhausted %u for %u free", + irq_count, free_slot); + ot_dev_proxy_reply_error(s, PE_OOM, NULL); return; } - const char *dev_name = object_get_typename(item->obj); - Error *errp = NULL; - char *dev_id = object_property_get_str(item->obj, "ot_id", &errp); - if (errp) { - error_free(errp); + char *irq_name; + irq_name = g_strdup_printf("%s[%u]", ngl->name, max_irq); + ObjectProperty *prop = object_property_find(item->obj, irq_name); + g_free(irq_name); + irq_name = NULL; + + if (!prop) { + ot_dev_proxy_reply_error(s, PE_INVALID_IRQ, NULL); + return; } /* reroute all marked IRQs */ - irqs = s->rx_buffer[1u]; - while (irqs) { - int ix = ctz32(irqs); - irqs &= ~(1u << ix); - if (enable) { - /* do not reroute IRQ if it is already routed */ - if (!item->intctrl_irqs[ix]) { - qemu_irq old_irq; - unsigned irq_num = item->irq_route_base + ix; - qemu_irq dest_irq = qdev_get_gpio_in(DEVICE(s), (int)irq_num); - old_irq = qdev_intercept_gpio_out(dev, dest_irq, - SYSBUS_DEVICE_GPIO_IRQ, ix); - item->intctrl_irqs[ix] = old_irq; - trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", ix, - enable); - } - } else { - /* release intercepted interrupt */ - if (item->intctrl_irqs[ix]) { - qdev_intercept_gpio_out(dev, item->intctrl_irqs[ix], - SYSBUS_DEVICE_GPIO_IRQ, ix); - item->intctrl_irqs[ix] = NULL; - trace_ot_dev_proxy_intercept_irq(dev_name, dev_id ?: "?", ix, - enable); + for (unsigned ix = 0; ix < mask_count; ix++) { + while (irqbms[ix]) { + unsigned irq_n = ctz32(irqbms[ix]); + irqbms[ix] &= ~(1u << irq_n); + if (enable) { + ot_dev_proxy_route_interrupt(s, item, ngl->name, group, irq_n); + } else { + ot_dev_proxy_restore_interrupt(item, ngl->name, irq_n); } } } - g_free(dev_id); - ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', enable ? 'i' : 'r'), NULL, 0); } @@ -996,7 +1183,7 @@ static void ot_dev_proxy_signal_interrupt(OtDevProxyState *s) return; } - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned devix = (s->rx_buffer[0] >> 16u) & 0xfffu; unsigned gid = s->rx_buffer[0u] & 0xffffu; if (devix >= s->dev_count) { @@ -1053,65 +1240,6 @@ static void ot_dev_proxy_signal_interrupt(OtDevProxyState *s) ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', 's'), NULL, 0); } -static void ot_dev_proxy_enumerate_interrupts(OtDevProxyState *s) -{ - if (s->rx_hdr.length != 1u * sizeof(uint32_t)) { - ot_dev_proxy_reply_error(s, PE_INVALID_COMMAND_LENGTH, NULL); - return; - } - - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; - - if (devix >= s->dev_count) { - ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, NULL); - return; - } - - OtDevProxyItem *item = &s->items[devix]; - - if (!object_dynamic_cast(item->obj, TYPE_DEVICE)) { - ot_dev_proxy_reply_error(s, PE_UNSUPPORTED_DEVICE, NULL); - return; - } - - DeviceState *dev = DEVICE(item->obj); - - unsigned group_count = 0; - NamedGPIOList *ngl; - QLIST_FOREACH(ngl, &dev->gpios, node) { - group_count++; - } - - struct irq_id { - uint16_t in_count; - uint16_t out_count; - char name[16u]; - }; - static_assert(sizeof(struct irq_id) == 5 * sizeof(uint32_t), - "invalid struct irq_id, need packing"); - - struct irq_id *entries; - - if (group_count) { - entries = g_new0(struct irq_id, group_count); - struct irq_id *irq_id = entries; - QLIST_FOREACH(ngl, &dev->gpios, node) { - irq_id->in_count = ngl->num_in; - irq_id->out_count = ngl->num_out; - if (ngl->name) { - strncpy(irq_id->name, ngl->name, sizeof(irq_id->name)); - } - irq_id++; - } - } else { - entries = NULL; - } - - ot_dev_proxy_reply_payload(s, PROXY_COMMAND('i', 'e'), entries, - group_count * sizeof(struct irq_id)); - g_free(entries); -} - static void ot_dev_proxy_intercept_mmio(OtDevProxyState *s) { if (s->rx_hdr.length != 3u * sizeof(uint32_t)) { @@ -1119,14 +1247,7 @@ static void ot_dev_proxy_intercept_mmio(OtDevProxyState *s) return; } - unsigned devix = (s->rx_buffer[0] >> 16u) & 0x3ffu; - - if (devix != 0x3ffu) { - ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, NULL); - return; - } - - unsigned mspc = s->rx_buffer[0] >> 28u; + unsigned mspc = s->rx_buffer[0] >> 24u; if (mspc >= s->subsys_count) { ot_dev_proxy_reply_error(s, PE_INVALID_DEVICE_ID, "Invalid MSpc"); return; @@ -1206,7 +1327,7 @@ static void ot_dev_proxy_release_mmio(OtDevProxyState *s) return; } - unsigned wid = (s->rx_buffer[0] >> 16u) & 0x3ffu; + unsigned wid = (s->rx_buffer[0] >> 16u) & 0xfffu; OtDevProxyWatcherState *watcher = NULL; OtDevProxyWatcherState *node; @@ -1222,7 +1343,6 @@ static void ot_dev_proxy_release_mmio(OtDevProxyState *s) } qdev_unrealize(DEVICE(watcher)); - // object_unref(OBJECT(watcher)); ot_dev_proxy_reply_payload(s, PROXY_COMMAND('m', 'r'), NULL, 0); } @@ -1265,9 +1385,13 @@ static void ot_dev_proxy_quit(OtDevProxyState *s) static void ot_dev_proxy_intercepted_irq(void *opaque, int irq, int level) { OtDevProxyState *s = opaque; - g_assert(irq < s->proxy_irq_count); + g_assert(irq < PROXY_IRQ_INTERCEPT_COUNT); OtDevProxyIrq *proxy_irq = &s->proxy_irq_map[irq]; + if (!proxy_irq->irq_orig) { + warn_report("%d non-assigned intercepted IRQ signaled", irq); + return; + } g_assert(proxy_irq->dev_num < s->dev_count); OtDevProxyItem *item = &s->items[proxy_irq->dev_num]; @@ -1281,8 +1405,7 @@ static void ot_dev_proxy_intercepted_irq(void *opaque, int irq, int level) trace_ot_dev_proxy_route_irq(dev_name, dev_id, proxy_irq->irq_num, level); - ot_dev_proxy_signal(s, PROXY_COMMAND('^', 'W'), proxy_irq->dev_num, - proxy_irq->irq_num, level); + ot_dev_proxy_signal(s, PROXY_COMMAND('^', 'W'), proxy_irq, level); g_free(dev_id); } @@ -1477,21 +1600,6 @@ static int ot_dev_proxy_be_change(void *opaque) return 0; } -static Property ot_dev_proxy_properties[] = { - DEFINE_PROP_CHR("chardev", OtDevProxyState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ot_dev_proxy_reset(DeviceState *dev) -{ - OtDevProxyState *s = OT_DEV_PROXY(dev); - - fifo8_reset(&s->rx_fifo); - memset(&s->rx_hdr, 0, sizeof(s->rx_hdr)); - s->requester_uid = 0; - s->initiator_uid = 0; -} - static int ot_dev_proxy_discover_device(Object *child, void *opaque) { GArray *array = opaque; @@ -1598,7 +1706,12 @@ static int ot_dev_proxy_discover_memory_root(Object *child, void *opaque) if (object_dynamic_cast(child, TYPE_MEMORY_REGION)) { MemoryRegion *mr = MEMORY_REGION(child); - if (mr->container) { + /* + * This is a hack. A proper implementation would require to search + * the address spaces for memory root regions, unfortunately QEMU APIs + * do not expose address_spaces, which are hidden in memory.c + */ + if (mr->container || mr->ram || mr->mapped_via_alias) { /* not a root memory region */ return 0; } @@ -1679,15 +1792,8 @@ static gint ot_dev_proxy_device_compare(gconstpointer a, gconstpointer b) return (gint)(ma->addr > mb->addr); } -static void ot_dev_proxy_realize(DeviceState *dev, Error **errp) +static void ot_dev_proxy_discover(OtDevProxyState *s) { - OtDevProxyState *s = OT_DEV_PROXY(dev); - (void)errp; - - qemu_chr_fe_set_handlers(&s->chr, &ot_dev_proxy_can_receive, - &ot_dev_proxy_receive, NULL, - &ot_dev_proxy_be_change, s, NULL, true); - Object *ms = qdev_get_machine(); /* search for 'proxyfi-able' devices */ GArray *array; @@ -1697,51 +1803,23 @@ static void ot_dev_proxy_realize(DeviceState *dev, Error **errp) g_array_sort(array, &ot_dev_proxy_device_compare); - /* only system Mailbox side is exposed */ s->dev_count = array->len; s->items = g_new0(OtDevProxyItem, s->dev_count); - unsigned total_irq_count = 0; + for (unsigned ix = 0; ix < array->len; ix++) { OtDevProxyItem *item; item = (OtDevProxyItem *)(g_array_index(array, OtDevProxyItem *, ix)); - if (object_dynamic_cast(item->obj, TYPE_SYS_BUS_DEVICE)) { - SysBusDevice *sysdev = SYS_BUS_DEVICE(item->obj); - unsigned irq_count = 0; - while (irq_count < 32u) { - if (item->caps.irq_mask & (1u << irq_count)) { - if (!sysbus_has_irq(sysdev, (int)irq_count)) { - break; - } - } - irq_count += 1; - } - item->intctrl_irqs = g_new0(qemu_irq, irq_count); - item->routed_irqs = g_new0(qemu_irq, irq_count); - item->irq_count = irq_count; - total_irq_count += irq_count; - } /* deep copy */ s->items[ix] = *item; /* allocated from ot_dev_proxy_discover */ g_free(item); } g_array_free(array, TRUE); - s->proxy_irq_map = g_new0(OtDevProxyIrq, total_irq_count); - s->proxy_irq_count = total_irq_count; - /* finally get how many IRQs can be re-routed, so initialize input IRQs */ - qdev_init_gpio_in(DEVICE(s), &ot_dev_proxy_intercepted_irq, - (int)total_irq_count); - unsigned irq_num = 0; - for (unsigned ix = 0; ix < s->dev_count; ix++) { - OtDevProxyItem *item = &s->items[ix]; - if (!item->irq_count) { - continue; - } - item->irq_route_base = irq_num; - for (unsigned irq_off = 0; irq_off < item->irq_count; irq_off++) { - s->proxy_irq_map[irq_num++] = (OtDevProxyIrq){ ix, irq_off }; - } - } + + s->proxy_irq_map = g_new0(OtDevProxyIrq, PROXY_IRQ_INTERCEPT_COUNT); + qdev_init_gpio_in_named(DEVICE(s), &ot_dev_proxy_intercepted_irq, + PROXY_IRQ_INTERCEPT_NAME, + PROXY_IRQ_INTERCEPT_COUNT); array = g_array_new(FALSE, TRUE, sizeof(MemoryRegion *)); object_child_foreach_recursive(ms, &ot_dev_proxy_discover_memory_root, @@ -1760,6 +1838,36 @@ static void ot_dev_proxy_realize(DeviceState *dev, Error **errp) object_child_foreach_recursive(ms, &ot_dev_proxy_map_bus, s); } +static Property ot_dev_proxy_properties[] = { + DEFINE_PROP_CHR("chardev", OtDevProxyState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ot_dev_proxy_reset(DeviceState *dev) +{ + OtDevProxyState *s = OT_DEV_PROXY(dev); + + if (!s->items) { + /* only done once */ + ot_dev_proxy_discover(s); + } + + fifo8_reset(&s->rx_fifo); + memset(&s->rx_hdr, 0, sizeof(s->rx_hdr)); + s->requester_uid = 0; + s->initiator_uid = 0; +} + +static void ot_dev_proxy_realize(DeviceState *dev, Error **errp) +{ + OtDevProxyState *s = OT_DEV_PROXY(dev); + (void)errp; + + qemu_chr_fe_set_handlers(&s->chr, &ot_dev_proxy_can_receive, + &ot_dev_proxy_receive, NULL, + &ot_dev_proxy_be_change, s, NULL, true); +} + static void ot_dev_proxy_init(Object *obj) { OtDevProxyState *s = OT_DEV_PROXY(obj); @@ -1775,7 +1883,7 @@ static void ot_dev_proxy_class_init(ObjectClass *klass, void *data) (void)data; dc->reset = &ot_dev_proxy_reset; - dc->realize = ot_dev_proxy_realize; + dc->realize = &ot_dev_proxy_realize; device_class_set_props(dc, ot_dev_proxy_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index ea13a4ef4805d..70684e55e069a 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -89,7 +89,7 @@ ot_csrng_try_schedule_genbits(unsigned slot, bool ready, bool queued, unsigned r ot_dev_proxy_dispatch_request(char a, char b) "%c%c" ot_dev_proxy_fe_error(int err) "error: %d" -ot_dev_proxy_intercept_irq(const char *dname, const char *did, unsigned irq, bool enable) "%s (%s) %u: enable %u" +ot_dev_proxy_intercept_irq(const char *dname, const char *did, const char *iid, bool enable) "%s (%s) %s: enable %u" ot_dev_proxy_read_buffer(const char *prefix, unsigned devix, bool mbx, unsigned offset, unsigned count) "%s #%u mbx:%u 0x%02x %u" ot_dev_proxy_read_memory(const char *prefix, unsigned devix, unsigned offset, unsigned count) "%s #%u 0x%08x 0x%x" ot_dev_proxy_read_reg(const char *prefix, unsigned devix, unsigned offset) "%s #%u 0x%08x" diff --git a/scripts/opentitan/ot/devproxy.py b/scripts/opentitan/ot/devproxy.py index 77884d4866453..c356960ceba0e 100644 --- a/scripts/opentitan/ot/devproxy.py +++ b/scripts/opentitan/ot/devproxy.py @@ -48,8 +48,10 @@ class ProxyCommandError(Exception): 0x402: 'PE_CANNOT_WRITE_DEVICE', 0x403: 'PE_TRUNCATED_RESPONSE', 0x404: 'PE_INCOMPLETE_WRITE', + 0x405: 'PE_OOM', # internal error 0x801: 'PE_UNSUPPORTED_DEVICE', + 0x802: 'PE_DUPLICATED_UID' } """Proxy error codes.""" @@ -97,6 +99,38 @@ class MemoryRoot(NamedTuple): size: int +class InterruptGroup(NamedTuple): + """Descriptor of a named group of interrupts. + Interrupts follow the meaning of QEMU IRQ: an "asynchronous" wire. + They are not only "interrupts" between a device and an interrupt + controller. + """ + out: bool # output or input IRQ group + num: int # group identifier + count: int # number of IRQ lines in the group + + +class MemoryHitEvent(NamedTuple): + """Event triggered when a memory watcher has been matched.""" + address: int + value: int + write: bool + width: int + role: int + + +InterruptHandler = Callable[['DeviceProxy', str, int, int, Any], None] +"""qemu_irq handler (device, group name, channel, value, *args) +""" + +RequestHandler = Callable[[str, bytes, Any], None] +"""Remote request handler.""" + + +MemoryWatcherHandler = Callable[[MemoryHitEvent, Any], None] +"""Memory Watcher handler.""" + + class DeviceProxy: """Remote device wrapper. @@ -121,6 +155,12 @@ class DeviceProxy: NO_ROLE = 0xf """Disabled role.""" + IRQ_NAME = 'sysbus-irq' + """SysBus Interrupt IRQ identifier.""" + + ALERT_NAME = 'ot-alert-sig' + """OpenTitan Alert IRQ identifier.""" + def __init__(self, proxy: 'ProxyEngine', name: str, devid: int, addr: int, count: int, offset: int): logname = self.__class__.__name__.lower().replace('proxy', '') @@ -134,7 +174,9 @@ def __init__(self, proxy: 'ProxyEngine', name: str, devid: int, addr: int, self._regcount = count self._offset = offset * 4 self._end = offset + count * 4 - self._interrupts: Optional[dict[str, tuple[int, int, int]]] = None + self._interrupts: Optional[dict[str, InterruptGroup]] = None + self._interrupt_handler: Optional[InterruptHandler] = None + self._interrupt_args: list[Any] = [] self._new() @property @@ -234,7 +276,19 @@ def write_buf(self, cmd: str, role: int, addr: int, buffer: bytes) -> int: value, = sunpack(' None: + def register_interrupt_handler(self, handler: InterruptHandler, *args) \ + -> None: + """Register a handler to receive notification from the remote peer. + + :param handler: the handler function + :param args: any argument to forward to the handler + """ + if self._interrupt_handler: + self._log.warning('Overridding previous IRQ handler') + self._interrupt_handler = handler + self._interrupt_args = args + + def intercept_interrupts(self, group: int, mask: int) -> None: """Request the remote proxy to intercept output interrupt for the device Interrupted are reported to the proxy rather than delivered them to @@ -245,38 +299,59 @@ def intercept_interrupts(self, mask: int) -> None: It is safe to intercept the same interrupts several time (in which case the remote proxy should ignore the requests for these IRQ channels and intercept the selected ones that were not intercepted) + + :param group: IRQ group identifier :param mask: a bitmask of interrupt (0..31) to intercept. """ - if not 0 <= mask <= 0xffff_ffff: - raise ValueError('Invalid interupt mask') - request = spack(' None: - """Release previously intercepted interrupts + def release_interrupts(self, group: int, mask: int) -> None: + """Release previously intercepted IRQ lines Interrupted are connected back to their initial destination device. + :param group: IRQ group identifier :param mask: a bitmask of interrupt (0..31) to release. """ - if not 0 <= mask <= 0xffff_ffff: - raise ValueError('Invalid interupt mask') - request = spack(' None: + """Capture or release one or more sysbus interrupts output channels. + + :param irq: the interrupt number to manage + :param enable: whether to intercept or release IRQ channels. + """ + self.capture_sysbus_irqs(1 << irq, enable) + + def capture_sysbus_irqs(self, irq_mask: int, enable: bool) -> None: + """Capture or release one or more sysbus interrupts output channels. :param irq: a bitfield of interrupt channel to manage :param enable: whether to intercept or release IRQ channels. """ - mask = 1 << irq + if self._interrupts is None: + self._enumerate_interrupts() + try: + gnum = self._interrupts[self.IRQ_NAME].num + except KeyError as exc: + raise ValueError(f'No {self.IRQ_NAME} interruption support for ' + f'{self.__class__.__name__}') from exc if enable: - self.intercept_interrupts(mask) + self.intercept_interrupts(gnum, irq_mask) else: - self.release_interrupts(mask) + self.release_interrupts(gnum, irq_mask) - def enumerate_interrupts(self, out: bool) -> Iterator[tuple[str, int]]: + def enumerate_interrupts(self, out: bool) -> Iterator[InterruptGroup]: """Enumerate supported interrupt lines. :param out: True to enumerate output IRQ lines, False for input ones @@ -285,13 +360,9 @@ def enumerate_interrupts(self, out: bool) -> Iterator[tuple[str, int]]: """ if self._interrupts is None: self._enumerate_interrupts() - for name, (_, gin, gout) in self._interrupts.items(): - if not out: - if gin: - yield name, gin - else: - if gout: - yield name, gout + for name, group in self._interrupts.items(): + if group.out == out: + yield name, group def signal_interrupt(self, group: str, irq: int, level: int | bool) -> None: """Set the level of an input interrupt line. @@ -305,15 +376,15 @@ def signal_interrupt(self, group: str, irq: int, level: int | bool) -> None: if self._interrupts is None: self._enumerate_interrupts() if group not in self._interrupts: - raise ValueError(f'No such interrupt group "{group}"') + raise ValueError(f"No such interrupt group '{group}'") grp = self._interrupts[group] - grp_num, irq_count = grp[0:2] - if irq >= irq_count: - raise ValueError(f'No such interrupt {irq} in {group}') + if irq >= grp.count: + raise ValueError(f"No such interrupt {irq} in '{group}' " + f"(<{grp.count})") level = int(level) if level >= (1 << 32): raise ValueError(f'Invalied interrupt level {level}') - request = spack(' None: self._log.fatal('%s', exc) raise + def notify_interrupt(self, group: int, channel: int, value: int) -> None: + """Wired IRQ notification handler.""" + if not self._interrupt_handler: + self._log.warning('Missed IRQ notification') + return + if self._interrupts is None: + self._log.error('IRQ received w/o registration') + return + for gname, igroup in self._interrupts.items(): + if igroup.num == group: + if channel >= igroup.count: + self._log.error('Interrupt %s out of bound: %d', gname, + channel) + return + self._interrupt_handler(self, gname, channel, value, + *self._interrupt_args) + break + else: + self._log.error('Unknow interrupt group %d, ignored', igroup) + @classmethod def _make_sel(cls, device: int, role: int = 0xf) -> int: if not isinstance(device, int) or not 0 <= device <= 0xfff: @@ -341,7 +432,7 @@ def _enumerate_interrupts(self) -> None: except ProxyCommandError as exc: self._log.fatal('%s', exc) raise - grpfmt = ' None: self._log.info('Found %d remote interrupt groups for %s', grpcount, str(self)) self._interrupts = {} - gnum = 0 while irqgroups: grphdr = irqgroups[0:grplen] irqgroups = irqgroups[grplen:] - gin, gout, gname = sunpack(grpfmt, grphdr) + gcount, gnum, gdir, gname = sunpack(grpfmt, grphdr) grpname = gname.rstrip(b'\x00').decode().lower() if grpname in self._interrupts: self._log.error("Multiple devices w/ identical identifier: " "'%s'", grpname) else: - self._interrupts[grpname] = (gnum, gin, gout) - gnum += 1 + group = InterruptGroup(bool(gdir & 0x80), gnum, gcount) + self._interrupts[grpname] = group class MbxHostProxy(DeviceProxy): @@ -938,7 +1028,7 @@ def test_alerts(self, alerts: int) -> None: class MemProxy(DeviceProxy): - """Memroy device proxy. + """Memory device proxy. Specialized DeviceProxy that helps managing random access memory. @@ -982,6 +1072,8 @@ def read(self, address: int, size: int, rel: bool = False) -> bytes: """ if self._role > 0xf: raise ValueError(f'Invalid role {self._role}') + # if specified address is an absolute address, compute the relative + # address within the device addr = (address - self._addr) if not rel else address if not 0 <= addr < self.size: raise ValueError(f'Invalid address 0x{addr:08x}') @@ -1000,6 +1092,8 @@ def write(self, address: int, buffer: bytes, rel: bool = False) -> None: """ if self._role > 0xf: raise ValueError(f'Invalid role {self._role}') + # if specified address is an absolute address, compute the relative + # address within the device addr = (address - self._addr) if not rel else address if not 0 <= addr < self.size: raise ValueError(f'Invalid address 0x{address:08x}') @@ -1089,15 +1183,11 @@ def initialize(self, renew_src_key: bool) -> None: self.write_word(self._role, self.REGS['CTRL'], ctrl) -RequestHandler = Callable[[str, bytes, Any], None] -"""Remote request handler.""" - - class ProxyEngine: """Tool to access and remotely drive devices and memories. """ - VERSION = (0, 13) + VERSION = (0, 14) """Protocol version.""" TIMEOUT = 2.0 @@ -1125,6 +1215,12 @@ class ProxyEngine: HEADER_SIZE = scalc(HEADER) """Proxy header size.""" + NOTIFICATIONS = { + 'W': 'wired_irq', + 'R': 'mr_hit', + } + """Notification dispatch map.""" + def __init__(self): self._log = getLogger('proxy.proxy') self._socket: Optional[socket] = None @@ -1142,8 +1238,7 @@ def __init__(self): self._mroots: dict[int, MemoryRoot] = {} self._request_handler: Optional[RequestHandler] = None self._request_args: list[Any] = [] - self._notifier_handler: Optional[RequestHandler] = None - self._notifier_args: list[Any] = [] + self._watchers: dict[int, tuple[MemoryWatcherHandler, Any]] = {} self._proxies = self._discover_proxies() def connect(self, host: str, port: int) -> None: @@ -1241,7 +1336,7 @@ def discover_memory_spaces(self) -> None: except ProxyCommandError as exc: self._log.fatal('%s', exc) raise - mrfmt = ' None: mrhdr = mregions[0:mrlen] mregions = mregions[mrlen:] mspc, address, size, mname = sunpack(mrfmt, mrhdr) - mspc >>= 4 mrname = mname.rstrip(b'\x00').decode().lower() if mspc in self._mroots: self._log.error("Multiple memory regions w/ identical " - "identifier: '%d'", mrname) + "identifier: '%s' @ %d", mrname, mspc) continue self._mroots[mspc] = MemoryRoot(mrname, address, size) @@ -1265,7 +1359,7 @@ def enumerate_devices(self) -> Iterator[str]: """ yield from self._devices - def enumerate_memory_spaces(self) -> Iterator[str]: + def enumerate_memory_spaces(self) -> Iterator[int]: """Provide an iterator on discovered memory spaces. """ yield from self._mroots @@ -1305,8 +1399,9 @@ def get_region_by_uid(self, uid: int) -> Optional[DeviceProxy]: return self._mroots.get(uid) def intercept_mmio_access(self, root: Optional[MemoryRoot], address: int, - size: int, read: bool, write: bool, stop: int = 0, - priority: int = 1): + size: int, read: bool, write: bool, + handler: MemoryWatcherHandler, *args, **kwargs) \ + -> None: """Request the remote proxy to intercept memory/IO access to a specified region. @@ -1316,12 +1411,17 @@ def intercept_mmio_access(self, root: Optional[MemoryRoot], address: int, :param size: the size of the region to intercept :param read: whether to intercept read access :param write: whether to intercept write access - :param stop: whether to stop auto-discard intercepter after the + :param kwargs: + * 'stop': whether to stop auto-discard intercepter after the specified count of access. Use 0 to disable auto-discard - :param prority: priority of the intercepter + * 'prority': priority of the intercepter """ if not read and not write: raise ValueError('Read or/and Write should be specified') + priority = kwargs.pop('priority', 1) + stop = kwargs.pop('stop', 0) + if kwargs: + raise ValueError(f'Unknown arguements: {", ".join(kwargs)}') if not isinstance(priority, int) or not priority or priority > 0x3f: raise ValueError('Invalid priority') if not isinstance(stop, int) or stop > 0x3f: @@ -1343,15 +1443,16 @@ def intercept_mmio_access(self, root: Optional[MemoryRoot], address: int, header |= 0b10 header |= priority << 2 header |= stop << 10 - header |= 0x3ff << 16 - header |= rid << 28 + header |= rid << 24 request = spack(' None: """Register a handler to receive requests from the remote peer. @@ -1362,16 +1463,6 @@ def register_request_handler(self, handler: RequestHandler, *args) -> None: self._request_handler = handler self._request_args = args - def register_notification_handler(self, handler: RequestHandler, *args) \ - -> None: - """Register a handler to receive notification from the remote peer. - - :param handler: the handler function - :param args: any argument to forward to the handler - """ - self._notifier_handler = handler - self._notifier_args = args - def exchange(self, command: str, payload: Optional[bytes] = None) -> bytes: """Execute a communication trip with the remote target. @@ -1508,14 +1599,20 @@ def _notify(self) -> None: rcmd, payload = self._requ_q.popleft() notify = rcmd.startswith('^') if notify: - if not self._notifier_handler: - self._log.warning('Missed notification %s', rcmd) + try: + handler = self.NOTIFICATIONS[rcmd[1:]] + except KeyError: + self._log.error('Unknown notification type: %s', + rcmd[1:]) + continue + dispatcher = getattr(self, f'_dispatch_{handler}', None) + if not dispatcher: + self._log.error('Unsupported notification: %s', handler) continue - self._notifier_handler(rcmd[1:], payload, - *self._notifier_args) + dispatcher(payload) else: if not self._request_handler: - self._log.warning('Missed notification %s', rcmd) + self._log.warning('Missed request %s', rcmd) continue self._request_handler(rcmd, payload, *self._request_args) @@ -1524,6 +1621,44 @@ def _notify(self) -> None: self._log.fatal('Exception: %s', exc) break + def _dispatch_wired_irq(self, payload: bytes) -> None: + wifmt = ' None: + rifmt = '> 4 + wid = reg & 0x3f + role = reg >> 12 + mhevent = MemoryHitEvent(addr, val, write, width, role) + try: + handler, args = self._watchers[wid] + except KeyError: + self._log.error('Memory Hit on unknown watcher: %d', wid) + return + try: + handler(mhevent, *args) + except Exception as exc: + self._log.critical('Exception in Memory Hit hit handler: %s', exc) + raise + def _kick_off(self) -> None: """Start engine. """ From 435768e5d1b34703f63944db590caac105414d89 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 15:39:38 +0200 Subject: [PATCH 57/65] [ot] scripts/opentitan: ot.util.log: add a color option for log message Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 68 ++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 997ef508220e2..26233ffaa881d 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -8,6 +8,7 @@ from os import isatty from sys import stderr +from typing import NamedTuple, Union import logging @@ -16,6 +17,11 @@ if k not in ('NOTSET', 'WARN')} +class Color(NamedTuple): + """Simple color wrapper.""" + color: str + + class ColorLogFormatter(logging.Formatter): """Custom log formatter for ANSI terminals. Colorize log levels. @@ -28,31 +34,38 @@ class ColorLogFormatter(logging.Formatter): * 'color' (boolean): enable/disable log level colorization """ - GREY = "\x1b[38;20m" - YELLOW = "\x1b[33;1m" - RED = "\x1b[31;1m" - MAGENTA = "\x1b[35;1m" - WHITE = "\x1b[37;1m" + COLORS = { + 'GREY': "\x1b[38;20m", + 'GREEN': "\x1b[32;1m", + 'YELLOW': "\x1b[33;1m", + 'RED': "\x1b[31;1m", + 'BLUE': "\x1b[34;1m", + 'MAGENTA': "\x1b[35;1m", + 'CYAN': "\x1b[36;1m", + 'WHITE': "\x1b[37;1m", + } + RESET = "\x1b[0m" FMT_LEVEL = '%(levelname)8s' - COLORS = { - logging.DEBUG: GREY, - logging.INFO: WHITE, - logging.WARNING: YELLOW, - logging.ERROR: RED, - logging.CRITICAL: MAGENTA, + LOG_COLORS = { + logging.DEBUG: COLORS['GREY'], + logging.INFO: COLORS['WHITE'], + logging.WARNING: COLORS['YELLOW'], + logging.ERROR: COLORS['RED'], + logging.CRITICAL: COLORS['MAGENTA'], } def __init__(self, *args, **kwargs): kwargs = dict(kwargs) name_width = kwargs.pop('name_width', 10) - self._use_ansi = kwargs.pop('color', isatty(stderr.fileno())) + self._use_ansi = kwargs.pop('ansi', isatty(stderr.fileno())) use_func = kwargs.pop('funcname', False) use_ms = kwargs.pop('ms', False) use_time = kwargs.pop('time', use_ms) use_lineno = kwargs.pop('lineno', False) super().__init__(*args, **kwargs) + self._logger_colors: dict[str, tuple[str, str]] = {} if use_time: tfmt = '%(asctime)s ' if not use_ms else '%(asctime)s.%(msecs)03d ' else: @@ -61,27 +74,42 @@ def __init__(self, *args, **kwargs): fnc = f' %(funcName)s{sep}' if use_func else ' ' sep = ' ' if not use_func else '' lno = f'{sep}[%(lineno)d] ' if use_lineno else '' - fmt_trail = f' %(name)-{name_width}s{fnc}{lno}%(message)s' + fmt_trail = f' %(name)-{name_width}s{fnc}{lno}%(scr)s%(message)s%(ecr)s' self._plain_format = f'{tfmt}{self.FMT_LEVEL}{fmt_trail}' self._color_formats = { lvl: f'{tfmt}{clr}{self.FMT_LEVEL}{self.RESET}{fmt_trail}' - for lvl, clr in self.COLORS.items() + for lvl, clr in self.LOG_COLORS.items() } self._formatter_args = ['%H:%M:%S'] if use_time else [] def format(self, record): log_fmt = self._color_formats[record.levelno] if self._use_ansi \ else self._plain_format + scr, ecr = ('', '') + if self._use_ansi and record.name in self._logger_colors: + scr, ecr = self._logger_colors[record.name] + setattr(record, 'scr', scr) + setattr(record, 'ecr', ecr) formatter = logging.Formatter(log_fmt, *self._formatter_args) return formatter.format(record) + def add_logger_colors(self, logname: str, color: str) -> None: + """Assign a color to the message of a specific logger.""" + if not self._use_ansi: + return + start_color = self.COLORS.get(color.upper(), '') + if not start_color: + return + end_color = self.RESET + self._logger_colors[logname] = (start_color, end_color) -def configure_loggers(level: int, *lognames: list[str], **kwargs) \ - -> list[logging.Logger]: + +def configure_loggers(level: int, *lognames: list[Union[str | int | Color]], + **kwargs) -> list[logging.Logger]: """Configure loggers. :param level: level (stepping: 1) - :param lognames: one or more loggers to configure + :param lognames: one or more loggers to configure, or log modifiers :param kwargs: optional features :return: configured loggers or level change """ @@ -99,10 +127,16 @@ def configure_loggers(level: int, *lognames: list[str], **kwargs) \ logh.setFormatter(formatter) loggers: list[logging.Logger] = [] logdefs: list[tuple[list[str], logging.Logger]] = [] + color = None for logdef in lognames: if isinstance(logdef, int): loglevel += -10 * logdef continue + if isinstance(logdef, Color): + color = logdef.color + continue + if color: + formatter.add_logger_colors(logdef, color) log = logging.getLogger(logdef) log.setLevel(max(logging.DEBUG, loglevel)) loggers.append(log) From 2358f853d827982030ed7af9a190cd988af48619 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 29 May 2024 15:40:42 +0200 Subject: [PATCH 58/65] [ot] scripts/opentitan: pyot.py: assign a color to QEMU VCP messages It helps differentiating QEMU traces from pyot.py and other tool messages Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 1a507741d117c..a16755b66ad41 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -35,7 +35,7 @@ from traceback import format_exc from typing import Any, Iterator, NamedTuple, Optional -from ot.util.log import configure_loggers +from ot.util.log import Color as LogColor, configure_loggers from ot.util.misc import EasyDict @@ -1632,7 +1632,9 @@ def main(): close(tmpfd) args.result = tmp_result - log = configure_loggers(args.verbose, 'pyot', debug=args.debug, + log = configure_loggers(args.verbose, 'pyot', + LogColor('blue'), 'pyot.vcp', + debug=args.debug, info=args.info, warning=args.warn)[0] qfm = QEMUFileManager(args.keep_tmp) From 358fdb487813fa7a70f223e26535262e9fec7e4b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 30 May 2024 11:01:04 +0200 Subject: [PATCH 59/65] [ot] scripts/opentitan: pyot.py: add an option to show log message time Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 28 ++++++++++++++++++---------- scripts/opentitan/pyot.py | 4 +++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index a9915782d2a9f..6dd123107ff54 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -5,11 +5,12 @@ ## Usage ````text -usage: pyot.py [-h] [-D DELAY] [-i N] [-L LOG_FILE] [-M LOG] [-m MACHINE] [-Q OPTS] [-q QEMU] - [-p DEVICE] [-t TRACE] [-S FIRST_SOC] [-s] [-U] [-b file] [-c JSON] [-e] [-f RAW] - [-K] [-l file] [-O RAW] [-o VMEM] [-r ELF] [-w CSV] [-x file] [-X] [-F TEST] - [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] [--debug DEBUG] [--info INFO] - [--warn WARN] +usage: pyot.py [-h] [-D DELAY] [-i N] [-L LOG_FILE] [-M LOG] [-m MACHINE] + [-Q OPTS] [-q QEMU] [-p DEVICE] [-t TRACE] [-S FIRST_SOC] [-s] + [-U] [-b file] [-c JSON] [-e] [-f RAW] [-K] [-l file] [-O RAW] + [-o VMEM] [-r ELF] [-w CSV] [-x file] [-X] [-F TEST] + [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] [--log-time] + [--debug LOGGER] [--info LOGGER] [--warn LOGGER] OpenTitan QEMU unit test sequencer. @@ -19,14 +20,16 @@ options: Virtual machine: -D DELAY, --start-delay DELAY QEMU start up delay before initial comm - -i N, --icount N virtual instruction counter with 2^N clock ticks per inst. + -i N, --icount N virtual instruction counter with 2^N clock ticks per + inst. -L LOG_FILE, --log_file LOG_FILE log file for trace and log messages -M LOG, --log LOG log message types -m MACHINE, --machine MACHINE virtual machine (default to ot-earlgrey) -Q OPTS, --opts OPTS QEMU verbatim option (can be repeated) - -q QEMU, --qemu QEMU path to qemu application (default: build/qemu-system-riscv32) + -q QEMU, --qemu QEMU path to qemu application (default: build/qemu-system- + riscv32) -p DEVICE, --device DEVICE serial port device name (default to localhost:8000) -t TRACE, --trace TRACE @@ -34,7 +37,8 @@ Virtual machine: -S FIRST_SOC, --first-soc FIRST_SOC Identifier of the first SoC, if any -s, --singlestep enable "single stepping" QEMU execution mode - -U, --muxserial enable multiple virtual UARTs to be muxed into same host output channel + -U, --muxserial enable multiple virtual UARTs to be muxed into same + host output channel Files: -b file, --boot file bootloader 0 file @@ -42,7 +46,8 @@ Files: path to configuration file -e, --embedded-flash generate an embedded flash image file -f RAW, --flash RAW SPI flash image file - -K, --keep-tmp Do not automatically remove temporary files and dirs on exit + -K, --keep-tmp Do not automatically remove temporary files and dirs + on exit -l file, --loader file ROM trampoline to execute, if any -O RAW, --otp-raw RAW @@ -55,7 +60,8 @@ Files: Execution: -F TEST, --filter TEST - run tests with matching filter, prefix with "!" to exclude matching tests + run tests with matching filter, prefix with "!" to + exclude matching tests -k SECONDS, --timeout SECONDS exit after the specified seconds (default: 60 secs) -z, --list show a list of tests to execute and exit @@ -67,6 +73,7 @@ Execution: Extras: -v, --verbose increase verbosity -d enable debug mode + --log-time show local time in log messages --debug LOGGER assign debug level to logger(s) --info LOGGER assign info level to logger(s) --warn LOGGER assign warning level to logger(s) @@ -165,6 +172,7 @@ This tool may be used in two ways, which can be combined: * `-v` / `--verbose` can be repeated to increase verbosity of the script, mostly for debug purpose. * `-d` only useful to debug the script, reports any Python traceback to the standard error stream. +* `--log-time` show local time before each logged message * `--debug` enable the debug level for the selected logger, may be repeated * `--info` enable the info level for the selected logger, may be repeated * `--warn` enable the warning level for the selected logger, may be repeated diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index a16755b66ad41..c27fac3100c91 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1608,6 +1608,8 @@ def main(): help='increase verbosity') extra.add_argument('-d', action='store_true', help='enable debug mode') + extra.add_argument('--log-time', action='store_true', + help='show local time in log messages') extra.add_argument('--debug', action='append', metavar='LOGGER', help='assign debug level to logger(s)') extra.add_argument('--info', action='append', metavar='LOGGER', @@ -1634,7 +1636,7 @@ def main(): log = configure_loggers(args.verbose, 'pyot', LogColor('blue'), 'pyot.vcp', - debug=args.debug, + ms=args.log_time, debug=args.debug, info=args.info, warning=args.warn)[0] qfm = QEMUFileManager(args.keep_tmp) From 5fc401c1bc0ae8ac6ba1cbd705b81c718a07ff30 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 27 May 2024 11:37:30 +0200 Subject: [PATCH 60/65] [ot] .gitlab-ci.d: update baremetal tests Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index fd7823203baaf..735d94b3d6e80 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240529-1" + BAREMETAL_REF: "240530-1" QEMU_BUILD_OPTS: "" include: From 35aca4e391cd1c4c43b762dd29735fe1fea7583a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 May 2024 09:40:49 +0200 Subject: [PATCH 61/65] [ot] scripts/opentitan: log.py: fix missing getLevelNamesMapping getLevelNamesMapping() has been added in Python 3.11. Add a replacement function for previous Python releases. Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 26233ffaa881d..9f696802f15ec 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -13,7 +13,16 @@ import logging -_LEVELS = {k: v for k, v in logging.getLevelNamesMapping().items() +try: + getLevelNamesMapping = logging.getLevelNamesMapping +except AttributeError: + # getLevelNamesMapping not supported on old Python versions (< 3.11) + def getLevelNamesMapping() -> dict[str, int]: + # pylint: disable=invalid-name,missing-function-docstring + return {lvl: getattr(logging, lvl) for lvl in + 'CRITICAL FATAL ERROR WARNING INFO DEBUG'.split()} + +_LEVELS = {k: v for k, v in getLevelNamesMapping().items() if k not in ('NOTSET', 'WARN')} From b218d84032ae45827ac4b00b400cedb81649327e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 30 May 2024 20:10:49 +0200 Subject: [PATCH 62/65] [ot] hw/opentitan: ot_timer: fix bug when CFG0.STEP is 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting CFG0.STEP to 0 and a compare value greater than the current timer value, QEMU would crash with a division by 0. Fix this issue by skipping QEMU internal timer scheduling when STEP=0 (same as a disabled ot_timer). Signed-off-by: Loïc Lefort --- hw/opentitan/ot_timer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/opentitan/ot_timer.c b/hw/opentitan/ot_timer.c index d0699b8468567..07ff2ab493f6d 100644 --- a/hw/opentitan/ot_timer.c +++ b/hw/opentitan/ot_timer.c @@ -169,7 +169,8 @@ static void ot_timer_rearm(OtTimerState *s, bool reset_origin) s->origin_ns = now; } - if (!ot_timer_is_active(s)) { + uint32_t step = FIELD_EX32(s->regs[R_CFG0], CFG0, STEP); + if (!ot_timer_is_active(s) || !step) { timer_del(s->timer); return; } From 41dab4434ac0b899aa5fae5b4c9129abbc915a2b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 May 2024 14:25:18 +0200 Subject: [PATCH 63/65] [ot] scripts/opentitan: pyot.py: accept 'auto' as `--icount` argument. Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 17 ++++++----------- scripts/opentitan/pyot.py | 10 +++++----- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index 6dd123107ff54..cbffa6e8df390 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -5,7 +5,7 @@ ## Usage ````text -usage: pyot.py [-h] [-D DELAY] [-i N] [-L LOG_FILE] [-M LOG] [-m MACHINE] +usage: pyot.py [-h] [-D DELAY] [-i ICOUNT] [-L LOG_FILE] [-M LOG] [-m MACHINE] [-Q OPTS] [-q QEMU] [-p DEVICE] [-t TRACE] [-S FIRST_SOC] [-s] [-U] [-b file] [-c JSON] [-e] [-f RAW] [-K] [-l file] [-O RAW] [-o VMEM] [-r ELF] [-w CSV] [-x file] [-X] [-F TEST] @@ -20,8 +20,9 @@ options: Virtual machine: -D DELAY, --start-delay DELAY QEMU start up delay before initial comm - -i N, --icount N virtual instruction counter with 2^N clock ticks per - inst. + -i ICOUNT, --icount ICOUNT + virtual instruction counter with 2^ICOUNT clock ticks + per inst. or 'auto' -L LOG_FILE, --log_file LOG_FILE log file for trace and log messages -M LOG, --log LOG log message types @@ -91,14 +92,8 @@ This tool may be used in two ways, which can be combined: * `-D` / `--start-delay` VM start up delay. Grace period to wait for the VM to start up before attempting to communicate with its char devices. * `-i` / `--icount` to specify virtual instruction counter with 2^N clock ticks per instruction. - This option if often used with two specific values: - * `-i 0` can be used to improve time synchronisation between the virtual CPU and the virtual HW: - as many OpenTitan tests rely on specific CPU clock counts for the HW to complete some action, - running QEMU without this option tends to favor CPU execution speed over HW emulation. With - this option, the vCPU is better synchronized, trying to emulate a 1GHz-clock vCPU. - * `-i 6` can be used to slow down vCPU virtual clock to a ~10-15MHz clock pace, which better - matches the expected FPGA-based lowRISC CPU. - Note that this option slows down the execution of guest applications. + Use 'auto' to enable QEMU adaptive icount counter. Note that this option slows down the execution + of guest applications. * `-L` / `--log_file` specify the log file for trace and log messages from QEMU. * `-M` / `--log` specify which log message types should be logged; most useful types are: * `in_asm` for guest instruction disassembly, diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index c27fac3100c91..d36f55e52e9fa 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -59,7 +59,7 @@ class TestResult(NamedTuple): name: str result: str time: ExecTime - icount: Optional[int] + icount: Optional[str] error: str @@ -1299,7 +1299,7 @@ def _build_qemu_command(self, args: Namespace, qemu_args.append('-singlestep') if 'icount' in args: if args.icount is not None: - qemu_args.extend(('-icount', f'{args.icount}')) + qemu_args.extend(('-icount', f'shift={args.icount}')) mux = f'mux={"on" if args.muxserial else "off"}' try: start_delay = float(getattr(args, 'start_delay') or @@ -1532,9 +1532,9 @@ def main(): rel_qemu_path = relpath(qemu_path) if qemu_path else '?' qvm.add_argument('-D', '--start-delay', type=float, metavar='DELAY', help='QEMU start up delay before initial comm') - qvm.add_argument('-i', '--icount', metavar='N', type=int, - help='virtual instruction counter with 2^N clock ticks' - ' per inst.') + qvm.add_argument('-i', '--icount', + help='virtual instruction counter with 2^ICOUNT clock ' + 'ticks per inst. or \'auto\'') qvm.add_argument('-L', '--log_file', help='log file for trace and log messages') qvm.add_argument('-M', '--log', action='append', From 7dc59dca36a48e68c37aa050999ab2b85442d644 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 May 2024 14:32:12 +0200 Subject: [PATCH 64/65] [ot] .gitlab-ci.d: update baremetal tests Signed-off-by: Emmanuel Blot --- .gitlab-ci.d/opentitan/qemu-ot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/opentitan/qemu-ot.yml b/.gitlab-ci.d/opentitan/qemu-ot.yml index 735d94b3d6e80..1950cf41c23e1 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240530-1" + BAREMETAL_REF: "240531-1" QEMU_BUILD_OPTS: "" include: From 837ed5e9b412896ef936d62d4663d0616df49791 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 31 May 2024 15:49:17 +0200 Subject: [PATCH 65/65] [ot] .github: update linter and format tools to LLVM v18. Signed-off-by: Emmanuel Blot --- .github/workflows/build_test.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_test.yaml b/.github/workflows/build_test.yaml index a18991874fd7c..8d8d9b96af507 100644 --- a/.github/workflows/build_test.yaml +++ b/.github/workflows/build_test.yaml @@ -16,9 +16,9 @@ jobs: run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && - sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" && + sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" && sudo apt-get update && - sudo apt-get install -y git make pkg-config clang-17 cmake ninja-build python3 rust-all \ + sudo apt-get install -y git make pkg-config clang-18 cmake ninja-build python3 rust-all \ libpixman-1-dev - name: Check out QEMU uses: actions/checkout@v4 @@ -28,7 +28,7 @@ jobs: git clean -dffx subprojects mkdir build-clang (cd build-clang && - ../configure --cc=clang-17 --disable-werror --target-list=riscv32-softmmu,riscv64-softmmu) + ../configure --cc=clang-18 --disable-werror --target-list=riscv32-softmmu,riscv64-softmmu) - name: Build run: | ninja -C build-clang && @@ -69,9 +69,9 @@ jobs: run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && - sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" && + sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" && sudo apt-get update && - sudo apt-get install -y clang-format-17 + sudo apt-get install -y clang-format-18 - name: Check out QEMU uses: actions/checkout@v4 - name: Check execution @@ -86,9 +86,9 @@ jobs: run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && - sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" && + sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" && sudo apt-get update && - sudo apt-get install -y clang-tidy-17 + sudo apt-get install -y clang-tidy-18 - name: Check out QEMU uses: actions/checkout@v4 - name: Download QEMU source artifacts @@ -137,7 +137,7 @@ jobs: run: | wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && - sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main" && + sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" && sudo apt-get update && sudo apt-get install -y git make pkg-config gcc cmake ninja-build python3 rust-all \ libpixman-1-dev