From f4ce129d21f3721db4a83a837f3d1bc3b26396c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Jun 2024 11:31:31 +0200 Subject: [PATCH 01/43] [ot] hw/opentitan: ot_otp_dj: secret partition should be DAI-readable unless locked Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 41 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 477093b861ce..075a9fd01d51 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1330,22 +1330,10 @@ static bool ot_otp_dj_is_part_digest_offset(int part, hwaddr addr) static bool ot_otp_dj_is_readable(OtOTPDjState *s, int partition) { if (OtOTPPartDescs[partition].secret) { - /* secret partition cannot be read (except their digest) */ - return false; - } - - if (!OtOTPPartDescs[partition].read_lock) { - /* read lock is not supported for this partition */ - return true; + /* secret partitions are only readable if digest is not yet set. */ + return ot_otp_dj_get_buffered_part_digest(s, partition) == 0u; } - if (!OtOTPPartDescs[partition].read_lock_csr && - !s->partctrls[partition].read_lock) { - /* hw read lock, not unlocked */ - return false; - } - - bool rdaccess; uint32_t reg; switch (partition) { @@ -1413,20 +1401,21 @@ static bool ot_otp_dj_is_readable(OtOTPDjState *s, int partition) error_setg(&error_fatal, "CSR register not defined"); g_assert_not_reached(); } - rdaccess = (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); - } else { - if (reg != UINT32_MAX) { - error_setg(&error_fatal, "Unexpected CSR register"); - g_assert_not_reached(); - } - /* - * hwdigest-protected partition are only readable if digest is not yet - * set. - */ - rdaccess = ot_otp_dj_get_buffered_part_digest(s, partition) == 0u; + return (bool)SHARED_FIELD_EX32(s->regs[reg], READ_LOCK); + } + + if (reg != UINT32_MAX) { + error_setg(&error_fatal, "Unexpected CSR register"); + g_assert_not_reached(); + } + + if (!OtOTPPartDescs[partition].read_lock) { + /* read lock is not supported for this partition */ + return true; } - return rdaccess; + /* hw read lock, not locked */ + return !s->partctrls[partition].read_lock; } static void From 6dbbac52fd341c075478dc503dcc144dd321a344 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Jun 2024 15:26:59 +0200 Subject: [PATCH 02/43] [ot] .gitlab-ci.d: update OTP DJ unit test 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 7fa79370899b..c6a9de9964af 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240606-1" + BAREMETAL_REF: "240628-1" QEMU_BUILD_OPTS: "" include: From 1f17b52a2a660117d9dc9d8ae255c5710c696ace Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 12 Jun 2024 10:51:17 +0200 Subject: [PATCH 03/43] [ot] system: memory.c: add flatview and address space simplified print functions These functions enables printing the AS and/or the flatview of a single AS. For example, this can be used to report the flatview exposed to a single vCPU. Signed-off-by: Emmanuel Blot --- include/exec/memory.h | 3 +++ system/memory.c | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 8626a355b310..8dbe866c09ae 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -2578,6 +2578,9 @@ void memory_global_dirty_log_start(unsigned int flags); void memory_global_dirty_log_stop(unsigned int flags); void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled); +void mtree_print_as_flatview(AddressSpace *as, bool dispatch_tree, bool owner); +void mtree_print_as_simple(AddressSpace *as, bool dispatch_tree, bool owner, + bool disabled); bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, bool is_write, diff --git a/system/memory.c b/system/memory.c index a229a79988fc..3ad0f18d4977 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3436,6 +3436,38 @@ static void mtree_print_flatview(gpointer key, gpointer value, qemu_printf("\n"); } +void mtree_print_as_flatview(AddressSpace *as, bool dispatch_tree, bool owner) +{ + struct FlatViewInfo fvi = { + .counter = 0, + .dispatch_tree = dispatch_tree, + .owner = owner, + }; + + FlatView *view = address_space_get_flatview(as); + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + if (ac->has_memory) { + fvi.ac = ac; + } + + GArray *ases = g_array_new(false, false, sizeof(as)); + g_array_append_val(ases, as); + + mtree_print_flatview(view, ases, &fvi); + + g_array_free(ases, false); +} + +static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled, + AddressSpace *sas); + +void mtree_print_as_simple(AddressSpace *as, bool dispatch_tree, bool owner, + bool disabled) +{ + mtree_info_as(dispatch_tree, owner, disabled, as); +} + + static gboolean mtree_info_flatview_free(gpointer key, gpointer value, gpointer user_data) { @@ -3529,7 +3561,8 @@ static gboolean mtree_info_as_free(gpointer key, gpointer value, return true; } -static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled) +static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled, + AddressSpace *sas) { MemoryRegionListHead ml_head; MemoryRegionList *ml, *ml2; @@ -3545,6 +3578,9 @@ static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled) QTAILQ_INIT(&ml_head); QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { + if (sas && as != sas) { + continue; + } /* Create hashtable, key=AS root MR, value = list of AS */ as_same_root_mr_list = g_hash_table_lookup(views, as->root); as_same_root_mr_list = g_slist_insert_sorted(as_same_root_mr_list, as, @@ -3574,7 +3610,7 @@ void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled) if (flatview) { mtree_info_flatview(dispatch_tree, owner); } else { - mtree_info_as(dispatch_tree, owner, disabled); + mtree_info_as(dispatch_tree, owner, disabled, NULL); } } From 714a844549c7809017b68aee9c3fa3d0d698f887 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 7 Jun 2024 15:41:12 +0200 Subject: [PATCH 04/43] [ot] scripts/opentitan: pyot.py: add a machine 'variant' option. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 95 ++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index fb3ae2927667..f64abeb6ecfb 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1190,48 +1190,50 @@ def _cleanup_temp_files(self, storage: dict[str, set[str]]) -> None: for filename in files: delete_file(filename) - def _build_qemu_command(self, args: Namespace, - opts: Optional[list[str]] = None) \ - -> EasyDict[str, Any]: - """Build QEMU command line from argparser values. - - :param args: the parsed arguments - :param opts: any QEMU-specific additional options - :return: a dictionary defining how to execute the command - """ - if args.qemu is None: - raise ValueError('QEMU path is not defined') - qemu_args = [ - args.qemu, - '-M', - args.machine - ] + def _build_qemu_fw_args(self, args: Namespace) \ + -> tuple[str, str, list[str]]: rom_exec = bool(args.rom_exec) roms = args.rom or [] multi_rom = (len(roms) + int(rom_exec)) > 1 # generate pre-application ROM option - rom_count = 0 - for rom in roms: - 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(f'{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={rom_path},digest=fake' - qemu_args.extend(('-object', rom_opt)) - rom_count += 1 + fw_args: list[str] = [] + machine = args.machine + variant = args.variant + chiplet_count = 1 + if variant: + machine = f'{machine},variant={variant}' + try: + chiplet_count = sum(int(x) + for x in re.split(r'[A-Za-z]', variant) + if x) + except ValueError: + self._log.warning('Unknown variant syntax %s', variant) + for chip_id in range(chiplet_count): + rom_count = 0 + for rom in roms: + 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: + if chiplet_count == 1: + rom_ids.append(f'{args.first_soc}.') + else: + rom_ids.append(f'{args.first_soc}{chip_id}.') + 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={rom_path},digest=fake' + fw_args.extend(('-object', rom_opt)) + rom_count += 1 xtype = None if args.exec: exec_path = self.abspath(args.exec) xtype = self.guess_test_type(exec_path) if xtype == 'spiflash': - qemu_args.extend(('-drive', - f'if=mtd,format=raw,file={exec_path}')) + fw_args.extend(('-drive', + f'if=mtd,format=raw,file={exec_path}')) else: if xtype != 'elf': raise ValueError(f'No support for test type: {xtype} ' @@ -1240,17 +1242,36 @@ def _build_qemu_command(self, args: Namespace, # generate ROM option for the application itself rom_ids = [] if args.first_soc: - rom_ids.append(f'{args.first_soc}.') + if chiplet_count == 1: + rom_ids.append(f'{args.first_soc}.') + else: + rom_ids.append(f'{args.first_soc}0.') 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)) + fw_args.extend(('-object', rom_opt)) rom_count += 1 else: - qemu_args.extend(('-kernel', exec_path)) + fw_args.extend(('-kernel', exec_path)) + return machine, xtype, fw_args + + def _build_qemu_command(self, args: Namespace, + opts: Optional[list[str]] = None) \ + -> EasyDict[str, Any]: + """Build QEMU command line from argparser values. + + :param args: the parsed arguments + :param opts: any QEMU-specific additional options + :return: a dictionary defining how to execute the command + """ + if args.qemu is None: + raise ValueError('QEMU path is not defined') + machine, xtype, fw_args = self._build_qemu_fw_args(args) + qemu_args = [args.qemu, '-M', machine] + qemu_args.extend(fw_args) temp_files = defaultdict(set) if all((args.otp, args.otp_raw)): raise ValueError('OTP VMEM and RAW options are mutually exclusive') @@ -1561,6 +1582,8 @@ def main(): const=True, help='enable multiple virtual UARTs to be muxed into ' 'same host output channel') + qvm.add_argument('-V', '--variant', + help='machine variant (machine specific)') files = argparser.add_argument_group(title='Files') files.add_argument('-b', '--boot', metavar='file', help='bootloader 0 file') From 29de78289d4db070a7a87520530ac3328206db09 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 10 Jun 2024 11:14:00 +0200 Subject: [PATCH 05/43] [ot] scripts/opentitan: pyot.py: specify the MTD bus instance for SPIflash Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index f64abeb6ecfb..ec6fde11621d 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -1233,7 +1233,7 @@ def _build_qemu_fw_args(self, args: Namespace) \ xtype = self.guess_test_type(exec_path) if xtype == 'spiflash': fw_args.extend(('-drive', - f'if=mtd,format=raw,file={exec_path}')) + f'if=mtd,bus=0,format=raw,file={exec_path}')) else: if xtype != 'elf': raise ValueError(f'No support for test type: {xtype} ' From 025096b9f935e4988fb070f260e4d7548a7fab91 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 10 Jun 2024 12:18:38 +0200 Subject: [PATCH 06/43] [ot] hw/riscv: ibex_common: add an optional offset to memmap functions Signed-off-by: Emmanuel Blot --- hw/riscv/ibex_common.c | 18 ++++++++++-------- include/hw/riscv/ibex_common.h | 22 ++++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c index b9bec6408822..5a6f90d50f33 100644 --- a/hw/riscv/ibex_common.c +++ b/hw/riscv/ibex_common.c @@ -231,9 +231,9 @@ void ibex_realize_devices(DeviceState **devices, BusState *bus, } } -void ibex_map_devices_mask(DeviceState **devices, MemoryRegion **mrs, - const IbexDeviceDef *defs, unsigned count, - uint32_t region_mask) +void ibex_map_devices_mask_offset(DeviceState **devices, MemoryRegion **mrs, + const IbexDeviceDef *defs, unsigned count, + uint32_t region_mask, uint32_t offset) { for (unsigned idx = 0; idx < count; idx++) { DeviceState *dev = devices[idx]; @@ -256,7 +256,8 @@ void ibex_map_devices_mask(DeviceState **devices, MemoryRegion **mrs, if (mr) { ibex_mmio_map_device(busdev, mr, mem, IBEX_MEMMAP_GET_ADDRESS( - memmap->base), + memmap->base) + + offset, memmap->priority); } } @@ -268,9 +269,9 @@ void ibex_map_devices_mask(DeviceState **devices, MemoryRegion **mrs, } } -void ibex_map_devices_ext_mask(DeviceState *dev, MemoryRegion **mrs, - const IbexDeviceMapDef *defs, unsigned count, - uint32_t region_mask) +void ibex_map_devices_ext_mask_offset( + DeviceState *dev, MemoryRegion **mrs, const IbexDeviceMapDef *defs, + unsigned count, uint32_t region_mask, uint32_t offset) { for (unsigned ix = 0; ix < count; ix++) { const IbexDeviceMapDef *def = &defs[ix]; @@ -294,7 +295,8 @@ void ibex_map_devices_ext_mask(DeviceState *dev, MemoryRegion **mrs, if (mr) { ibex_mmio_map_device(sdev, mr, mem, IBEX_MEMMAP_GET_ADDRESS( - memmap->base), + memmap->base) + + offset, memmap->priority); } } diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index b3c87178ea3b..dea242620210 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -424,14 +424,20 @@ void ibex_realize_devices(DeviceState **devices, BusState *bus, void ibex_connect_devices(DeviceState **devices, const IbexDeviceDef *defs, unsigned count); #define ibex_map_devices(_devs_, _mrs_, _defs_, _cnt_) \ - ibex_map_devices_mask(_devs_, _mrs_, _defs_, _cnt_, \ - IBEX_MEMMAP_DEFAULT_REG_MASK); -void ibex_map_devices_mask(DeviceState **devices, MemoryRegion **mrs, - const IbexDeviceDef *defs, unsigned count, - uint32_t region_mask); -void ibex_map_devices_ext_mask(DeviceState *dev, MemoryRegion **mrs, - const IbexDeviceMapDef *defs, unsigned count, - uint32_t region_mask); + ibex_map_devices_offset(_devs_, _mrs_, _defs_, _cnt_, 0u) +#define ibex_map_devices_offset(_devs_, _mrs_, _defs_, _cnt_, _off_) \ + ibex_map_devices_mask_offset(_devs_, _mrs_, _defs_, _cnt_, \ + IBEX_MEMMAP_DEFAULT_REG_MASK, _off_) +#define ibex_map_devices_mask(_devs_, _mrs_, _defs_, _cnt_, _msk_) \ + ibex_map_devices_mask_offset(_devs_, _mrs_, _defs_, _cnt_, _msk_, 0u) +void ibex_map_devices_mask_offset(DeviceState **devices, MemoryRegion **mrs, + const IbexDeviceDef *defs, unsigned count, + uint32_t region_mask, uint32_t offset); +#define ibex_map_devices_ext_mask(_dev_, _mrs_, _defs_, _cnt_, _msk_) \ + ibex_map_devices_ext_mask_offset(_dev_, _mrs_, _defs_, _cnt_, _msk_, 0u) +void ibex_map_devices_ext_mask_offset( + DeviceState *dev, MemoryRegion **mrs, const IbexDeviceMapDef *defs, + unsigned count, uint32_t region_mask, uint32_t offset); void ibex_configure_devices(DeviceState **devices, BusState *bus, const IbexDeviceDef *defs, unsigned count); void ibex_identify_devices(DeviceState **devices, const char *id_prop, From 4394547b68f2713fe5b1acfac9bed06dc890efd7 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 10 Jun 2024 19:13:48 +0200 Subject: [PATCH 07/43] [ot] hw/riscv: ibex_common: add ibex_get_current_cpu helper Signed-off-by: Emmanuel Blot --- hw/riscv/ibex_common.c | 7 +++++++ include/hw/riscv/ibex_common.h | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/hw/riscv/ibex_common.c b/hw/riscv/ibex_common.c index 5a6f90d50f33..2dcbe8d0c0cd 100644 --- a/hw/riscv/ibex_common.c +++ b/hw/riscv/ibex_common.c @@ -712,6 +712,13 @@ uint32_t ibex_get_current_pc(void) return cs && cs->cc->get_pc ? (uint32_t)cs->cc->get_pc(cs) : 0u; } +int ibex_get_current_cpu(void) +{ + CPUState *cs = current_cpu; + + return cs ? cs->cpu_index : -1; +} + /* x0 is replaced with PC */ static const char ibex_ireg_names[32u][4u] = { "pc", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index dea242620210..6dbb926e2969 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -486,6 +486,14 @@ uint32_t ibex_load_kernel(CPUState *cpu); */ uint32_t ibex_get_current_pc(void); +/** + * Helper for device debugging: report the current guest CPU index, if any. + * + * If a HW access is performed from another device but the CPU, reported CPU + * is -1. + */ +int ibex_get_current_cpu(void); + enum { RV_GPR_PC = (1u << 0u), RV_GPR_RA = (1u << 1u), From 9b964038b7f1548690513d9913571f3c6920fce5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 12 Jun 2024 18:30:42 +0200 Subject: [PATCH 08/43] [ot] scripts/opentitan: ot.util.log: fix color management for sub loggers Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 9f696802f15e..92847ad29843 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -95,8 +95,15 @@ 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] + if self._use_ansi: + logname = record.name + while logname: + if logname in self._logger_colors: + scr, ecr = self._logger_colors[logname] + break + if '.' not in logname: + break + logname = logname.rsplit('.', 1)[0] setattr(record, 'scr', scr) setattr(record, 'ecr', ecr) formatter = logging.Formatter(log_fmt, *self._formatter_args) From 8c298debf58a4170837f4285acd3517ceaa3927a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 1 Jul 2024 15:33:31 +0200 Subject: [PATCH 09/43] [ot] scripts/opentitan: pyot.py: rework support for multiple QEMU VCP Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 226 +++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 88 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index ec6fde11621d..f5d1fe852b10 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -25,6 +25,7 @@ from os.path import (abspath, basename, dirname, exists, isabs, isdir, isfile, join as joinpath, normpath, relpath) from re import Match, compile as re_compile, error as re_error, sub as re_sub +from select import POLLIN, POLLERR, POLLHUP, poll as spoll from shutil import rmtree from socket import socket, timeout as LegacyTimeoutError from subprocess import Popen, PIPE, TimeoutExpired @@ -137,13 +138,10 @@ class QEMUWrapper: LOG_LEVELS = {'D': DEBUG, 'I': INFO, 'E': ERROR} """OpenTitan log levels.""" - def __init__(self, tcpdev: tuple[str, int], debug: bool): - # self._mterm: Optional[MiniTerm] = None - self._device = tcpdev + def __init__(self, debug: bool): self._debug = debug self._log = getLogger('pyot') self._qlog = getLogger('pyot.qemu') - self._vcplog = getLogger('pyot.vcp') def run(self, tdef: EasyDict[str, Any]) -> tuple[int, ExecTime, str]: """Execute the specified QEMU command, aborting execution if QEMU does @@ -152,6 +150,7 @@ def run(self, tdef: EasyDict[str, Any]) -> tuple[int, ExecTime, str]: :param tdef: test definition and parameters - command, a list of strings defining the QEMU command to execute with all its options + - vcp_map: how to connect to QEMU virtual communication ports - timeout, the allowed time for the command to execute, specified as a real number - expect_result, the expected outcome of QEMU (exit code). Some @@ -193,11 +192,12 @@ def trig_match(bline): trig_match = None ret = None proc = None - sock = None xstart = None xend = None log = self._log last_error = '' + vcp_map = tdef.vcp_map + vcp_ctxs: dict[int, tuple[str, socket, bytearray]] = {} try: workdir = dirname(tdef.command[0]) log.debug('Executing QEMU as %s', ' '.join(tdef.command)) @@ -206,29 +206,14 @@ def trig_match(bline): 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', tdef.start_delay) - proc.wait(tdef.start_delay) + proc.wait(0.1) except TimeoutExpired: pass else: ret = proc.returncode 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) - try: - # timeout for connecting to VCP - sock.settimeout(1) - sock.connect(self._device) - except OSError as exc: - log.error('Cannot connect to QEMU VCP port: %s', exc) - raise - # timeout for communicating over VCP - sock.settimeout(0.05) 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 # dedicated thread that may block whenever no output is available @@ -241,6 +226,42 @@ def trig_match(bline): args=(proc, log_q, True)).start() Thread(target=self._qemu_logger, name='qemu_err_logger', args=(proc, log_q, False)).start() + poller = spoll() + connect_map = vcp_map.copy() + timeout = now() + tdef.start_delay + # ensure that QEMU starts and give some time for it to set up + # when multiple VCPs are set to 'wait', one VCP can be connected at + # a time, i.e. QEMU does not open all connections at once. + while connect_map: + if now() > timeout: + raise TimeoutError(f'Cannot connect to QEMU VCPs ' + f'{", ".join(connect_map)}') + connected = [] + for vcpid, (host, port) in connect_map.items(): + try: + # timeout for connecting to VCP + sock = socket() + sock.settimeout(1) + sock.connect((host, port)) + connected.append(vcpid) + vcp_name = re_sub(r'^.*[-\.+]', '', vcpid) + vcp_log = getLogger(f'pyot.vcp.{vcp_name}') + vcp_ctxs[sock.fileno()] = [vcpid, sock, bytearray(), + vcp_log] + # timeout for communicating over VCP + sock.settimeout(0.05) + poller.register(sock, POLLIN | POLLERR | POLLHUP) + except ConnectionRefusedError: + log.debug('Cannot connect yet to %s', vcpid) + continue + except OSError as exc: + log.error('Cannot setup QEMU VCP connection %s: %s', + vcpid, exc) + print(format_exc(chain=False), file=stderr) + raise + # removal from dictionary cannot be done while iterating it + for vcpid in connected: + del connect_map[vcpid] xstart = now() if tdef.context: try: @@ -285,52 +306,62 @@ def trig_match(bline): logfn('Abnormal QEMU termination: %d for "%s"', ret, tdef.test_name) break - try: - data = sock.recv(4096) - except (TimeoutError, LegacyTimeoutError): - continue - vcp_buf += data - if not vcp_buf: - continue - lines = vcp_buf.split(b'\n') - 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(tdef.timeout) + now() - log.debug('Resuming for %.0f secs', tdef.timeout) - sync_event.set() - trig_match = None - xmo = xre.search(line) - if xmo: - xend = now() - exit_word = xmo.group(1).decode('utf-8', - errors='ignore') - ret = self._get_exit_code(xmo) - log.info("Exit sequence detected: '%s' -> %d", - exit_word, ret) - if ret == 0: - last_error = '' - break - sline = line.decode('utf-8', errors='ignore').rstrip() - lmo = lre.search(sline) - if lmo: - level = self.LOG_LEVELS.get(lmo.group(1)) - if level == ERROR: - err = re_sub(r'^.*:\d+]', '', sline).lstrip() - # be sure not to preserve comma as this char is - # used as a CSV separator. - last_error = err.strip('"').replace(',', ';') + for vfd, event in poller.poll(0.05): + if event in (POLLERR, POLLHUP): + poller.modify(vfd, 0) + continue + vcpid, vcp, vcp_buf, vcp_log = vcp_ctxs[vfd] + try: + data = vcp.recv(4096) + except (TimeoutError, LegacyTimeoutError): + log.error('Unexpected timeout w/ poll on %s', vcp) + continue + vcp_buf += data + if not vcp_buf: + continue + lines = vcp_buf.split(b'\n') + 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(tdef.timeout) + now() + log.debug('Resuming for %.0f secs', tdef.timeout) + sync_event.set() + trig_match = None + xmo = xre.search(line) + if xmo: + xend = now() + exit_word = xmo.group(1).decode('utf-8', + errors='ignore') + ret = self._get_exit_code(xmo) + log.info("Exit sequence detected: '%s' -> %d", + exit_word, ret) + if ret == 0: + last_error = '' + break + sline = line.decode('utf-8', errors='ignore').rstrip() + lmo = lre.search(sline) + if lmo: + level = self.LOG_LEVELS.get(lmo.group(1)) + if level == ERROR: + err = re_sub(r'^.*:\d+]', '', sline).lstrip() + # be sure not to preserve comma as this char is + # used as a CSV separator. + last_error = err.strip('"').replace(',', ';') + else: + level = DEBUG # fall back when no prefix is found + vcp_log.log(level, sline) else: - level = DEBUG # fall back when no prefix is found - self._vcplog.log(level, sline) - else: - # no match - continue - # match - break + # no match for exit sequence on current VCP + continue + if ret is not None: + # match for exit sequence on current VCP + break + if ret is not None: + # match for exit sequence on last VCP + break if ret is None: log.warning('Execution timed out for "%s"', tdef.test_name) ret = 124 # timeout @@ -341,8 +372,9 @@ def trig_match(bline): finally: if xend is None: xend = now() - if sock: + for _, sock, _, _ in vcp_ctxs.values(): sock.close() + vcp_ctxs.clear() if proc: if xend is None: xend = now() @@ -985,6 +1017,9 @@ class QEMUExecuter: virtual UART port. """ + DEFAULT_SERIAL_PORT = 'serial0' + """Default VCP name.""" + def __init__(self, qfm: QEMUFileManager, config: dict[str, any], args: Namespace): self._log = getLogger('pyot.exec') @@ -993,7 +1028,6 @@ def __init__(self, qfm: QEMUFileManager, config: dict[str, any], self._args = args 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')) @@ -1008,7 +1042,6 @@ def build(self) -> None: """ 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', []) @@ -1029,7 +1062,7 @@ def run(self, debug: bool, allow_no_test: bool) -> int: :return: success or the code of the first encountered error """ - qot = QEMUWrapper(self._vcp, debug) + qot = QEMUWrapper(debug) ret = 0 results = defaultdict(int) result_file = self._argdict.get('result') @@ -1094,6 +1127,8 @@ def run(self, debug: bool, allow_no_test: bool) -> int: # pylint: disable=broad-except except Exception as exc: self._log.critical('%s', str(exc)) + if debug: + print(format_exc(chain=False), file=stderr) tret = 99 xtime = 0.0 err = str(exc) @@ -1258,6 +1293,31 @@ def _build_qemu_fw_args(self, args: Namespace) \ fw_args.extend(('-kernel', exec_path)) return machine, xtype, fw_args + def _build_qemu_vcp_args(self, args: Namespace): + device = args.device + devdesc = device.split(':') + host = devdesc[0] + try: + port = int(devdesc[1]) + if not 0 < port < 65536: + raise ValueError(f'Invalid serial TCP port: {port}') + except IndexError as exc: + raise ValueError(f'TCP port not specified: {device}') from exc + except TypeError as exc: + raise ValueError(f'Invalid TCP serial device: {device}') from exc + mux = f'mux={"on" if args.muxserial else "off"}' + vcps = args.vcp or [self.DEFAULT_SERIAL_PORT] + vcp_args = ['-display', 'none'] + vcp_map = {} + for vix, vcp in enumerate(vcps): + vcp_map[vcp] = (host, port+vix) + vcp_args.extend(('-chardev', + f'socket,id={vcp},host={host},port={port+vix},' + f'{mux},server=on,wait=on')) + if vcp == self.DEFAULT_SERIAL_PORT: + vcp_args.extend(('-serial', 'chardev:serial0')) + return vcp_args, vcp_map + def _build_qemu_command(self, args: Namespace, opts: Optional[list[str]] = None) \ -> EasyDict[str, Any]: @@ -1322,7 +1382,6 @@ def _build_qemu_command(self, args: Namespace, if 'icount' in args: if args.icount is not None: 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 self.DEFAULT_START_DELAY) @@ -1331,24 +1390,12 @@ def _build_qemu_command(self, args: Namespace, from exc start_delay *= args.timeout_factor trigger = getattr(args, 'trigger', '') - device = args.device - devdesc = device.split(':') - try: - port = int(devdesc[1]) - if not 0 < port < 65536: - raise ValueError('Invalid serial TCP port') - tcpdev = (devdesc[0], port) - qemu_args.extend(('-display', 'none')) - qemu_args.extend(('-chardev', - f'socket,id=serial0,host={devdesc[0]},' - f'port={port},{mux},server=on,wait=on')) - qemu_args.extend(('-serial', 'chardev:serial0')) - except TypeError as exc: - raise ValueError('Invalid TCP serial device') from exc + vcp_args, vcp_map = self._build_qemu_vcp_args(args) + qemu_args.extend(vcp_args) qemu_args.extend(args.global_opts or []) if opts: qemu_args.extend((str(o) for o in opts)) - return EasyDict(command=qemu_args, connection=tcpdev, + return EasyDict(command=qemu_args, vcp_map=vcp_map, tmpfiles=temp_files, start_delay=start_delay, trigger=trigger) @@ -1568,8 +1615,10 @@ def main(): qvm.add_argument('-q', '--qemu', help=f'path to qemu application ' f'(default: {rel_qemu_path})') + qvm.add_argument('-P', '--vcp', action='append', + help='serial port devices (default: use serial0)') qvm.add_argument('-p', '--device', - help=f'serial port device name ' + help=f'serial port device name / template name ' f'(default to {DEFAULT_DEVICE})') qvm.add_argument('-t', '--trace', type=FileType('rt', encoding='utf-8'), help='trace event definition file') @@ -1660,6 +1709,7 @@ def main(): log = configure_loggers(args.verbose, 'pyot', LogColor('blue'), 'pyot.vcp', + name_width=16, ms=args.log_time, debug=args.debug, info=args.info, warning=args.warn)[0] @@ -1737,7 +1787,7 @@ def main(): if debug: print(format_exc(chain=False), file=stderr) argparser.error(str(exc)) - ret = qexc.run(args.debug, args.zero) + ret = qexc.run(debug, args.zero) if args.summary: rfmt = ResultFormatter() rfmt.load(args.result) From a3e3d89b94eb15299b6f701808ada888da6583e2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 1 Jul 2024 15:31:56 +0200 Subject: [PATCH 10/43] [ot] scripts/opentitan: pyot.py: colorize each QEMU VCP with a distinct color Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index f5d1fe852b10..848bdad0ab70 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -36,7 +36,7 @@ from traceback import format_exc from typing import Any, Iterator, NamedTuple, Optional -from ot.util.log import Color as LogColor, configure_loggers +from ot.util.log import ColorLogFormatter, configure_loggers from ot.util.misc import EasyDict @@ -232,6 +232,8 @@ def trig_match(bline): # ensure that QEMU starts and give some time for it to set up # when multiple VCPs are set to 'wait', one VCP can be connected at # a time, i.e. QEMU does not open all connections at once. + vcp_lognames = [] + vcplogname = 'pyot.vcp' while connect_map: if now() > timeout: raise TimeoutError(f'Cannot connect to QEMU VCPs ' @@ -245,14 +247,14 @@ def trig_match(bline): sock.connect((host, port)) connected.append(vcpid) vcp_name = re_sub(r'^.*[-\.+]', '', vcpid) - vcp_log = getLogger(f'pyot.vcp.{vcp_name}') + vcp_lognames.append(vcp_name) + vcp_log = getLogger(f'{vcplogname}.{vcp_name}') vcp_ctxs[sock.fileno()] = [vcpid, sock, bytearray(), vcp_log] # timeout for communicating over VCP sock.settimeout(0.05) poller.register(sock, POLLIN | POLLERR | POLLHUP) except ConnectionRefusedError: - log.debug('Cannot connect yet to %s', vcpid) continue except OSError as exc: log.error('Cannot setup QEMU VCP connection %s: %s', @@ -262,6 +264,7 @@ def trig_match(bline): # removal from dictionary cannot be done while iterating it for vcpid in connected: del connect_map[vcpid] + self._colorize_vcp_log(vcplogname, vcp_lognames) xstart = now() if tdef.context: try: @@ -421,6 +424,21 @@ def classify_log(cls, line: str, default: int = ERROR) -> int: return DEBUG return default + def _colorize_vcp_log(self, vcplogname: str, lognames: list[str]) -> None: + vlog = getLogger(vcplogname) + clr_fmt = None + while vlog: + for hdlr in vlog.handlers: + if isinstance(hdlr.formatter, ColorLogFormatter): + clr_fmt = hdlr.formatter + break + vlog = vlog.parent + if not clr_fmt: + return + colors = ('blue', 'yellow', 'red', 'magenta', 'cyan', 'green') + for logname, color in zip(lognames, colors): + clr_fmt.add_logger_colors(f'{vcplogname}.{logname}', color) + 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 @@ -1708,7 +1726,6 @@ def main(): args.result = tmp_result log = configure_loggers(args.verbose, 'pyot', - LogColor('blue'), 'pyot.vcp', name_width=16, ms=args.log_time, debug=args.debug, info=args.info, warning=args.warn)[0] From 2a7aab9d9e2875f2c600929601bc4bca410299cf Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 17 Jun 2024 12:06:53 +0200 Subject: [PATCH 11/43] [ot] scripts/opentitan: ot.util.misc: add a sequence group helper Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/misc.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/opentitan/ot/util/misc.py b/scripts/opentitan/ot/util/misc.py index 7fdb82bda484..05fac1c1d92f 100644 --- a/scripts/opentitan/ot/util/misc.py +++ b/scripts/opentitan/ot/util/misc.py @@ -62,6 +62,17 @@ def __dir__(self) -> Iterable[Any]: yield from sorted(items) +def group(lst, count): + """Group a list into consecutive count-tuples. Incomplete tuples are + discarded. + + `group([0,3,4,10,2,3], 2) => [(0,3), (4,10), (2,3)]` + + From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/303060 + """ + return list(zip(*[lst[i::count] for i in range(count)])) + + def dump_buffer(buffer: Buffer, addr: int = 0, file: Optional[TextIO] = None) \ -> None: """Dump a binary buffer, same format as hexdump -C.""" From 64d88e2b10e2ca873246ef09ff4eb53515db0400 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Mon, 17 Jun 2024 19:51:03 +0200 Subject: [PATCH 12/43] [ot] hw/opentitan: dev_proxy: add device friendly name to trace messages Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 21 +++++++++++++-------- hw/opentitan/trace-events | 12 ++++++------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index 9bd9e9b91fb3..bf3d83e12ea5 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -74,6 +74,8 @@ REG32(MBX_DOE_READ_DATA, 0x014u) /* Mailbox proxy */ /* ------------------------------------------------------------------------ */ +#define DEV_PROXY_DESC_LEN 16u + typedef struct _DevProxyHeader { uint16_t command; uint16_t length; @@ -94,6 +96,7 @@ typedef struct { OtDevProxyCaps caps; /* object capabilities */ const char *prefix; /* prefix name for idenfifying the device */ GHashTable *iirq_ht; /* intercepted IRQs, may be NULL */ + char desc[DEV_PROXY_DESC_LEN]; /* user friendly name, for debug purposes */ } OtDevProxyItem; typedef struct { @@ -308,7 +311,7 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) uint32_t header; uint32_t base; uint32_t count; - char desc[16u]; + char desc[DEV_PROXY_DESC_LEN]; }; g_assert(sizeof(struct entry) == 7u * sizeof(uint32_t)); @@ -317,7 +320,7 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) unsigned mrcount = 0; char desc[32u]; for (unsigned ix = 0; ix < s->dev_count; ix++) { - const OtDevProxyItem *item = &s->items[ix]; + OtDevProxyItem *item = &s->items[ix]; const OtDevProxyCaps *caps = &item->caps; struct entry *entry = &entries[count]; memset(entry, 0, sizeof(*entry)); @@ -372,6 +375,8 @@ static void ot_dev_proxy_enumerate_devices(OtDevProxyState *s) object_get_typename(item->obj), desc); } memcpy(entry->desc, desc, sizeof(entry->desc)); + strncpy(item->desc, desc, sizeof(entry->desc)); + item->desc[sizeof(entry->desc) - 1] = '\0'; entry->header = ix << 16u; entry->base = (uint32_t)caps->mr->addr; entry->count = caps->reg_count; @@ -529,7 +534,7 @@ static void ot_dev_proxy_read_reg(OtDevProxyState *s) return; } - trace_ot_dev_proxy_read_reg(item->prefix, devix, reg); + trace_ot_dev_proxy_read_reg(item->desc, reg); const MemoryRegionOps *ops = mr->ops; if (role != PROXY_DISABLED_ROLE ? !ops->read_with_attrs : !ops->read) { @@ -588,7 +593,7 @@ static void ot_dev_proxy_write_reg(OtDevProxyState *s) return; } - trace_ot_dev_proxy_write_reg(item->prefix, devix, reg, value); + trace_ot_dev_proxy_write_reg(item->desc, reg, value); const MemoryRegionOps *ops = mr->ops; if (role != PROXY_DISABLED_ROLE ? !ops->write_with_attrs : !ops->write) { @@ -672,7 +677,7 @@ static void ot_dev_proxy_read_buffer(OtDevProxyState *s, bool mbx_mode) return; } - trace_ot_dev_proxy_read_buffer(item->prefix, devix, mbx_mode, reg, count); + trace_ot_dev_proxy_read_buffer(item->desc, mbx_mode, reg, count); const MemoryRegionOps *ops = mr->ops; if (role != PROXY_DISABLED_ROLE ? !ops->read_with_attrs : !ops->read) { @@ -786,7 +791,7 @@ static void ot_dev_proxy_write_buffer(OtDevProxyState *s, bool mbx_mode) return; } - trace_ot_dev_proxy_write_buffer(item->prefix, devix, mbx_mode, reg, count); + trace_ot_dev_proxy_write_buffer(item->desc, mbx_mode, reg, count); const MemoryRegionOps *ops = mr->ops; if (role != PROXY_DISABLED_ROLE ? !ops->write_with_attrs : !ops->write) { @@ -900,7 +905,7 @@ static void ot_dev_proxy_read_memory(OtDevProxyState *s) } } - trace_ot_dev_proxy_read_memory(item->prefix, devix, offset, count); + trace_ot_dev_proxy_read_memory(item->desc, offset, count); uint32_t *buf = g_new0(uint32_t, count); if (count) { @@ -956,7 +961,7 @@ static void ot_dev_proxy_write_memory(OtDevProxyState *s) } } - trace_ot_dev_proxy_write_memory(item->prefix, devix, offset, count); + trace_ot_dev_proxy_write_memory(item->desc, offset, count); if (object_dynamic_cast(obj, TYPE_OT_SRAM_CTRL)) { MemoryRegion *mr = caps->mr; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 70684e55e069..4bb083d4e66d 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -90,15 +90,15 @@ 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, 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" +ot_dev_proxy_read_buffer(const char *desc, bool mbx, unsigned offset, unsigned count) "%s mbx:%u 0x%02x %u" +ot_dev_proxy_read_memory(const char *desc, unsigned offset, unsigned count) "%s 0x%08x 0x%x" +ot_dev_proxy_read_reg(const char *desc, unsigned offset) "%s 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" -ot_dev_proxy_write_reg(const char *prefix, unsigned devix, unsigned offset, unsigned value) "%s #%u 0x%08x 0x%08x" +ot_dev_proxy_write_buffer(const char *desc, bool mbx, unsigned offset, unsigned count) "%s mbx:%u 0x%02x %u" +ot_dev_proxy_write_memory(const char *desc, unsigned offset, unsigned count) "%s 0x%08x 0x%x" +ot_dev_proxy_write_reg(const char *desc, unsigned offset, unsigned value) "%s 0x%08x 0x%08x" # ot_dm_tl.c From 885bd702186f0edd751ac069590b16ae872bfe1c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Jun 2024 10:21:58 +0200 Subject: [PATCH 13/43] [ot] scripts/opentitan: pyot.py: discard QEMU sync messages There is no way to disable them from the VM itself, and those are mostly useless messages Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 848bdad0ab70..5bbfa97d0819 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -20,7 +20,7 @@ except ImportError: # fallback on legacy JSON syntax otherwise from json import load as jload -from logging import CRITICAL, DEBUG, INFO, ERROR, WARNING, getLogger +from logging import CRITICAL, DEBUG, INFO, ERROR, NOTSET, WARNING, getLogger 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) @@ -251,8 +251,8 @@ def trig_match(bline): vcp_log = getLogger(f'{vcplogname}.{vcp_name}') vcp_ctxs[sock.fileno()] = [vcpid, sock, bytearray(), vcp_log] - # timeout for communicating over VCP - sock.settimeout(0.05) + # remove timeout for VCP comm, as poll is used + sock.settimeout(None) poller.register(sock, POLLIN | POLLERR | POLLHUP) except ConnectionRefusedError: continue @@ -278,12 +278,13 @@ def trig_match(bline): ret = 126 last_error = str(exc) raise + qemu_exec = f'{basename(tdef.command[0])}: ' abstimeout = float(tdef.timeout) + now() while now() < abstimeout: while log_q: err, qline = log_q.popleft() if err: - level = self.classify_log(qline) + level = self.classify_log(qline, qemux=qemu_exec) if level == INFO and \ qline.find('QEMU waiting for connection') >= 0: level = DEBUG @@ -309,7 +310,7 @@ def trig_match(bline): logfn('Abnormal QEMU termination: %d for "%s"', ret, tdef.test_name) break - for vfd, event in poller.poll(0.05): + for vfd, event in poller.poll(0.01): if event in (POLLERR, POLLHUP): poller.modify(vfd, 0) continue @@ -327,10 +328,10 @@ def trig_match(bline): 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(tdef.timeout) + now() - log.debug('Resuming for %.0f secs', tdef.timeout) + log.info('Trigger pattern detected, resuming for ' + '%.0f secs', tdef.timeout) sync_event.set() trig_match = None xmo = xre.search(line) @@ -403,13 +404,17 @@ def trig_match(bline): return abs(ret) or 0, xtime, last_error @classmethod - def classify_log(cls, line: str, default: int = ERROR) -> int: + def classify_log(cls, line: str, default: int = ERROR, + qemux: Optional[str] = None) -> int: """Classify log level of a line depending on its content. :param line: line to classify :param default: defaut log level in no classification is found :return: the logger log level to use """ + if qemux and line.startswith(qemux): + # discard QEMU internal messages that cannot be disable from the VM + return NOTSET if (line.find('info: ') >= 0 or line.startswith('INFO ') or line.find(' INFO ') >= 0): # noqa @@ -821,7 +826,7 @@ def _run(self): self._log.info('Waiting for sync') while self._resume: if self._sync.wait(0.1): - self._log.info('Sync triggered') + self._log.debug('Synchronized') break self._sync.clear() # pylint: disable=consider-using-with @@ -830,11 +835,13 @@ def _run(self): errors='ignore', text=True) Thread(target=self._logger, args=(proc, True)).start() Thread(target=self._logger, args=(proc, False)).start() + qemu_exec = f'{basename(self._cmd[0])}: ' while self._resume: while self._log_q: err, qline = self._log_q.popleft() if err: - self._log.log(QEMUWrapper.classify_log(qline), qline) + loglevel = QEMUWrapper.classify_log(qline, qemux=qemu_exec) + self._log.log(loglevel, qline) else: self._log.debug(qline) if proc.poll() is not None: From f8ff057ea400381964154c85e455b2c9af884645 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Jun 2024 14:04:26 +0200 Subject: [PATCH 14/43] [ot] scripts/opentitan: pyot.py: rework log message classification Log message classification can now be configured with regular expressions. Signed-off-by: Emmanuel Blot --- scripts/opentitan/pyot.py | 145 ++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 46 deletions(-) diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 5bbfa97d0819..4d9f7cc1d65c 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -20,11 +20,9 @@ except ImportError: # fallback on legacy JSON syntax otherwise from json import load as jload -from logging import CRITICAL, DEBUG, INFO, ERROR, NOTSET, WARNING, getLogger 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, error as re_error, sub as re_sub from select import POLLIN, POLLERR, POLLHUP, poll as spoll from shutil import rmtree from socket import socket, timeout as LegacyTimeoutError @@ -36,6 +34,9 @@ from traceback import format_exc from typing import Any, Iterator, NamedTuple, Optional +import logging +import re + from ot.util.log import ColorLogFormatter, configure_loggers from ot.util.misc import EasyDict @@ -45,6 +46,8 @@ DEFAULT_TIMEOUT = 60 # seconds DEFAULT_TIMEOUT_FACTOR = 1.0 +getLogger = logging.getLogger + class ExecTime(float): """Float with hardcoded formatter. @@ -106,6 +109,58 @@ def _show_row(self, widths: list[int], cols: list[str]) -> None: print(f'|{line}|') +class LogMessageClassifier: + """Log level classifier for log messages. + + :param classifiers: a map of loglevel, list of RE-compatible strings + to match messages + :param qemux: the QEMU executable name, to filter out useless messages + """ + + def __init__(self, classifiers: Optional[dict[str, list[str]]] = None, + qemux: Optional[str] = None): + self._qemux = qemux + if classifiers is None: + classifiers = {} + self._regexes: dict[int, re.Pattern] = {} + for kl in 'error warning info debug'.split(): + ukl = kl.upper() + cstrs = classifiers.get(kl, []) + if not isinstance(cstrs, list): + raise ValueError(f'Invalid log classifiers for {kl}') + regexes = [f'{kl}: ', f'^{ukl} ', f' {ukl} '] + for cstr in cstrs: + try: + # only sanity-check pattern, do not use result + re.compile(cstr) + except re.error as exc: + raise ValueError(f"Invalid log classifier '{cstr}' for " + f"{kl}: {exc}") from exc + regexes.append(cstr) + if regexes: + lvl = getattr(logging, ukl) + self._regexes[lvl] = re.compile(f"({'|'.join(regexes)})") + else: + lvl = getattr(logging, 'NOTSET') + # never match RE + self._regexes[lvl] = re.compile(r'\A(?!x)x') + + def classify(self, line: str, default: int = logging.ERROR) -> int: + """Classify log level of a line depending on its content. + + :param line: line to classify + :param default: defaut log level in no classification is found + :return: the logger log level to use + """ + if self._qemux and line.startswith(self._qemux): + # discard QEMU internal messages that cannot be disable from the VM + return logging.NOTSET + for lvl, pattern in self._regexes.items(): + if pattern.search(line): + return lvl + return default + + class QEMUWrapper: """A small engine to run tests with QEMU. @@ -125,7 +180,7 @@ class QEMUWrapper: such as SIGABRT. """ - ANSI_CRE = re_compile(rb'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') + ANSI_CRE = re.compile(rb'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') """ANSI escape sequences.""" GUEST_ERROR_OFFSET = 40 @@ -135,10 +190,8 @@ class QEMUWrapper: NO_MATCH_RETURN_CODE = 100 """Return code when no matching string is found in guest output.""" - LOG_LEVELS = {'D': DEBUG, 'I': INFO, 'E': ERROR} - """OpenTitan log levels.""" - - def __init__(self, debug: bool): + def __init__(self, log_classifiers: dict[str, list[str]], debug: bool): + self._log_classifiers = log_classifiers self._debug = debug self._log = getLogger('pyot') self._qlog = getLogger('pyot.qemu') @@ -169,15 +222,13 @@ def run(self, tdef: EasyDict[str, Any]) -> tuple[int, ExecTime, str]: # stdout and stderr belongs to QEMU VM # OT's UART0 is redirected to a TCP stream that can be accessed through # self._device. The VM pauses till the TCP socket is connected - xre = re_compile(self.EXIT_ON) - otre = r'^([' + ''.join(self.LOG_LEVELS.keys()) + r'])\d{5}\s' - lre = re_compile(otre) + xre = re.compile(self.EXIT_ON) if tdef.trigger: sync_event = Event() if tdef.trigger.startswith("r'") and tdef.trigger.endswith("'"): try: - tmo = re_compile(tdef.trigger[2:-1].encode()) - except re_error as exc: + 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): @@ -246,7 +297,7 @@ def trig_match(bline): sock.settimeout(1) sock.connect((host, port)) connected.append(vcpid) - vcp_name = re_sub(r'^.*[-\.+]', '', vcpid) + vcp_name = re.sub(r'^.*[-\.+]', '', vcpid) vcp_lognames.append(vcp_name) vcp_log = getLogger(f'{vcplogname}.{vcp_name}') vcp_ctxs[sock.fileno()] = [vcpid, sock, bytearray(), @@ -279,17 +330,21 @@ def trig_match(bline): last_error = str(exc) raise qemu_exec = f'{basename(tdef.command[0])}: ' + classifier = LogMessageClassifier(classifiers=self._log_classifiers, + qemux=qemu_exec) abstimeout = float(tdef.timeout) + now() + qemu_default_log = logging.ERROR + vcp_default_log = logging.DEBUG while now() < abstimeout: while log_q: err, qline = log_q.popleft() if err: - level = self.classify_log(qline, qemux=qemu_exec) - if level == INFO and \ + level = classifier.classify(qline, qemu_default_log) + if level == logging.INFO and \ qline.find('QEMU waiting for connection') >= 0: - level = DEBUG + level = logging.DEBUG else: - level = INFO + level = logging.INFO self._qlog.log(level, qline) if tdef.context: wret = tdef.context.check_error() @@ -346,16 +401,12 @@ def trig_match(bline): last_error = '' break sline = line.decode('utf-8', errors='ignore').rstrip() - lmo = lre.search(sline) - if lmo: - level = self.LOG_LEVELS.get(lmo.group(1)) - if level == ERROR: - err = re_sub(r'^.*:\d+]', '', sline).lstrip() - # be sure not to preserve comma as this char is - # used as a CSV separator. - last_error = err.strip('"').replace(',', ';') - else: - level = DEBUG # fall back when no prefix is found + level = classifier.classify(sline, vcp_default_log) + if level == logging.ERROR: + err = re.sub(r'^.*:\d+]', '', sline).lstrip() + # be sure not to preserve comma as this char is + # used as a CSV separator. + last_error = err.strip('"').replace(',', ';') vcp_log.log(level, sline) else: # no match for exit sequence on current VCP @@ -404,7 +455,7 @@ def trig_match(bline): return abs(ret) or 0, xtime, last_error @classmethod - def classify_log(cls, line: str, default: int = ERROR, + def classify_log(cls, line: str, default: int = logging.ERROR, qemux: Optional[str] = None) -> int: """Classify log level of a line depending on its content. @@ -414,19 +465,19 @@ def classify_log(cls, line: str, default: int = ERROR, """ if qemux and line.startswith(qemux): # discard QEMU internal messages that cannot be disable from the VM - return NOTSET + return logging.NOTSET if (line.find('info: ') >= 0 or line.startswith('INFO ') or line.find(' INFO ') >= 0): # noqa - return INFO + return logging.INFO if (line.find('warning: ') >= 0 or line.startswith('WARNING ') or line.find(' WARNING ') >= 0): # noqa - return WARNING + return logging.WARNING if (line.find('debug: ') >= 0 or line.startswith('DEBUG ') or line.find(' DEBUG ') >= 0): # noqa - return DEBUG + return logging.DEBUG return default def _colorize_vcp_log(self, vcplogname: str, lognames: list[str]) -> None: @@ -452,7 +503,7 @@ def _qemu_logger(self, proc: Popen, queue: deque, err: bool): if line: queue.append((err, line)) - def _get_exit_code(self, xmo: Match) -> int: + def _get_exit_code(self, xmo: re.Match) -> int: groups = xmo.groups() if not groups: self._log.debug('No matching group, using defaut code') @@ -465,7 +516,7 @@ def _get_exit_code(self, xmo: Match) -> int: pass # try to find in the regular expression whether the match is one of # the alternative in the first group - alts = re_sub(rb'^.*\((.*?)\).*$', r'\1', xmo.re.pattern).split(b'|') + alts = re.sub(rb'^.*\((.*?)\).*$', r'\1', xmo.re.pattern).split(b'|') try: pos = alts.index(match) if pos: @@ -535,13 +586,13 @@ def interpolate(self, value: Any) -> str: :param value: input value :return: interpolated value as a string """ - def replace(smo: Match) -> str: + def replace(smo: re.Match) -> str: name = smo.group(1) val = self._env[name] if name in self._env \ else environ.get(name, '') return val svalue = str(value) - nvalue = re_sub(r'\$\{(\w+)\}', replace, svalue) + nvalue = re.sub(r'\$\{(\w+)\}', replace, svalue) if nvalue != svalue: self._log.debug('Interpolate %s with %s', value, nvalue) return nvalue @@ -553,14 +604,14 @@ def define(self, aliases: dict[str, Any]) -> None: :param aliases: an alias JSON (sub-)tree """ - def replace(smo: Match) -> str: + def replace(smo: re.Match) -> str: name = smo.group(1) val = self._env[name] if name in self._env \ else environ.get(name, '') return val for name in aliases: value = str(aliases[name]) - value = re_sub(r'\$\{(\w+)\}', replace, value) + value = re.sub(r'\$\{(\w+)\}', replace, value) if exists(value): value = normpath(value) aliases[name] = value @@ -595,7 +646,7 @@ def interpolate_dirs(self, value: str, default: str) -> str: none :return: the interpolated string """ - def replace(smo: Match) -> str: + def replace(smo: re.Match) -> str: name = smo.group(1) if name == '': name = default @@ -607,7 +658,7 @@ def replace(smo: Match) -> str: if not tmp_dir.endswith(sep): tmp_dir = f'{tmp_dir}{sep}' return tmp_dir - nvalue = re_sub(r'\@\{(\w*)\}/', replace, value) + nvalue = re.sub(r'\@\{(\w*)\}/', replace, value) if nvalue != value: self._log.debug('Interpolate %s with %s', value, nvalue) return nvalue @@ -728,7 +779,7 @@ def _configure_logger(self, tool) -> None: log = getLogger('pyot') flog = tool.logger # sub-tool get one logging level down to reduce log messages - floglevel = min(CRITICAL, log.getEffectiveLevel() + 10) + floglevel = min(logging.CRITICAL, log.getEffectiveLevel() + 10) flog.setLevel(floglevel) for hdlr in log.handlers: flog.addHandler(hdlr) @@ -836,11 +887,12 @@ def _run(self): Thread(target=self._logger, args=(proc, True)).start() Thread(target=self._logger, args=(proc, False)).start() qemu_exec = f'{basename(self._cmd[0])}: ' + classifier = LogMessageClassifier(qemux=qemu_exec) while self._resume: while self._log_q: err, qline = self._log_q.popleft() if err: - loglevel = QEMUWrapper.classify_log(qline, qemux=qemu_exec) + loglevel = classifier.classify(qline) self._log.log(loglevel, qline) else: self._log.debug(qline) @@ -1087,7 +1139,8 @@ def run(self, debug: bool, allow_no_test: bool) -> int: :return: success or the code of the first encountered error """ - qot = QEMUWrapper(debug) + log_classifiers = self._config.get('logclass', {}) + qot = QEMUWrapper(log_classifiers, debug) ret = 0 results = defaultdict(int) result_file = self._argdict.get('result') @@ -1498,7 +1551,7 @@ def _enumerate_from(self, config_entry: str) -> Iterator[str]: incf_dir = dirname(incf) with open(incf, 'rt', encoding='utf-8') as ifp: for testfile in ifp: - testfile = re_sub('#.*$', '', testfile).strip() + testfile = re.sub('#.*$', '', testfile).strip() if not testfile: continue testfile = self._qfm.interpolate(testfile) @@ -1596,8 +1649,8 @@ def _build_test_context(self, test_name: str) -> QEMUContext: if not isinstance(cmd, str): raise ValueError(f'Invalid command #{pos} in ' f'"{ctx_name}" for test {test_name}') - cmd = re_sub(r'[\n\r]', ' ', cmd.strip()) - cmd = re_sub(r'\s{2,}', ' ', cmd) + cmd = re.sub(r'[\n\r]', ' ', cmd.strip()) + cmd = re.sub(r'\s{2,}', ' ', cmd) cmd = self._qfm.interpolate(cmd) cmd = self._qfm.interpolate_dirs(cmd, test_name) context[ctx_name].append(cmd) From 396d983f345331b02b1f6f416feba870903bca45 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Jun 2024 15:20:05 +0200 Subject: [PATCH 15/43] [ot] hw/riscv: ibex_common: use a bitfield rather special address for markers Signed-off-by: Emmanuel Blot --- include/hw/riscv/ibex_common.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/include/hw/riscv/ibex_common.h b/include/hw/riscv/ibex_common.h index 6dbb926e2969..d1b3b9b4f445 100644 --- a/include/hw/riscv/ibex_common.h +++ b/include/hw/riscv/ibex_common.h @@ -155,11 +155,20 @@ typedef struct { }; } IbexDevicePropDef; +typedef enum { + IBEX_MEM_MAP_ENTRY_FLAG_LAST, + IBEX_MEM_MAP_ENTRY_FLAG_SKIP, + IBEX_MEM_MAP_ENTRY_FLAG_COUNT +} IbexMemMapEntryFlags; + typedef struct IbexMemMapEntry { hwaddr base; int8_t priority; + uint8_t flags; /* bitfield of IbexMemMapEntryFlags */ } IbexMemMapEntry; +#define IBEX_MEM_MAP_ENTRY_FLAG(_f_) (1u << (IBEX_MEM_MAP_ENTRY_FLAG_##_f_)) + /* Device definition */ struct IbexDeviceDef { /** Registered type of the device */ @@ -231,10 +240,12 @@ typedef struct { (((_par_) << IBEX_DEVLINK_RMT_SHIFT) | ((_ix_) & IBEX_DEVLINK_IDX_MASK)) /* MemMapEntry that should be ignored (i.e. skipped, not mapped) */ -#define IBEX_MEMMAP_LAST ((hwaddr)0ull) -#define IBEX_MEMMAP_SKIP { .base = HWADDR_MAX } -#define IBEX_MEMMAP_IS_LAST(_mmap_) ((_mmap_)->base == IBEX_MEMMAP_LAST) -#define IBEX_MEMMAP_IGNORE(_mmap_) ((_mmap_)->base == HWADDR_MAX) +#define IBEX_MEMMAP_LAST { .flags = IBEX_MEM_MAP_ENTRY_FLAG(LAST) } +#define IBEX_MEMMAP_SKIP { .flags = IBEX_MEM_MAP_ENTRY_FLAG(SKIP) } +#define IBEX_MEMMAP_IS_LAST(_mmap_) \ + ((bool)((_mmap_)->flags & IBEX_MEM_MAP_ENTRY_FLAG(LAST))) +#define IBEX_MEMMAP_IGNORE(_mmap_) \ + ((bool)((_mmap_)->flags & IBEX_MEM_MAP_ENTRY_FLAG(SKIP))) /** * Create memory map entries, each arg is MemMapEntry definition @@ -242,10 +253,7 @@ typedef struct { #define MEMMAPENTRIES(...) \ (const IbexMemMapEntry[]) \ { \ - __VA_ARGS__, \ - { \ - .base = IBEX_MEMMAP_LAST \ - } \ + __VA_ARGS__, IBEX_MEMMAP_LAST \ } /** From 8a064c60a278cbbdd2f0ac83cf725441b563af52 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Jun 2024 17:16:20 +0200 Subject: [PATCH 16/43] [ot] hw/opentitan: ot_dev_proxy: enable interception of disconnected IRQs IRQs that are not connected to a sink can now be intercepted. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_dev_proxy.c | 10 ++++++---- hw/opentitan/trace-events | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index bf3d83e12ea5..d5cd00445fa3 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -104,6 +104,7 @@ typedef struct { unsigned dev_num; /* device number (in device array) */ uint16_t irq_num; /* IRQ number (in proxied device) */ uint8_t grp_num; /* IRQ group (in proxied device) */ + bool assigned; /* Proxy IRQ slot in use */ } OtDevProxyIrq; typedef struct { @@ -1005,7 +1006,7 @@ ot_dev_proxy_route_interrupt(OtDevProxyState *s, OtDevProxyItem *item, unsigned six; for (six = 0; six < PROXY_IRQ_INTERCEPT_COUNT; six++) { - if (!s->proxy_irq_map[six].irq_orig) { + if (!s->proxy_irq_map[six].assigned) { proxy_irq = &s->proxy_irq_map[six]; break; } @@ -1018,6 +1019,7 @@ ot_dev_proxy_route_interrupt(OtDevProxyState *s, OtDevProxyItem *item, qemu_irq icpt_irq; icpt_irq = qdev_get_gpio_in_named(DEVICE(s), PROXY_IRQ_INTERCEPT_NAME, (int)six); + proxy_irq->assigned = true; 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); @@ -1142,7 +1144,7 @@ static void ot_dev_proxy_intercept_interrupts(OtDevProxyState *s, bool enable) */ unsigned free_slot = 0; for (unsigned ix = 0; ix < PROXY_IRQ_INTERCEPT_COUNT; ix++) { - if (!s->proxy_irq_map[ix].irq_orig) { + if (!s->proxy_irq_map[ix].assigned) { free_slot += 1; } } @@ -1393,8 +1395,8 @@ static void ot_dev_proxy_intercepted_irq(void *opaque, int irq, int level) 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); + if (!proxy_irq->assigned) { + trace_ot_dev_proxy_unassigned_irq(irq); return; } g_assert(proxy_irq->dev_num < s->dev_count); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 4bb083d4e66d..bae2829f49aa 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -96,6 +96,7 @@ ot_dev_proxy_read_reg(const char *desc, unsigned offset) "%s 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_unassigned_irq(int irq) "%d non-assigned intercepted IRQ signaled" ot_dev_proxy_write_buffer(const char *desc, bool mbx, unsigned offset, unsigned count) "%s mbx:%u 0x%02x %u" ot_dev_proxy_write_memory(const char *desc, unsigned offset, unsigned count) "%s 0x%08x 0x%x" ot_dev_proxy_write_reg(const char *desc, unsigned offset, unsigned value) "%s 0x%08x 0x%08x" From 80ebd38a95a788448419cef4afbb60b575158de2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 18 Jun 2024 18:23:14 +0200 Subject: [PATCH 17/43] [ot] scripts/opentitan: pyot.py: add shortcuts for QEMU log messages Also add a new verbosity option for increasing QEMU virtual comm port verbosity. Signed-off-by: Emmanuel Blot --- docs/opentitan/pyot.md | 39 +++++++++++++++++---------- scripts/opentitan/pyot.py | 57 ++++++++++++++++++++++++++++----------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/docs/opentitan/pyot.md b/docs/opentitan/pyot.md index cbffa6e8df39..a801ed2a4fdd 100644 --- a/docs/opentitan/pyot.md +++ b/docs/opentitan/pyot.md @@ -5,12 +5,13 @@ ## Usage ````text -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] - [-k SECONDS] [-z] [-R] [-T FACTOR] [-Z] [-v] [-d] [--log-time] - [--debug LOGGER] [--info LOGGER] [--warn LOGGER] +usage: pyot.py [-h] [-D DELAY] [-i ICOUNT] [-L LOG_FILE] [-M VARIANT] [-N LOG] + [-m MACHINE] [-Q OPTS] [-q QEMU] [-P VCP] [-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] [-V] [-d] [--log-time] [--debug LOGGER] + [--info LOGGER] [--warn LOGGER] OpenTitan QEMU unit test sequencer. @@ -25,14 +26,18 @@ Virtual machine: 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 + -M VARIANT, --variant VARIANT + machine variant (machine specific) + -N 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) + -P VCP, --vcp VCP serial port devices (default: use serial0) -p DEVICE, --device DEVICE - serial port device name (default to localhost:8000) + serial port device name / template name (default to + localhost:8000) -t TRACE, --trace TRACE trace event definition file -S FIRST_SOC, --first-soc FIRST_SOC @@ -73,6 +78,7 @@ Execution: Extras: -v, --verbose increase verbosity + -V, --vcp-verbose increase verbosity of QEMU virtual comm ports -d enable debug mode --log-time show local time in log messages --debug LOGGER assign debug level to logger(s) @@ -95,12 +101,16 @@ This tool may be used in two ways, which can be combined: 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, - * `unimp` for uimplemented guest features, - * `int` for guest interrupts and exceptions, - * `guest_errors` for unexpected guest behavior, - * `exec` for guest execution stream (caution: highly verbose). +* `-N` / `--log` specify which log message types should be logged; most useful types are: + * `in_asm`/`A` for guest instruction disassembly, + * `unimp`/`U` for uimplemented guest features, + * `int`/`I` for guest interrupts and exceptions, + * `guest_errors`/`G` for unexpected guest behavior, + * `exec`/`E` for guest execution stream (caution: highly verbose). + These definitions may be abbreviated using their upper case, single char variants, _e.g._ + `-N GIU` would enable _guest_errors_, _interruptions_ and _unimplemented features_ logs. +* `-M` / `--variant` specify a variant of the selected machine. The accepted values depend on the + machine, see `-m` option. * `-m` / `--machine` specify the kind of virtual machine to run. * `-q` / `--qemu` specify an alternative path to the QEMU application. * `-Q` / `--opts` add a single QEMU option forwarded verbatim to QEMU (no check is performed) @@ -165,6 +175,7 @@ This tool may be used in two ways, which can be combined: ### Extras +* `-V` / `--vcp-verbose` can be repeated to increase verbosity of the QEMU virtual comm ports * `-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 diff --git a/scripts/opentitan/pyot.py b/scripts/opentitan/pyot.py index 4d9f7cc1d65c..4ee111d51dd1 100755 --- a/scripts/opentitan/pyot.py +++ b/scripts/opentitan/pyot.py @@ -123,22 +123,22 @@ def __init__(self, classifiers: Optional[dict[str, list[str]]] = None, if classifiers is None: classifiers = {} self._regexes: dict[int, re.Pattern] = {} - for kl in 'error warning info debug'.split(): - ukl = kl.upper() - cstrs = classifiers.get(kl, []) + for klv in 'error warning info debug'.split(): + uklv = klv.upper() + cstrs = classifiers.get(klv, []) if not isinstance(cstrs, list): - raise ValueError(f'Invalid log classifiers for {kl}') - regexes = [f'{kl}: ', f'^{ukl} ', f' {ukl} '] + raise ValueError(f'Invalid log classifiers for {klv}') + regexes = [f'{klv}: ', f'^{uklv} ', f' {uklv} '] for cstr in cstrs: try: # only sanity-check pattern, do not use result re.compile(cstr) except re.error as exc: raise ValueError(f"Invalid log classifier '{cstr}' for " - f"{kl}: {exc}") from exc + f"{klv}: {exc}") from exc regexes.append(cstr) if regexes: - lvl = getattr(logging, ukl) + lvl = getattr(logging, uklv) self._regexes[lvl] = re.compile(f"({'|'.join(regexes)})") else: lvl = getattr(logging, 'NOTSET') @@ -1097,6 +1097,15 @@ class QEMUExecuter: DEFAULT_SERIAL_PORT = 'serial0' """Default VCP name.""" + LOG_SHORTCUTS = { + 'A': 'in_asm', + 'E': 'exec', + 'G': 'guest_errors', + 'I': 'int', + 'U': 'unimp', + } + """Shortcut names for QEMU log sources.""" + def __init__(self, qfm: QEMUFileManager, config: dict[str, any], args: Namespace): self._log = getLogger('pyot.exec') @@ -1371,7 +1380,24 @@ def _build_qemu_fw_args(self, args: Namespace) \ fw_args.extend(('-kernel', exec_path)) return machine, xtype, fw_args - def _build_qemu_vcp_args(self, args: Namespace): + def _build_qemu_log_sources(self, args: Namespace) -> list[str]: + if not args.log: + return [] + log_args = [] + for arg in args.log: + if arg.lower() == arg: + log_args.append(arg) + continue + for upch in arg: + try: + logname = self.LOG_SHORTCUTS[upch] + except KeyError as exc: + raise ValueError(f"Unknown log name '{upch}'") from exc + log_args.append(logname) + return ['-d', ','.join(log_args)] + + def _build_qemu_vcp_args(self, args: Namespace) -> \ + tuple[list[str], dict[str, tuple[str, int]]]: device = args.device devdesc = device.split(':') host = devdesc[0] @@ -1452,9 +1478,7 @@ def _build_qemu_command(self, args: Namespace, args.trace.close() qemu_args.extend(('-trace', f'events={self.abspath(args.trace.name)}')) - if args.log: - qemu_args.append('-d') - qemu_args.append(','.join(args.log)) + qemu_args.extend(self._build_qemu_log_sources(args)) if args.singlestep: qemu_args.append('-singlestep') if 'icount' in args: @@ -1684,7 +1708,9 @@ def main(): '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', + qvm.add_argument('-M', '--variant', + help='machine variant (machine specific)') + qvm.add_argument('-N', '--log', action='append', help='log message types') qvm.add_argument('-m', '--machine', help=f'virtual machine (default to {DEFAULT_MACHINE})') @@ -1709,8 +1735,6 @@ def main(): const=True, help='enable multiple virtual UARTs to be muxed into ' 'same host output channel') - qvm.add_argument('-V', '--variant', - help='machine variant (machine specific)') files = argparser.add_argument_group(title='Files') files.add_argument('-b', '--boot', metavar='file', help='bootloader 0 file') @@ -1757,6 +1781,8 @@ def main(): extra = argparser.add_argument_group(title='Extras') extra.add_argument('-v', '--verbose', action='count', help='increase verbosity') + extra.add_argument('-V', '--vcp-verbose', action='count', + help='increase verbosity of QEMU virtual comm ports') extra.add_argument('-d', action='store_true', help='enable debug mode') extra.add_argument('--log-time', action='store_true', @@ -1786,7 +1812,8 @@ def main(): args.result = tmp_result log = configure_loggers(args.verbose, 'pyot', - name_width=16, + args.vcp_verbose or 0, + 'pyot.vcp', name_width=16, ms=args.log_time, debug=args.debug, info=args.info, warning=args.warn)[0] From e871e7e7e300404c4b0b25bef4bb9769cfe25f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Mon, 1 Jul 2024 19:17:15 +0200 Subject: [PATCH 18/43] [ot] hw/opentitan: ot_dma: use int128_getlo to convert MRS size to 64-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't use MemoryRegionSection.size directly, use int128_getlo to convert it to a 64-bit value. Signed-off-by: Loïc Lefort --- hw/opentitan/ot_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/opentitan/ot_dma.c b/hw/opentitan/ot_dma.c index 57f5f0551f44..7469e8806101 100644 --- a/hw/opentitan/ot_dma.c +++ b/hw/opentitan/ot_dma.c @@ -582,7 +582,7 @@ ot_dma_check_device(OtDMAState *s, bool d_or_s, OtDMAAddrSpace *asix, MemoryRegionSection mrs = { 0 }; mrs = memory_region_find(as->root, start, size); - if (!mrs.mr || !mrs.size) { + if (!mrs.mr || !int128_getlo(mrs.size)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Invalid %s address as:%s " "addr: 0x%" HWADDR_PRIx " size: 0x%" HWADDR_PRIx "\n", @@ -592,7 +592,7 @@ ot_dma_check_device(OtDMAState *s, bool d_or_s, OtDMAAddrSpace *asix, return NULL; } - if (mrs.offset_within_region + mrs.size < size) { + if (mrs.offset_within_region + int128_getlo(mrs.size) < size) { qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Invalid size\n", __func__, s->ot_id); ot_dma_set_xerror(s, ERR_SIZE); From a12caae8bad1fd2e10eaa0179589e9e5e8315d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Mon, 1 Jul 2024 19:19:41 +0200 Subject: [PATCH 19/43] [ot] hw/opentitan: use memory_region_size instead of int128_getlo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use memory_region_size to get a 64-bit memory region size instead of calling int128_getlo. One int128_getlo(mr->size) remains in ot_ibex_wrapper_dj.c because memory_region_size argument is not declared const. Signed-off-by: Loïc Lefort --- hw/opentitan/ot_dev_proxy.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/opentitan/ot_dev_proxy.c b/hw/opentitan/ot_dev_proxy.c index d5cd00445fa3..767249a800c5 100644 --- a/hw/opentitan/ot_dev_proxy.c +++ b/hw/opentitan/ot_dev_proxy.c @@ -414,7 +414,7 @@ static void ot_dev_proxy_enumerate_memory_spaces(OtDevProxyState *s) struct entry *entry = &entries[count]; entry->header = ix << 24u; entry->address = subsys->mr->addr; - uint64_t size = int128_getlo(subsys->mr->size); + uint64_t size = memory_region_size(subsys->mr); entry->size = (uint32_t)MIN(size, UINT32_MAX); const char *name = memory_region_name(subsys->mr); size_t namelen = strlen(name); @@ -1263,7 +1263,7 @@ static void ot_dev_proxy_intercept_mmio(OtDevProxyState *s) MemoryRegion *mr = s->subsys[mspc].mr; g_assert(mr->addr == 0); - uint64_t lmrsize = (uint64_t)int128_getlo(mr->size); + uint64_t lmrsize = memory_region_size(mr); uint64_t mrsize = MAX(lmrsize, UINT32_MAX); uint32_t address = s->rx_buffer[1]; @@ -1636,7 +1636,7 @@ static void ot_dev_proxy_reg_mr(GArray *array, Object *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->caps.reg_count = memory_region_size(mr) / sizeof(uint32_t); item->prefix = "M/"; g_array_append_val(array, item); } @@ -1693,7 +1693,7 @@ static void ot_dev_proxy_reg_sram_ctrl(GArray *array, Object *obj) item->obj = obj; item->caps.mr = sysdev->mmio[0].memory; item->caps.reg_count = - int128_getlo(item->caps.mr->size) / sizeof(uint32_t); + memory_region_size(item->caps.mr) / sizeof(uint32_t); item->prefix = "SRC/"; /* SRAM control */ g_array_append_val(array, item); item = g_new0(OtDevProxyItem, 1); @@ -1701,7 +1701,7 @@ static void ot_dev_proxy_reg_sram_ctrl(GArray *array, Object *obj) item->obj = obj; item->caps.mr = sysdev->mmio[1].memory; item->caps.reg_count = - int128_getlo(item->caps.mr->size) / sizeof(uint32_t); + memory_region_size(item->caps.mr) / sizeof(uint32_t); item->prefix = "SRM/"; /* SRAM memory */ g_array_append_val(array, item); } @@ -1722,7 +1722,7 @@ static int ot_dev_proxy_discover_memory_root(Object *child, void *opaque) /* not a root memory region */ return 0; } - if (int128_getlo(mr->size) == 0) { + if (memory_region_size(mr) == 0) { /* empty region, useless */ return 0; } From bddd4bbe165325a6b1b8de002b03aac880cfd308 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 6 Jun 2024 17:35:35 +0200 Subject: [PATCH 20/43] [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 c6a9de9964af..1fd1660bb67a 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240628-1" + BAREMETAL_REF: "240701-1" QEMU_BUILD_OPTS: "" include: From 3f52cbdcc6f8fd19aa55512aba22a42f4a9059c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Tue, 2 Jul 2024 15:08:31 +0200 Subject: [PATCH 21/43] [ot] target/riscv: set MISA.U for Ibex CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Lefort --- target/riscv/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 991b8450ab08..079e5c379770 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -679,7 +679,7 @@ static void rv32_lowrisc_ibexdemo_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - riscv_cpu_set_misa_ext(env, RVI | RVM | RVC); + riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU); env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); @@ -701,7 +701,7 @@ static void rv32_lowrisc_opentitan_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - riscv_cpu_set_misa_ext(env, RVI | RVM | RVC); + riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU); env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); From 4b76e1c1dcf542f7d81953f777388594622490ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Tue, 2 Jul 2024 12:32:29 +0200 Subject: [PATCH 22/43] [ot] target/riscv: add support for MISA.X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Lefort --- target/riscv/cpu.c | 5 +++-- target/riscv/cpu.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 079e5c379770..cddd00351c15 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -42,7 +42,7 @@ /* RISC-V CPU definitions */ static const char riscv_single_letter_exts[] = "IEMAFDQCBPVH"; const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV, - RVC, RVS, RVU, RVH, RVJ, RVG, RVB, 0}; + RVC, RVS, RVU, RVH, RVJ, RVG, RVB, RVX, 0}; /* * From vector_helper.c @@ -1519,7 +1519,8 @@ static const MISAExtInfo misa_ext_info_arr[] = { MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"), MISA_EXT_INFO(RVV, "v", "Vector operations"), MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"), - MISA_EXT_INFO(RVB, "x-b", "Bit manipulation (Zba_Zbb_Zbs)") + MISA_EXT_INFO(RVB, "x-b", "Bit manipulation (Zba_Zbb_Zbs)"), + MISA_EXT_INFO(RVX, "x", "Non-standard extensions") }; static void riscv_cpu_validate_misa_mxl(RISCVCPUClass *mcc) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a5d862d51a02..10718ed7c16d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -71,6 +71,7 @@ typedef struct CPUArchState CPURISCVState; #define RVJ RV('J') #define RVG RV('G') #define RVB RV('B') +#define RVX RV('X') extern const uint32_t misa_bits[]; const char *riscv_get_misa_ext_name(uint32_t bit); From 1a3b87e5449c866a135ba79e400b0bc51356ffcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Tue, 2 Jul 2024 12:33:08 +0200 Subject: [PATCH 23/43] [ot] hw/riscv: enable MISA.X for OpenTitan CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ibex sets MISA.X if any zb* extension is enabled. Signed-off-by: Loïc Lefort --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index cddd00351c15..6f745f8bde3a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -701,7 +701,7 @@ static void rv32_lowrisc_opentitan_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU); + riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU | RVX); env->priv_ver = PRIV_VERSION_1_12_0; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_MBARE); From 0aee7c23a09a900e6f86f175f4a550355e30fdaf Mon Sep 17 00:00:00 2001 From: Noah Moroze Date: Fri, 14 Jun 2024 15:00:01 -0400 Subject: [PATCH 24/43] [ot] hw/opentitan: fix SPI device busy bit calculation The cast to bool takes precedence over the &, so we need parens. Otherwise, the busy bit is always evaluated as false. Signed-off-by: Noah Moroze --- hw/opentitan/ot_spi_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 61296ebd8b0a..77b4a9a2dd75 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -1162,7 +1162,7 @@ static void ot_spi_device_flash_decode_command(OtSPIDeviceState *s, uint8_t cmd) return; } - bool set_busy = (bool)f->cmd_info & CMD_INFO_BUSY_MASK; + bool set_busy = (bool)(f->cmd_info & CMD_INFO_BUSY_MASK); if (set_busy) { s->spi_regs[R_FLASH_STATUS] |= R_FLASH_STATUS_BUSY_MASK; } From 3f38edfa1522d6e51911998daebb58c868437e25 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Jun 2024 14:27:22 +0200 Subject: [PATCH 25/43] [ot] hw/opentitan: ot_spi_device: clear out payload length Payload length should be reset even if the incoming command does not have a payload. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_spi_device.c | 6 ++++-- hw/opentitan/trace-events | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/opentitan/ot_spi_device.c b/hw/opentitan/ot_spi_device.c index 77b4a9a2dd75..26e1806e2485 100644 --- a/hw/opentitan/ot_spi_device.c +++ b/hw/opentitan/ot_spi_device.c @@ -978,8 +978,8 @@ static void ot_spi_device_update_irqs(OtSPIDeviceState *s) uint32_t levels = s->spi_regs[R_INTR_STATE] & s->spi_regs[R_INTR_ENABLE]; for (unsigned ix = 0; ix < PARAM_NUM_IRQS; ix++) { bool level = (bool)((levels >> ix) & 0x1u); - if (level && !ibex_irq_get_level(&s->irqs[ix])) { - trace_ot_spi_device_set_irq(IRQ_NAME(ix), ix); + if (level != (bool)ibex_irq_get_level(&s->irqs[ix])) { + trace_ot_spi_device_set_irq(IRQ_NAME(ix), ix, level); } ibex_irq_set(&s->irqs[ix], (int)level); } @@ -1525,6 +1525,8 @@ static void ot_spi_device_flash_decode_sw_command(OtSPIDeviceState *s) FLASH_CHANGE_STATE(f, UP_DUMMY); } else if (ot_spi_device_flash_has_input_payload(f->cmd_info)) { ot_spi_device_flash_init_payload(s); + } else { + s->spi_regs[R_UPLOAD_STATUS2] = 0; } } diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index bae2829f49aa..98353b6dd53a 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -396,7 +396,7 @@ ot_spi_device_gen_rx_timeout(unsigned count) "%d" ot_spi_device_gen_update_fifo(const char *fifo, int line, uint32_t val) "%s@%d: 0x%08x" ot_spi_device_io_spi_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_spi_device_io_spi_write_in(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_spi_device_set_irq(const char *name, unsigned irq) "%s [%u]" +ot_spi_device_set_irq(const char *name, unsigned irq, bool level) "%s [%u]: %u" ot_spi_device_io_tpm_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_spi_device_io_tpm_write_in(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_spi_device_update_last_read_addr(uint32_t addr) "0x%08x" From 099278f6f16dfb677c72975764516ad889b764e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Jun 2024 12:06:02 +0200 Subject: [PATCH 26/43] [ot] scripts/opentitan: ot.spi: add a new Python module for the SPI device Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/spi/__init__.py | 6 + scripts/opentitan/ot/spi/spi_device.py | 342 +++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 scripts/opentitan/ot/spi/__init__.py create mode 100644 scripts/opentitan/ot/spi/spi_device.py diff --git a/scripts/opentitan/ot/spi/__init__.py b/scripts/opentitan/ot/spi/__init__.py new file mode 100644 index 000000000000..ea3258d4ff96 --- /dev/null +++ b/scripts/opentitan/ot/spi/__init__.py @@ -0,0 +1,6 @@ +# Copyright (c) 2024 Rivos, Inc. +# SPDX-License-Identifier: Apache2 + +"""SPI device tools.""" + +from .spi_device import SpiDevice # noqa: F401 diff --git a/scripts/opentitan/ot/spi/spi_device.py b/scripts/opentitan/ot/spi/spi_device.py new file mode 100644 index 000000000000..48ca9b6fb71a --- /dev/null +++ b/scripts/opentitan/ot/spi/spi_device.py @@ -0,0 +1,342 @@ +# Copyright (c) 2023-2024, Rivos, Inc. +# All rights reserved. + +"""SPI device proxy. + + :author: Emmanuel Blot +""" + +from binascii import hexlify +from logging import getLogger +from select import POLLIN, poll as spoll +from socket import (create_connection, socket, IPPROTO_TCP, TCP_NODELAY, + SHUT_RDWR, timeout as LegacyTimeoutError) +from struct import calcsize as scalc, pack as spack +from time import sleep, time as now +from typing import Optional + + +class SpiDevice: + """SPI device proxy that implements the SPI device protocol over TCP.""" + + TIMEOUT = 2.0 + """Default allowed timeout to complete an exchange with the remote target. + """ + + POLL_TIMEOUT = 0.05 + """Maximum time to wait on a blocking operation. + """ + + VERSION = 0 + """Protocol version.""" + + CS_HEADER_FMT = '3sBBxH' + """CS header format.""" + + CS_HEADER_SIZE = scalc(CS_HEADER_FMT) + """Size of a CS header.""" + + BUSY = 1 << 0 + """Status register BUSY bit.""" + + WEL = 1 << 1 + """Status register write enabled bit.""" + + READ_BUFFER_SIZE = 0x400 + """1 KiB HW buffer.""" + + MAILBOX_SIZE = 0x400 + """1 KiB mailbox.""" + + COMMANDS = { + 'PAGE_PROGRAM': 0x02, + 'READ_DATA': 0x3, + 'WRITE_DISABLE': 0x04, + 'READ_STATUS': 0x05, + 'WRITE_ENABLE': 0x06, + 'FAST_READ': 0x0b, + 'READ_SFDP': 0x5a, + 'READ_JEDEC_ID': 0x9f, + 'ENTER_ADDR4': 0xb7, + 'POWER_DOWN': 0xb9, + 'EXIT_ADDR4': 0xe9, + 'CHIP_ERASE': 0xc7, + 'BLOCK_ERASE': 0xd8, + 'SECTOR_ERASE': 0x20, + 'RESET1': 0x66, + 'RESET2': 0x99, + } + """Supported *25 SPI data flash device commands.""" + + def __init__(self): + self._log = getLogger('spidev') + self._socket: Optional[socket] = None + self._mode = 0 + self._4ben = False + self._rev_rx = False + self._rev_tx = False + + def connect(self, host: str, port: int) -> None: + """Open a TCP connection to the remote host. + + @param host the host name + @paran port the TCP port + """ + if self._socket: + raise RuntimeError('Cannot open multiple comm port at once') + try: + self._socket = create_connection((host, port), timeout=self.TIMEOUT) + except OSError: + self._log.fatal('Cannot connect to %s:%d', host, port) + raise + # use poll + self._socket.settimeout(None) + self._socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) + + def quit(self) -> None: + """Close the communication socket.""" + if self._socket: + self._socket.shutdown(SHUT_RDWR) + self._socket.close() + self._socket = None + + def transmit(self, cmd: Optional[int] = None, + in_payload: Optional[bytes | bytearray | int] = None, + out_len: int = 0, release: bool = True) -> bytes: + """SPI data transfer. + + :param cmd: the command to send + :param in_payload: the payload to send + :param out_len: the count of meaningful bytes to receive + :param release: whether to release /CS line (to manage transactions) + """ + if isinstance(in_payload, int): + in_payload = bytes([0xff] * in_payload) + elif in_payload is not None: + assert isinstance(in_payload, (bytes, bytearray)) + else: + in_payload = bytes() + assert isinstance(out_len, int) and 0 <= out_len <= 0xffff + if cmd is not None: + assert 0 <= cmd <= 0xff + tx_payload = b''.join((bytes([cmd]), in_payload, bytes(out_len))) + else: + tx_payload = b''.join((in_payload, bytes(out_len))) + if self._rev_tx: + tx_payload = bytes(SpiDevice.rev8(x) for x in tx_payload) + rx_payload = self._exchange(tx_payload, release) + if self._rev_rx: + rx_payload = bytes(SpiDevice.rev8(x) for x in rx_payload) + assert len(rx_payload) == len(tx_payload) + return rx_payload[-out_len:] + + def read_status_register(self) -> int: + """Read out the flash status register.""" + resp = self.transmit(self.COMMANDS['READ_STATUS'], out_len=1) + return resp[0] + + def is_busy(self, sreg: Optional[int] = None) -> bool: + """Check if the flash device is busy.""" + if sreg is None: + sreg = self.read_status_register() + return bool(sreg & self.BUSY) + + def is_wel(self, sreg: Optional[int] = None) -> bool: + """Check if the flash device is write-enabled.""" + if sreg is None: + sreg = self.read_status_register() + return bool(sreg & self.WEL) + + def wait_idle(self, timeout: float = 1.0, pace: float = 0.0005): + """Wait for the flash device to become idle. + + :param timeout: raise a TimeoutError if flash does not become + available after this delay + :param pace: delay between each flash device poll request. + """ + timeout += now() + while True: + status = self.read_status_register() + if not status & self.BUSY: + break + if now() > timeout: + raise TimeoutError('Flash stuck to busy') + sleep(pace) + + def read_jedec_id(self) -> bytes: + """Read out the flash device JEDEC ID.""" + jedec = bytearray() + self.transmit(self.COMMANDS['READ_JEDEC_ID'], release=False) + while True: + manuf = self.transmit(out_len=1, release=False)[0] + if manuf != 0x7f: + jedec.append(manuf) + break + jedec.extend(self.transmit(out_len=2)) + return jedec + + def read_sfdp(self, address: int = 0) -> bytes: + """Read out the flash device SFTP descriptor.""" + payload = spack('>I', address) + return self.transmit(self.COMMANDS['READ_SFDP'], payload, 256) + + def enable_write(self): + """Enable write.""" + self.transmit(self.COMMANDS['WRITE_ENABLE']) + + def disable_write(self): + """Disable write.""" + self.transmit(self.COMMANDS['WRITE_DISABLE']) + + def enable_4b_address(self, enable: bool): + """Enable or disable 4-byte addressing mode.""" + self.transmit(self.COMMANDS['ENTER_ADDR4' if enable else 'EXIT_ADDR4']) + self._4ben = enable + + def page_program(self, address: int, buffer: bytes): + """Program a page (usually 256 bytes) into the flash device. + + :param address: address of the first byte to program + :param buffer: the page content + """ + addr = spack('>I', address) + if not self.is_4b_addr: + if address >= (1 << 24): + raise ValueError('Cannot encode address') + addr = addr[1:] + self.transmit(self.COMMANDS['PAGE_PROGRAM'], b''.join((addr, buffer))) + + def power_down(self): + """Power down the device (may trigger a QEMU shurtdown).""" + self.transmit(self.COMMANDS['POWER_DOWN']) + + def chip_erase(self): + """Erase the content of the whole chip.""" + self.transmit(self.COMMANDS['CHIP_ERASE']) + + def block_erase(self, address: int): + """Erase a 64 KiB block.""" + address &= ~(64 << 10) + addr = spack('>I', address) + if not self.is_4b_addr: + if address >= (1 << 24): + raise ValueError('Cannot encode address') + addr = addr[1:] + self.transmit(self.COMMANDS['BLOCK_ERASE'], addr) + + def sector_erase(self, address: int): + """Erase a 4 KiB block.""" + address &= ~(4 << 10) + addr = spack('>I', address) + if not self.is_4b_addr: + if address >= (1 << 24): + raise ValueError('Cannot encode address') + addr = addr[1:] + self.transmit(self.COMMANDS['SECTOR_ERASE'], addr) + + def reset(self): + """Reset the flash device.""" + # self.transmit(self.COMMANDS['RESET1']) + self.transmit(self.COMMANDS['RESET2']) + + def read(self, address: int, length: int, fast: bool = False) -> bytes: + """Read out from the flash device. + + :param address: the address of the first byte to read + :param length: how many bytes to read + :param fast: whether to use the fast SPI read command + """ + if fast: + cmd = self.COMMANDS['FAST_READ'] + dummy = True + else: + cmd = self.COMMANDS['READ_DATA'] + dummy = False + addr = spack('>I', address) + if not self.is_4b_addr: + if address >= (1 << 24): + raise ValueError('Cannot encode address') + addr = addr[1:] + if dummy: + addr.append(0) + return self.transmit(cmd, addr, length) + + @staticmethod + def rev8(num: int) -> int: + """Bit-wise reversal for a byte.""" + num = ((num >> 1) & 0x55) | ((num & 0x55) << 1) + num = ((num >> 2) & 0x33) | ((num & 0x33) << 2) + num = ((num >> 4) & 0x0f) | (num << 4) + return num & 0xff + + def enable_rx_lsb(self, enable: bool): + """Change RX bit ordering.""" + self._rev_rx = enable + if enable: + self._mode |= 1 << 3 + else: + self._mode &= ~(1 << 3) + + def enable_tx_lsb(self, enable: bool): + """Change TX bit ordering.""" + self._rev_tx = enable + if enable: + self._mode |= 1 << 2 + else: + self._mode &= ~(1 << 2) + + @property + def is_4b_addr(self) -> bool: + """Report wether 4-byte addressing mode is active.""" + return self._4ben + + def _build_cs_header(self, size: int, release: bool = True) -> bytes: + mode = self._mode & 0xf + if not release: + mode |= 0x80 + header = spack(self.CS_HEADER_FMT, b'/CS', self.VERSION, mode, + size) + self._log.debug('Header: %s', hexlify(header).decode()) + return header + + def _send(self, buf: bytes, release: bool = True): + data = b''.join((self._build_cs_header(len(buf), release), buf)) + self._log.debug('TX[%d]: %s %s', len(buf), hexlify(buf).decode(), + '/' if release else '...') + self._socket.send(data) + + def _receive(self, size: int) -> bytes: + buf = bytearray() + rem = size + timeout = now() + self.TIMEOUT + poller = spoll() + poller.register(self._socket, POLLIN) + while rem: + for _ in poller.poll(self.POLL_TIMEOUT): + try: + data = self._socket.recv(rem) + buf.extend(data) + rem -= len(data) + if rem == 0: + break + except (TimeoutError, LegacyTimeoutError): + self._log.error('Unexpected timeout w/ poll') + raise + if rem == 0: + break + if now() > timeout: + raise TimeoutError('Failed to receive response') + else: + raise TimeoutError(f'{"No" if len(buf) == 0 else "Truncated"} ' + f'response from host') + self._log.debug("RX[%d]: %s", len(buf), hexlify(buf).decode()) + return bytes(buf) + + def _exchange(self, requ: bytes, release: bool = True) -> bytes: + txlen = len(requ) + self._send(requ, release) + resp = self._receive(txlen) + rxlen = len(resp) + if rxlen != txlen: + raise RuntimeError(f'Response truncated {rxlen}/{txlen}') + return resp From cf1115725656a8bcaffd7510bd92da7eb88c9c1b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 2 Jul 2024 11:23:54 +0200 Subject: [PATCH 27/43] [ot] scripts/opentitan: ot.util.log: add a quiet logger mode Verbose logging messages are only reported if an error is detected Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/util/log.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/opentitan/ot/util/log.py b/scripts/opentitan/ot/util/log.py index 92847ad29843..7196cc68a9d9 100644 --- a/scripts/opentitan/ot/util/log.py +++ b/scripts/opentitan/ot/util/log.py @@ -11,7 +11,7 @@ from typing import NamedTuple, Union import logging - +from logging.handlers import MemoryHandler try: getLevelNamesMapping = logging.getLevelNamesMapping @@ -138,9 +138,15 @@ def configure_loggers(level: int, *lognames: list[Union[str | int | Color]], if isinstance(lnames, str): lnames = [lnames] loglevels[lvl] = tuple(lnames) + quiet = kwargs.pop('quiet', False) formatter = ColorLogFormatter(**kwargs) - logh = logging.StreamHandler(stderr) - logh.setFormatter(formatter) + shandler = logging.StreamHandler(stderr) + shandler.setFormatter(formatter) + if quiet: + logh = MemoryHandler(100000, target=shandler, flushOnClose=False) + shandler.setLevel(loglevel) + else: + logh = shandler loggers: list[logging.Logger] = [] logdefs: list[tuple[list[str], logging.Logger]] = [] color = None From ad36a3a643d03cbdeee13386c420a068ce80a043 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Fri, 28 Jun 2024 14:52:44 +0200 Subject: [PATCH 28/43] [ot] scripts/opentitan: spidevflash: add a new Python script to flash SPI device Signed-off-by: Emmanuel Blot --- scripts/opentitan/spidevflash.py | 129 +++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 scripts/opentitan/spidevflash.py diff --git a/scripts/opentitan/spidevflash.py b/scripts/opentitan/spidevflash.py new file mode 100755 index 000000000000..6c2f60115677 --- /dev/null +++ b/scripts/opentitan/spidevflash.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +"""SPI device flasher tool. + + :author: Emmanuel Blot +""" + +# Copyright (c) 2024 Rivos, Inc. +# SPDX-License-Identifier: Apache2 + +from argparse import ArgumentParser, FileType +from logging import getLogger +from os import linesep +from sys import exit as sysexit, modules, stderr +from time import sleep, time as now +from traceback import format_exc + +from ot.spi import SpiDevice +from ot.util.log import configure_loggers +from ot.util.misc import HexInt + + +class SpiDeviceFlasher: + "Simple SPI device flasher, using OT protocol." + + DEFAULT_PORT = 8004 + """Default TCP port for SPI device.""" + + def __init__(self): + self._log = getLogger('spidev.flash') + self._spidev = SpiDevice() + + def connect(self, host: str, port: int): + """Connect to the remote SPI device and wait for sync.""" + self._spidev.connect(host, port) + self._wait_for_remote() + + def disconnect(self): + """Disconnect from the remote host.""" + self._spidev.power_down() + + def program(self, data: memoryview, offset: int = 0): + """Programm a buffer into the remote flash device.""" + start = now() + total = 0 + page_size = 256 + page_count = (len(data) + page_size - 1) // page_size + log = getLogger('spidev') + log.info('\nRead SFTP') + self._spidev.read_sfdp() + log.info('\nChip erase') + self._spidev.enable_write() + self._spidev.chip_erase() + self._spidev.wait_idle() + for pos in range(0, len(data), page_size): + page = data[pos:pos+page_size] + log.info('Program page @ 0x%06x %d/%d, %d bytes', + pos + offset, pos//page_size, page_count, len(page)) + self._spidev.enable_write() + self._spidev.page_program(pos + offset, page) + sleep(0.003) + self._spidev.wait_idle(pace=0.001) # bootrom is slow :-) + total += len(page) + delta = now() - start + msg = f'{delta:.1f}s to send {total/1024:.1f}KB: ' \ + f'{total/(1024*delta):.1f}KB/s' + log.info('%s', msg) + self._spidev.reset() + + def _wait_for_remote(self): + # use JEDEC ID presence as a sycnhronisation token + # remote SPI device firware should set JEDEC ID when it is full ready + # to handle requests + timeout = now() + 3.0 + while now() < timeout: + jedec = set(self._spidev.read_jedec_id()) + if len(jedec) > 1 or jedec.pop() not in (0x00, 0xff): + return + raise RuntimeError('Remote SPI device not ready') + + +def main(): + """Main routine""" + debug = True + try: + desc = modules[__name__].__doc__.split('.', 1)[0].strip() + argparser = ArgumentParser(description=f'{desc}.') + argparser.add_argument('-f', '--file', type=FileType('rb'), + required=True, + help='Binary file to flash') + argparser.add_argument('-a', '--address', type=HexInt.parse, + default='0', + help='Address in the SPI flash (default to 0)') + argparser.add_argument('-r', '--host', + default='127.0.0.1', + help='remote host name (default: localhost)') + argparser.add_argument('-p', '--port', type=int, + default=SpiDeviceFlasher.DEFAULT_PORT, + help=f'remote host TCP port (defaults to ' + f'{SpiDeviceFlasher.DEFAULT_PORT})') + argparser.add_argument('-v', '--verbose', action='count', + help='increase verbosity') + argparser.add_argument('-d', '--debug', action='store_true', + help='enable debug mode') + args = argparser.parse_args() + debug = args.debug + + configure_loggers(args.verbose, 'spidev') + + flasher = SpiDeviceFlasher() + flasher.connect(args.host, args.port) + data = args.file.read() + args.file.close() + flasher.program(data, args.address) + flasher.disconnect() + + sysexit(0) + # pylint: disable=broad-except + except Exception as exc: + print(f'{linesep}Error: {exc}', file=stderr) + if debug: + print(format_exc(chain=False), file=stderr) + sysexit(1) + except KeyboardInterrupt: + sysexit(2) + + +if __name__ == '__main__': + main() From 3c458d1470c6ccf7b448aaf47498eefb66e89499 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 2 Jul 2024 18:23:27 +0200 Subject: [PATCH 29/43] [ot] docs/opentitan: add documentation for the spidevflash.py tool Signed-off-by: Emmanuel Blot --- docs/opentitan/spi_device.md | 3 +++ docs/opentitan/spidevflash.md | 47 +++++++++++++++++++++++++++++++++++ docs/opentitan/tools.md | 1 + 3 files changed, 51 insertions(+) create mode 100644 docs/opentitan/spidevflash.md diff --git a/docs/opentitan/spi_device.md b/docs/opentitan/spi_device.md index 12dd09f14822..f47eaa7574b7 100644 --- a/docs/opentitan/spi_device.md +++ b/docs/opentitan/spi_device.md @@ -57,6 +57,9 @@ Commands: --qemu-spidev-port [default: 8004] ```` +IT is also possible to use [`spidevflash.py`](spidevflash.md) tool to upload a binary using the same +protocol. + ### SPI device CharDev protocol SPI clock is not emulated, but each byte exchanged over the communication channel represent 8-bit diff --git a/docs/opentitan/spidevflash.md b/docs/opentitan/spidevflash.md new file mode 100644 index 000000000000..b7b596c9965b --- /dev/null +++ b/docs/opentitan/spidevflash.md @@ -0,0 +1,47 @@ +# `spidevflash.py` + +`spidevflash.py` is a tiny script to upload bootstrap image using the SPI device virtual device. + +## Usage + +````text +usage: spidevflash.py [-h] -f FILE [-a ADDRESS] [-r HOST] [-p PORT] [-v] [-d] + +SPI device flasher tool. + +options: + -h, --help show this help message and exit + -f FILE, --file FILE Binary file to flash + -a ADDRESS, --address ADDRESS + Address in the SPI flash (default to 0) + -r HOST, --host HOST remote host name (default: localhost) + -p PORT, --port PORT remote host TCP port (defaults to 8004) + -v, --verbose increase verbosity + -d, --debug enable debug mode +```` + +### Arguments + +* `-a` specify an alernative start address + +* `-d` only useful to debug the script, reports any Python traceback to the standard error stream. + +* `-f` specify the binary file to upload + +* `-p` specify an alternative port for the TCP connection on the QEMU instance + +* `-r` specify the name or address of the remote host running the QEMU instance + +* `-v` can be repeated to increase verbosity of the script, mostly for debug purpose. + +### Examples + +With the following examples: + +`-chardev socket,id=spidev,host=localhost,port=8004,server=on,wait=off -global ot-spi_device.chardev=spidev` has been +added to the QEMU command line to create a TCP chardev and connect it the SPI Device backend. See the [SPI Device](spi_device.md) documentation for details. + +* Upload a bootstrap binary + ````sh + ./scripts/opentitan/spidevflash.py -f test_bootstrap_virtual_sim_dv+manifest.bin + ```` diff --git a/docs/opentitan/tools.md b/docs/opentitan/tools.md index 82c85bac38df..e2ac9eb63669 100644 --- a/docs/opentitan/tools.md +++ b/docs/opentitan/tools.md @@ -44,6 +44,7 @@ directory to help with these tasks. * `ot-tidy.sh` is a simple shell wrapper to run clang-tidy (C linter) on OpenTitan files * `present.py` implements the Present 128-bit scrambler/descrambler used in OTP image files for HW digest verification. +* [spidevice.py](spidevice.md) is a tiny script to upload a binary using the SPI device. * `treillis/` directory contains the test application to test the [GPIO](gpio.md) device. * [`uartmux.py`](uartmux.md) is a tiny stream wrapper to help dealing with multiple QEMU output streams, typically multiple virtual UARTs. From b80ee6842b50d0c438ae83ab75291ffbd6acc0f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 2 Jul 2024 11:22:32 +0200 Subject: [PATCH 30/43] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: simplify SPI device configuration Signed-off-by: Emmanuel Blot --- docs/opentitan/spi_device.md | 1 - hw/riscv/ot_darjeeling.c | 17 +++++++++++++++++ hw/riscv/ot_earlgrey.c | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/opentitan/spi_device.md b/docs/opentitan/spi_device.md index f47eaa7574b7..2fcdfb9f34c9 100644 --- a/docs/opentitan/spi_device.md +++ b/docs/opentitan/spi_device.md @@ -37,7 +37,6 @@ instanciated this way from the command line: ```` -chardev socket,id=spidev,host=localhost,port=8004,server=on,wait=off --global ot-spi_device.chardev=spidev ```` Note that `opentitantool` and association library do support this protocol when the `qemu` backend diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index ccb8b95afaa6..571af630eece 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -92,6 +92,8 @@ 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_spi_device_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_dj_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); @@ -1195,6 +1197,7 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { }, [OT_DJ_SOC_DEV_SPI_DEVICE] = { .type = TYPE_OT_SPI_DEVICE, + .cfg = &ot_dj_soc_spi_device_configure, .memmap = MEMMAPENTRIES( { .base = 0x30310000u } ), @@ -1470,6 +1473,20 @@ static void ot_dj_soc_tap_ctrl_configure( } } +static void ot_dj_soc_spi_device_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("spidev"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + static void ot_dj_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 6e97a795c59e..5c61bafa94e8 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -84,6 +84,8 @@ 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_spi_device_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent); @@ -444,6 +446,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { }, [OT_EG_SOC_DEV_SPI_DEVICE] = { .type = TYPE_OT_SPI_DEVICE, + .cfg = &ot_eg_soc_spi_device_configure, .memmap = MEMMAPENTRIES( { .base = 0x40050000u } ), @@ -1120,6 +1123,20 @@ static void ot_eg_soc_tap_ctrl_configure( } } +static void ot_eg_soc_spi_device_configure( + DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) +{ + (void)parent; + (void)def; + + Chardev *chr; + + chr = ibex_get_chardev_by_id("spidev"); + if (chr) { + qdev_prop_set_chr(dev, "chardev", chr); + } +} + static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent) { From 6ac35e929240a4f37bc91b28b1c27c297ecc10d9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 2 Jul 2024 18:08:22 +0200 Subject: [PATCH 31/43] [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 1fd1660bb67a..accea45145a4 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240701-1" + BAREMETAL_REF: "240702-1" QEMU_BUILD_OPTS: "" include: From 48775228330e93c897ff047dff5bbca5779e1aa9 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 27 Jun 2024 10:30:02 +0200 Subject: [PATCH 32/43] [ot] scripts/opentitan: let dtm.py access memory without halting hart Signed-off-by: Emmanuel Blot --- docs/opentitan/dtm.md | 20 ++++++++--------- scripts/opentitan/dtm.py | 37 +++++++++++++++---------------- scripts/opentitan/ot/dm/dm.py | 5 ++++- scripts/opentitan/ot/util/misc.py | 5 ++++- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/docs/opentitan/dtm.md b/docs/opentitan/dtm.md index 9a8a1c6df876..09a01682e495 100644 --- a/docs/opentitan/dtm.md +++ b/docs/opentitan/dtm.md @@ -1,14 +1,14 @@ # `dtm.py` -`dtm.py` checks that the JTAG/DTM/DM stack is up and running and demonstrates how to use the -Debug Module to access the Ibex core. +`dtm.py` checks that the JTAG/DTM/DM stack is up and running and demonstrates how to use the Debug +Module to access the Ibex core. ## Usage ````text -usage: dtm.py [-h] [-H HOST] [-P PORT] [-Q] [-l IR_LENGTH] [-b BASE] [-I] [-c] - [-C MISA_CHECK] [-x] [-X] [-a ADDRESS] [-m {read,write}] - [-s SIZE] [-f FILE] [-e ELF] [-v] [-d] +usage: dtm.py [-h] [-H HOST] [-P PORT] [-Q] [-t] [-l IR_LENGTH] [-b BASE] [-I] + [-c CSR] [-C CSR_CHECK] [-x] [-X] [-a ADDRESS] [-m {read,write}] + [-s SIZE] [-f FILE] [-e ELF] [-F] [-v] [-d] Debug Transport Module tiny demo @@ -18,12 +18,12 @@ options: Virtual machine: -H HOST, --host HOST JTAG host (default: localhost) -P PORT, --port PORT JTAG port, default: 3335 - -Q, --no-quit do not ask the QEMU to quit on exit + -t, --terminate terminate QEMU when done DMI: -l IR_LENGTH, --ir-length IR_LENGTH - bit length of the IR register - -b BASE, --base BASE define DMI base address + bit length of the IR register (default: 5) + -b BASE, --base BASE define DMI base address (default: 0x0) Info: -I, --info report JTAG ID code and DTM configuration @@ -85,13 +85,13 @@ Extras: * `-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. - * `-s` specify the number of bytes to read from or write to memory. Useful with the `--mem` option. See also the `--address` option. This option may be omitted for the `write` memory operation, in which case the size of the specified file is used. Note that only sizes multiple of 4-byte are supported for now. +* `-t` send QEMU a request for termination when this script exits. + * `-v` can be repeated to increase verbosity of the script, mostly for debug purpose. * `-X` do not attempt to resume normal execution of the hart once DTM operation have been completed. diff --git a/scripts/opentitan/dtm.py b/scripts/opentitan/dtm.py index 8ee99aabdc6a..8e4950b77a79 100755 --- a/scripts/opentitan/dtm.py +++ b/scripts/opentitan/dtm.py @@ -65,8 +65,8 @@ def main(): default=JtagBitbangController.DEFAULT_PORT, help=f'JTAG port, ' f'default: {JtagBitbangController.DEFAULT_PORT}') - qvm.add_argument('-Q', '--no-quit', action='store_true', default=False, - help='do not ask the QEMU to quit on exit') + qvm.add_argument('-t', '--terminate', action='store_true', + help='terminate QEMU when done') dmi = argparser.add_argument_group(title='DMI') dmi.add_argument('-l', '--ir-length', type=int, default=DEFAULT_IR_LENGTH, @@ -114,7 +114,11 @@ def main(): configure_loggers(args.verbose, 'dtm.rvdm', -1, 'dtm', 'jtag') - sock = create_connection((args.host, args.port), timeout=0.5) + try: + sock = create_connection((args.host, args.port), timeout=0.5) + except OSError as exc: + raise RuntimeError(f'Cannot connect to {args.host}:{args.port}: ' + f'{exc}') from exc sock.settimeout(0.1) ctrl = JtagBitbangController(sock) eng = JtagEngine(ctrl) @@ -172,21 +176,16 @@ def main(): if not rvdm: rvdm = DebugModule(dtm, args.base) rvdm.initialize() - try: - rvdm.halt() - if args.file: - mode = 'rb' if args.mem == 'write' else 'wb' - with open(args.file, mode) as mfp: - rvdm.memory_copy(mfp, args.mem, args.address, - args.size, no_check=args.fast_mode) - else: - mfp = BytesIO() - rvdm.memory_copy(mfp, args.mem, args.address, args.size, - no_check=args.fast_mode) - dump_buffer(mfp, args.address) - finally: - if not args.no_exec: - rvdm.resume() + if args.file: + mode = 'rb' if args.mem == 'write' else 'wb' + with open(args.file, mode) as mfp: + rvdm.memory_copy(mfp, args.mem, args.address, + args.size, no_check=args.fast_mode) + else: + mfp = BytesIO() + rvdm.memory_copy(mfp, args.mem, args.address, args.size, + no_check=args.fast_mode) + dump_buffer(mfp, args.address) if args.elf: if not ElfBlob.LOADED: argparser.error('pyelftools module not available') @@ -212,7 +211,7 @@ def main(): if args.execute: argparser.error('Cannot execute without loaded an ELF file') finally: - if not args.no_quit: + if args.terminate: ctrl.quit() # pylint: disable=broad-except diff --git a/scripts/opentitan/ot/dm/dm.py b/scripts/opentitan/ot/dm/dm.py index 910b85cf8ea4..6483d906fa7c 100644 --- a/scripts/opentitan/ot/dm/dm.py +++ b/scripts/opentitan/ot/dm/dm.py @@ -378,7 +378,10 @@ def memory_copy(self, mfp: BinaryIO, mop: str, addr: int, self._wait_sb_idle(check=True) lap = now() - start rate = size / (lap * 1024) - self._log.info('copied %d KB @ %.1f KB/s', size//1024, rate) + if size > 1024: + self._log.info('copied %d KB @ %.1f KB/s', size//1024, rate) + else: + self._log.info('copied %d bytes @ %.1f KB/s', size, rate) def read32(self, addr: int) -> int: """Read a single word from memory.""" diff --git a/scripts/opentitan/ot/util/misc.py b/scripts/opentitan/ot/util/misc.py index 05fac1c1d92f..4dac0feb9469 100644 --- a/scripts/opentitan/ot/util/misc.py +++ b/scripts/opentitan/ot/util/misc.py @@ -6,6 +6,7 @@ :author: Emmanuel Blot """ +from io import BytesIO from sys import stdout from typing import Any, Iterable, Optional, TextIO @@ -76,7 +77,9 @@ def group(lst, count): def dump_buffer(buffer: Buffer, addr: int = 0, file: Optional[TextIO] = None) \ -> None: """Dump a binary buffer, same format as hexdump -C.""" - if isinstance(buffer, memoryview): + if isinstance(buffer, BytesIO): + view = buffer.getbuffer() + elif isinstance(buffer, memoryview): view = buffer.getbuffer() else: view = buffer From de0a39a624892c317b2ab8f0765cf842048f83d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 3 Jul 2024 10:32:39 +0200 Subject: [PATCH 33/43] [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 accea45145a4..41bab7554e82 100644 --- a/.gitlab-ci.d/opentitan/qemu-ot.yml +++ b/.gitlab-ci.d/opentitan/qemu-ot.yml @@ -1,5 +1,5 @@ variables: - BAREMETAL_REF: "240702-1" + BAREMETAL_REF: "240703-1" QEMU_BUILD_OPTS: "" include: From 0935784940c6211464abf39b2c99bbe967c57ada Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 3 Jul 2024 12:52:07 +0200 Subject: [PATCH 34/43] [ot] hw/opentitan: ot_otp_dj: update OTP definitions Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 488 +++++++++++++++++--------------------- hw/opentitan/ot_otp_eg.c | 40 ++-- hw/opentitan/trace-events | 11 +- 3 files changed, 244 insertions(+), 295 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 075a9fd01d51..442a857afee1 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -112,6 +112,7 @@ REG32(STATUS, 0x10u) FIELD(STATUS, BUS_INTEG_ERROR, 28u, 1u) FIELD(STATUS, DAI_IDLE, 29u, 1u) FIELD(STATUS, CHECK_PENDING, 30u, 1u) + FIELD(STATUS, RESET_ALLOWED, 31u, 1u) REG32(ERR_CODE_0, 0x14u) REG32(ERR_CODE_1, 0x18u) REG32(ERR_CODE_2, 0x1cu) @@ -213,127 +214,139 @@ REG32(SECRET2_DIGEST_1, 0x170u) REG32(SECRET3_DIGEST_0, 0x174u) REG32(SECRET3_DIGEST_1, 0x178u) /* Software Config Window registers (at offset SW_CFG_WINDOW = +0x4000) */ -REG32(SCRATCH, 0u) +REG32(VENDOR_TEST_SCRATCH, 0u) REG32(VENDOR_TEST_DIGEST, 56u) -REG32(CREATOR_SW_CFG_AST_CFG, 64u) -REG32(CREATOR_SW_CFG_AST_INIT_EN, 188u) -REG32(CREATOR_SW_CFG_OVERRIDES, 192u) -REG32(CREATOR_SW_CFG_ROM_EXT_SKU, 224u) -REG32(CREATOR_SW_CFG_SIGVERIFY_RSA_MOD_EXP_IBEX_EN, 228u) -REG32(CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN, 232u) -REG32(CREATOR_SW_CFG_SIGVERIFY_SPX_EN, 240u) -REG32(CREATOR_SW_CFG_SIGVERIFY_SPX_KEY_EN, 244u) -REG32(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG, 252u) -REG32(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG, 256u) -REG32(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE, 260u) -REG32(CREATOR_SW_CFG_RNG_EN, 264u) -REG32(CREATOR_SW_CFG_JITTER_EN, 268u) -REG32(CREATOR_SW_CFG_RET_RAM_RESET_MASK, 272u) -REG32(CREATOR_SW_CFG_MANUF_STATE, 276u) -REG32(CREATOR_SW_CFG_ROM_EXEC_EN, 280u) -REG32(CREATOR_SW_CFG_CPUCTRL, 284u) -REG32(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT, 288u) -REG32(CREATOR_SW_CFG_MIN_SEC_VER_BL0, 292u) -REG32(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN, 296u) -REG32(CREATOR_SW_CFG_RMA_SPIN_EN, 300u) -REG32(CREATOR_SW_CFG_RMA_SPIN_CYCLES, 304u) -REG32(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS, 308u) -REG32(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS, 312u) -REG32(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS, 316u) -REG32(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS, 320u) -REG32(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS, 324u) -REG32(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS, 328u) -REG32(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS, 332u) -REG32(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS, 336u) -REG32(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS, 340u) -REG32(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD, 344u) -REG32(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST, 348u) -REG32(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN, 352u) +REG32(CREATOR_SW_CFG_DIO_ATTR, 64u) +REG32(CREATOR_SW_CFG_AST_CFG, 140u) +REG32(CREATOR_SW_CFG_AST_SPARES, 160u) +REG32(CREATOR_SW_CFG_AST_AVGSFUSECTL, 176u) +REG32(CREATOR_SW_CFG_AST_AVGSHDRCFG, 180u) +REG32(CREATOR_SW_CFG_AST_RINGOSC_TRIM_CTL, 184u) +REG32(CREATOR_SW_CFG_AST_RINGOSC_FREQ_COUNT_CTL, 188u) +REG32(CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_SLOW, 192u) +REG32(CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_FAST, 196u) +REG32(CREATOR_SW_CFG_AST_INIT_EN, 200u) +REG32(CREATOR_SW_CFG_OVERRIDES, 204u) +REG32(CREATOR_SW_CFG_ROM_EXT_SKU, 236u) +REG32(CREATOR_SW_CFG_SIGVERIFY_RSA_MOD_EXP_IBEX_EN, 240u) +REG32(CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN, 244u) +REG32(CREATOR_SW_CFG_SIGVERIFY_SPX_EN, 252u) +REG32(CREATOR_SW_CFG_SIGVERIFY_SPX_KEY_EN, 256u) +REG32(CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG, 264u) +REG32(CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG, 268u) +REG32(CREATOR_SW_CFG_FLASH_HW_INFO_CFG_OVERRIDE, 272u) +REG32(CREATOR_SW_CFG_RNG_EN, 276u) +REG32(CREATOR_SW_CFG_JITTER_EN, 280u) +REG32(CREATOR_SW_CFG_RET_RAM_RESET_MASK, 284u) +REG32(CREATOR_SW_CFG_MANUF_STATE, 288u) +REG32(CREATOR_SW_CFG_ROM_EXEC_EN, 292u) +REG32(CREATOR_SW_CFG_CPUCTRL, 296u) +REG32(CREATOR_SW_CFG_MIN_SEC_VER_ROM_EXT, 300u) +REG32(CREATOR_SW_CFG_MIN_SEC_VER_BL0, 304u) +REG32(CREATOR_SW_CFG_DEFAULT_BOOT_DATA_IN_PROD_EN, 308u) +REG32(CREATOR_SW_CFG_RMA_SPIN_EN, 312u) +REG32(CREATOR_SW_CFG_RMA_SPIN_CYCLES, 316u) +REG32(CREATOR_SW_CFG_RNG_REPCNT_THRESHOLDS, 320u) +REG32(CREATOR_SW_CFG_RNG_REPCNTS_THRESHOLDS, 324u) +REG32(CREATOR_SW_CFG_RNG_ADAPTP_HI_THRESHOLDS, 328u) +REG32(CREATOR_SW_CFG_RNG_ADAPTP_LO_THRESHOLDS, 332u) +REG32(CREATOR_SW_CFG_RNG_BUCKET_THRESHOLDS, 336u) +REG32(CREATOR_SW_CFG_RNG_MARKOV_HI_THRESHOLDS, 340u) +REG32(CREATOR_SW_CFG_RNG_MARKOV_LO_THRESHOLDS, 344u) +REG32(CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS, 348u) +REG32(CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS, 352u) +REG32(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD, 356u) +REG32(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST, 360u) +REG32(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN, 364u) REG32(CREATOR_SW_CFG_DIGEST, 376u) REG32(OWNER_SW_CFG_ROM_ERROR_REPORTING, 384u) REG32(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS, 388u) REG32(OWNER_SW_CFG_ROM_ALERT_CLASS_EN, 392u) REG32(OWNER_SW_CFG_ROM_ALERT_ESCALATION, 396u) REG32(OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION, 400u) -REG32(OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION, 796u) -REG32(OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH, 860u) -REG32(OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES, 876u) -REG32(OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES, 892u) -REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD, 956u) -REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END, 960u) -REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV, 964u) -REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA, 968u) -REG32(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES, 972u) -REG32(OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN, 976u) -REG32(OWNER_SW_CFG_MANUF_STATE, 980u) -REG32(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN, 984u) +REG32(OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION, 800u) +REG32(OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH, 864u) +REG32(OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES, 880u) +REG32(OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES, 896u) +REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD, 960u) +REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END, 964u) +REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV, 968u) +REG32(OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA, 972u) +REG32(OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES, 976u) +REG32(OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN, 980u) +REG32(OWNER_SW_CFG_MANUF_STATE, 984u) +REG32(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN, 988u) REG32(OWNER_SW_CFG_DIGEST, 1008u) REG32(OWNERSHIP_SLOT_STATE_ROT_OWNER_AUTH, 1016u) REG32(OWNERSHIP_SLOT_STATE_PLAT_INTEG_AUTH, 1032u) REG32(OWNERSHIP_SLOT_STATE_PLAT_OWNER_AUTH, 1048u) REG32(ROT_CREATOR_AUTH_NON_RAW_MFW_CODESIGN_KEY, 1064u) -REG32(ROT_CREATOR_AUTH_OWNERSHIP_STATE, 1224u) -REG32(ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY, 1228u) -REG32(ROT_CREATOR_AUTH_KEYMANIFEST_KEY, 1388u) -REG32(ROT_CREATOR_AUTH_UNLOCK4XFER_KEY, 1548u) -REG32(ROT_CREATOR_AUTH_IDENTITY_CERT, 1708u) +REG32(ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY, 1240u) +REG32(ROT_CREATOR_AUTH_KEYMANIFEST_KEY, 1416u) +REG32(ROT_CREATOR_AUTH_IDENTITY_CERT, 1592u) +REG32(ROT_CREATOR_AUTH_IDENTITY_CERT_CMAC, 2360u) REG32(ROT_CREATOR_AUTH_DIGEST, 2480u) REG32(ROT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY, 2488u) -REG32(ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY, 2648u) +REG32(ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY, 2552u) REG32(ROT_OWNER_AUTH_SLOT0_DIGEST, 2808u) REG32(ROT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY, 2816u) -REG32(ROT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY, 2976u) +REG32(ROT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY, 2880u) REG32(ROT_OWNER_AUTH_SLOT1_DIGEST, 3136u) REG32(PLAT_INTEG_AUTH_SLOT0_KEYMANIFEST_KEY, 3144u) -REG32(PLAT_INTEG_AUTH_SLOT0_UNLOCK4XFER_KEY, 3304u) +REG32(PLAT_INTEG_AUTH_SLOT0_UNLOCK4XFER_KEY, 3208u) REG32(PLAT_INTEG_AUTH_SLOT0_DIGEST, 3464u) REG32(PLAT_INTEG_AUTH_SLOT1_KEYMANIFEST_KEY, 3472u) -REG32(PLAT_INTEG_AUTH_SLOT1_UNLOCK4XFER_KEY, 3632u) +REG32(PLAT_INTEG_AUTH_SLOT1_UNLOCK4XFER_KEY, 3536u) REG32(PLAT_INTEG_AUTH_SLOT1_DIGEST, 3792u) REG32(PLAT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY, 3800u) -REG32(PLAT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY, 3960u) +REG32(PLAT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY, 3864u) REG32(PLAT_OWNER_AUTH_SLOT0_DIGEST, 4120u) REG32(PLAT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY, 4128u) -REG32(PLAT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY, 4288u) +REG32(PLAT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY, 4192u) REG32(PLAT_OWNER_AUTH_SLOT1_DIGEST, 4448u) REG32(PLAT_OWNER_AUTH_SLOT2_KEYMANIFEST_KEY, 4456u) -REG32(PLAT_OWNER_AUTH_SLOT2_UNLOCK4XFER_KEY, 4616u) +REG32(PLAT_OWNER_AUTH_SLOT2_UNLOCK4XFER_KEY, 4520u) REG32(PLAT_OWNER_AUTH_SLOT2_DIGEST, 4776u) REG32(PLAT_OWNER_AUTH_SLOT3_KEYMANIFEST_KEY, 4784u) -REG32(PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY, 4944u) +REG32(PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY, 4848u) REG32(PLAT_OWNER_AUTH_SLOT3_DIGEST, 5104u) REG32(EXT_NVM_ANTIREPLAY_FRESHNESS_CNT, 5112u) REG32(ROM_PATCH_DATA, 6136u) REG32(ROM_PATCH_DIGEST, 15912u) -REG32(DEVICE_ID, 15920u) -REG32(MANUF_STATE, 15952u) +REG32(HW_CFG0_DEVICE_ID, 15920u) +REG32(HW_CFG0_MANUF_STATE, 15952u) REG32(HW_CFG0_DIGEST, 15984u) -REG32(SOC_DBG_STATE, 15992u) -REG32(EN_SRAM_IFETCH, 15996u) +REG32(HW_CFG1_SOC_DBG_STATE, 15992u) +REG32(HW_CFG1_EN_SRAM_IFETCH, 15996u) REG32(HW_CFG1_DIGEST, 16000u) -REG32(TEST_UNLOCK_TOKEN, 16008u) -REG32(TEST_EXIT_TOKEN, 16024u) +REG32(SECRET0_TEST_UNLOCK_TOKEN, 16008u) +REG32(SECRET0_TEST_EXIT_TOKEN, 16024u) REG32(SECRET0_DIGEST, 16040u) -REG32(FLASH_ADDR_KEY_SEED, 16048u) -REG32(FLASH_DATA_KEY_SEED, 16080u) -REG32(SRAM_DATA_KEY_SEED, 16112u) +REG32(SECRET1_FLASH_ADDR_KEY_SEED, 16048u) +REG32(SECRET1_FLASH_DATA_KEY_SEED, 16080u) +REG32(SECRET1_SRAM_DATA_KEY_SEED, 16112u) REG32(SECRET1_DIGEST, 16128u) -REG32(RMA_TOKEN, 16136u) -REG32(CREATOR_ROOT_KEY_SHARE0, 16152u) -REG32(CREATOR_ROOT_KEY_SHARE1, 16184u) -REG32(CREATOR_SEED, 16216u) +REG32(SECRET2_RMA_TOKEN, 16136u) +REG32(SECRET2_CREATOR_ROOT_KEY_SHARE0, 16152u) +REG32(SECRET2_CREATOR_ROOT_KEY_SHARE1, 16184u) +REG32(SECRET2_CREATOR_SEED, 16216u) REG32(SECRET2_DIGEST, 16248u) -REG32(OWNER_SEED, 16256u) +REG32(SECRET3_OWNER_SEED, 16256u) REG32(SECRET3_DIGEST, 16288u) REG32(LC_TRANSITION_CNT, 16296u) REG32(LC_STATE, 16344u) /* clang-format on */ -#define VENDOR_TEST_SIZE 64u -#define SCRATCH_SIZE 56u -#define VENDOR_TEST_DIGEST_SIZE 8u -#define CREATOR_SW_CFG_SIZE 320u -#define CREATOR_SW_CFG_AST_CFG_SIZE 124u +#define VENDOR_TEST_SCRATCH_SIZE 56u +#define CREATOR_SW_CFG_DIO_ATTR_SIZE 76u +#define CREATOR_SW_CFG_AST_CFG_SIZE 20u +#define CREATOR_SW_CFG_AST_SPARES_SIZE 14u +#define CREATOR_SW_CFG_AST_AVGSFUSECTL_SIZE 4u +#define CREATOR_SW_CFG_AST_AVGSHDRCFG_SIZE 3u +#define CREATOR_SW_CFG_AST_RINGOSC_TRIM_CTL_SIZE 1u +#define CREATOR_SW_CFG_AST_RINGOSC_FREQ_COUNT_CTL_SIZE 2u +#define CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_SLOW_SIZE 2u +#define CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_FAST_SIZE 2u #define CREATOR_SW_CFG_AST_INIT_EN_SIZE 4u #define CREATOR_SW_CFG_OVERRIDES_SIZE 32u #define CREATOR_SW_CFG_ROM_EXT_SKU_SIZE 4u @@ -365,104 +378,60 @@ REG32(LC_STATE, 16344u) #define CREATOR_SW_CFG_RNG_EXTHT_HI_THRESHOLDS_SIZE 4u #define CREATOR_SW_CFG_RNG_EXTHT_LO_THRESHOLDS_SIZE 4u #define CREATOR_SW_CFG_RNG_ALERT_THRESHOLD_SIZE 4u -#define CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST_SIZE 4u #define CREATOR_SW_CFG_SRAM_KEY_RENEW_EN_SIZE 4u -#define CREATOR_SW_CFG_DIGEST_SIZE 8u -#define OWNER_SW_CFG_SIZE 632u #define OWNER_SW_CFG_ROM_ERROR_REPORTING_SIZE 4u #define OWNER_SW_CFG_ROM_BOOTSTRAP_DIS_SIZE 4u #define OWNER_SW_CFG_ROM_ALERT_CLASS_EN_SIZE 4u #define OWNER_SW_CFG_ROM_ALERT_ESCALATION_SIZE 4u -#define OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION_SIZE 396u +#define OWNER_SW_CFG_ROM_ALERT_CLASSIFICATION_SIZE 400u #define OWNER_SW_CFG_ROM_LOCAL_ALERT_CLASSIFICATION_SIZE 64u #define OWNER_SW_CFG_ROM_ALERT_ACCUM_THRESH_SIZE 16u #define OWNER_SW_CFG_ROM_ALERT_TIMEOUT_CYCLES_SIZE 16u #define OWNER_SW_CFG_ROM_ALERT_PHASE_CYCLES_SIZE 64u -#define OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_SIZE 4u -#define OWNER_SW_CFG_ROM_ALERT_DIGEST_PROD_END_SIZE 4u -#define OWNER_SW_CFG_ROM_ALERT_DIGEST_DEV_SIZE 4u -#define OWNER_SW_CFG_ROM_ALERT_DIGEST_RMA_SIZE 4u #define OWNER_SW_CFG_ROM_WATCHDOG_BITE_THRESHOLD_CYCLES_SIZE 4u #define OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN_SIZE 4u #define OWNER_SW_CFG_MANUF_STATE_SIZE 4u #define OWNER_SW_CFG_ROM_RSTMGR_INFO_EN_SIZE 4u -#define OWNER_SW_CFG_DIGEST_SIZE 8u -#define OWNERSHIP_SLOT_STATE_SIZE 48u #define OWNERSHIP_SLOT_STATE_ROT_OWNER_AUTH_SIZE 16u #define OWNERSHIP_SLOT_STATE_PLAT_INTEG_AUTH_SIZE 16u #define OWNERSHIP_SLOT_STATE_PLAT_OWNER_AUTH_SIZE 16u -#define ROT_CREATOR_AUTH_SIZE 1424u -#define ROT_CREATOR_AUTH_NON_RAW_MFW_CODESIGN_KEY_SIZE 160u -#define ROT_CREATOR_AUTH_OWNERSHIP_STATE_SIZE 4u -#define ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY_SIZE 160u -#define ROT_CREATOR_AUTH_KEYMANIFEST_KEY_SIZE 160u -#define ROT_CREATOR_AUTH_UNLOCK4XFER_KEY_SIZE 160u +#define ROT_CREATOR_AUTH_NON_RAW_MFW_CODESIGN_KEY_SIZE 176u +#define ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY_SIZE 176u +#define ROT_CREATOR_AUTH_KEYMANIFEST_KEY_SIZE 176u #define ROT_CREATOR_AUTH_IDENTITY_CERT_SIZE 768u -#define ROT_CREATOR_AUTH_DIGEST_SIZE 8u -#define ROT_OWNER_AUTH_SLOT0_SIZE 328u -#define ROT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 160u -#define ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 160u -#define ROT_OWNER_AUTH_SLOT0_DIGEST_SIZE 8u -#define ROT_OWNER_AUTH_SLOT1_SIZE 328u -#define ROT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 160u -#define ROT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 160u -#define ROT_OWNER_AUTH_SLOT1_DIGEST_SIZE 8u -#define PLAT_INTEG_AUTH_SLOT0_SIZE 328u -#define PLAT_INTEG_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_INTEG_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_INTEG_AUTH_SLOT0_DIGEST_SIZE 8u -#define PLAT_INTEG_AUTH_SLOT1_SIZE 328u -#define PLAT_INTEG_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_INTEG_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_INTEG_AUTH_SLOT1_DIGEST_SIZE 8u -#define PLAT_OWNER_AUTH_SLOT0_SIZE 328u -#define PLAT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT0_DIGEST_SIZE 8u -#define PLAT_OWNER_AUTH_SLOT1_SIZE 328u -#define PLAT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT1_DIGEST_SIZE 8u -#define PLAT_OWNER_AUTH_SLOT2_SIZE 328u -#define PLAT_OWNER_AUTH_SLOT2_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT2_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT2_DIGEST_SIZE 8u -#define PLAT_OWNER_AUTH_SLOT3_SIZE 328u -#define PLAT_OWNER_AUTH_SLOT3_KEYMANIFEST_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY_SIZE 160u -#define PLAT_OWNER_AUTH_SLOT3_DIGEST_SIZE 8u -#define EXT_NVM_SIZE 1024u +#define ROT_CREATOR_AUTH_IDENTITY_CERT_CMAC_SIZE 16u +#define ROT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 64u +#define ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 64u +#define ROT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 64u +#define ROT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_INTEG_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_INTEG_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_INTEG_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_INTEG_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT2_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT2_UNLOCK4XFER_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT3_KEYMANIFEST_KEY_SIZE 64u +#define PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY_SIZE 64u #define EXT_NVM_ANTIREPLAY_FRESHNESS_CNT_SIZE 1024u -#define ROM_PATCH_SIZE 9784u #define ROM_PATCH_DATA_SIZE 9192u -#define ROM_PATCH_DIGEST_SIZE 8u -#define HW_CFG0_SIZE 72u -#define DEVICE_ID_SIZE 32u -#define MANUF_STATE_SIZE 32u -#define HW_CFG0_DIGEST_SIZE 8u -#define HW_CFG1_SIZE 16u -#define SOC_DBG_STATE_SIZE 4u -#define EN_SRAM_IFETCH_SIZE 1u -#define HW_CFG1_DIGEST_SIZE 8u -#define SECRET0_SIZE 40u -#define TEST_UNLOCK_TOKEN_SIZE 16u -#define TEST_EXIT_TOKEN_SIZE 16u -#define SECRET0_DIGEST_SIZE 8u -#define SECRET1_SIZE 88u -#define FLASH_ADDR_KEY_SEED_SIZE 32u -#define FLASH_DATA_KEY_SEED_SIZE 32u -#define SRAM_DATA_KEY_SEED_SIZE 16u -#define SECRET1_DIGEST_SIZE 8u -#define SECRET2_SIZE 120u -#define RMA_TOKEN_SIZE 16u -#define CREATOR_ROOT_KEY_SHARE0_SIZE 32u -#define CREATOR_ROOT_KEY_SHARE1_SIZE 32u -#define CREATOR_SEED_SIZE 32u -#define SECRET2_DIGEST_SIZE 8u -#define SECRET3_SIZE 40u -#define OWNER_SEED_SIZE 32u -#define SECRET3_DIGEST_SIZE 8u -#define LIFE_CYCLE_SIZE 88u +#define HW_CFG0_DEVICE_ID_SIZE 32u +#define HW_CFG0_MANUF_STATE_SIZE 32u +#define HW_CFG1_SOC_DBG_STATE_SIZE 4u +#define HW_CFG1_EN_SRAM_IFETCH_SIZE 1u +#define SECRET0_TEST_UNLOCK_TOKEN_SIZE 16u +#define SECRET0_TEST_EXIT_TOKEN_SIZE 16u +#define SECRET1_FLASH_ADDR_KEY_SEED_SIZE 32u +#define SECRET1_FLASH_DATA_KEY_SEED_SIZE 32u +#define SECRET1_SRAM_DATA_KEY_SEED_SIZE 16u +#define SECRET2_RMA_TOKEN_SIZE 16u +#define SECRET2_CREATOR_ROOT_KEY_SHARE0_SIZE 32u +#define SECRET2_CREATOR_ROOT_KEY_SHARE1_SIZE 32u +#define SECRET2_CREATOR_SEED_SIZE 32u +#define SECRET3_OWNER_SEED_SIZE 32u #define LC_TRANSITION_CNT_SIZE 48u #define LC_STATE_SIZE 40u @@ -565,51 +534,6 @@ REG32(CSR7, 0x1cu) #define OTP_ENTROPY_BUF_COUNT \ (OTP_ENTROPY_PRESENT_WORDS + OTP_ENTROPY_NONCE_WORDS) -#define OTP_PART_VENDOR_TEST_OFFSET 0u -#define OTP_PART_VENDOR_TEST_SIZE 64u -#define OTP_PART_CREATOR_SW_CFG_OFFSET 64u -#define OTP_PART_CREATOR_SW_CFG_SIZE 320u -#define OTP_PART_OWNER_SW_CFG_OFFSET 384u -#define OTP_PART_OWNER_SW_CFG_SIZE 632u -#define OTP_PART_OWNERSHIP_SLOT_STATE_OFFSET 1016u -#define OTP_PART_OWNERSHIP_SLOT_STATE_SIZE 48u -#define OTP_PART_ROT_CREATOR_AUTH_OFFSET 1064u -#define OTP_PART_ROT_CREATOR_AUTH_SIZE 1424u -#define OTP_PART_ROT_OWNER_AUTH_SLOT0_OFFSET 2488u -#define OTP_PART_ROT_OWNER_AUTH_SLOT0_SIZE 328u -#define OTP_PART_ROT_OWNER_AUTH_SLOT1_OFFSET 2816u -#define OTP_PART_ROT_OWNER_AUTH_SLOT1_SIZE 328u -#define OTP_PART_PLAT_INTEG_AUTH_SLOT0_OFFSET 3144u -#define OTP_PART_PLAT_INTEG_AUTH_SLOT0_SIZE 328u -#define OTP_PART_PLAT_INTEG_AUTH_SLOT1_OFFSET 3472u -#define OTP_PART_PLAT_INTEG_AUTH_SLOT1_SIZE 328u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT0_OFFSET 3800u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT0_SIZE 328u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT1_OFFSET 4128u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT1_SIZE 328u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT2_OFFSET 4456u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT2_SIZE 328u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT3_OFFSET 4784u -#define OTP_PART_PLAT_OWNER_AUTH_SLOT3_SIZE 328u -#define OTP_PART_EXT_NVM_OFFSET 5112u -#define OTP_PART_EXT_NVM_SIZE 1024u -#define OTP_PART_ROM_PATCH_OFFSET 6136u -#define OTP_PART_ROM_PATCH_SIZE 9784u -#define OTP_PART_HW_CFG0_OFFSET 15920u -#define OTP_PART_HW_CFG0_SIZE 72u -#define OTP_PART_HW_CFG1_OFFSET 15992u -#define OTP_PART_HW_CFG1_SIZE 16u -#define OTP_PART_SECRET0_OFFSET 16008u -#define OTP_PART_SECRET0_SIZE 40u -#define OTP_PART_SECRET1_OFFSET 16048u -#define OTP_PART_SECRET1_SIZE 88u -#define OTP_PART_SECRET2_OFFSET 16136u -#define OTP_PART_SECRET2_SIZE 120u -#define OTP_PART_SECRET3_OFFSET 16256u -#define OTP_PART_SECRET3_SIZE 40u -#define OTP_PART_LIFE_CYCLE_OFFSET 16296u -#define OTP_PART_LIFE_CYCLE_SIZE 88u - typedef enum { OTP_PART_VENDOR_TEST, OTP_PART_CREATOR_SW_CFG, @@ -733,6 +657,8 @@ typedef struct { #define OT_OTP_DJ_PARTS +#define OTP_PART_LIFE_CYCLE_SIZE 88u + /* NOLINTNEXTLINE */ #include "ot_otp_dj_parts.c" /* NOLINTNEXTLINE */ @@ -1197,9 +1123,13 @@ static void ot_otp_dj_set_error(OtOTPDjState *s, unsigned part, OtOTPError err) { /* This is it NUM_ERROR_ENTRIES */ g_assert(part < NUM_ERROR_ENTRIES); - s->regs[R_ERR_CODE_0 + part] = ((uint32_t)err) & 0x7u; - trace_ot_otp_set_error(part, ERR_CODE_NAME(err), err); + uint32_t errval = ((uint32_t)err) & 0x7; + if (errval || errval != s->regs[R_ERR_CODE_0 + part]) { + trace_ot_otp_set_error(part, ERR_CODE_NAME(err), err); + } + s->regs[R_ERR_CODE_0 + part] = errval; + switch (err) { case OTP_MACRO_ERROR: @@ -1245,6 +1175,7 @@ static uint32_t ot_otp_dj_get_status(const OtOTPDjState *s) status = FIELD_DP32(s->regs[R_STATUS], STATUS, DAI_IDLE, !ot_otp_dj_dai_is_busy(s)); + status = FIELD_DP32(status, STATUS, RESET_ALLOWED, 1u); return status; } @@ -1993,7 +1924,7 @@ static void ot_otp_dj_lci_init(OtOTPDjState *s) LCI_CHANGE_STATE(s, OTP_LCI_IDLE); } -static uint64_t ot_otp_dj_regs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ot_otp_dj_reg_read(void *opaque, hwaddr addr, unsigned size) { OtOTPDjState *s = OT_OTP_DJ(opaque); (void)size; @@ -2265,13 +2196,13 @@ static uint64_t ot_otp_dj_regs_read(void *opaque, hwaddr addr, unsigned size) } uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_otp_io_reg_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); return (uint64_t)val32; } -static void ot_otp_dj_regs_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) +static void ot_otp_dj_reg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { OtOTPDjState *s = OT_OTP_DJ(opaque); (void)size; @@ -2281,7 +2212,7 @@ static void ot_otp_dj_regs_write(void *opaque, hwaddr addr, uint64_t value, uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_write((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_otp_io_reg_write((uint32_t)addr, REG_NAME(reg), val32, pc); switch (reg) { case R_DIRECT_ACCESS_CMD: @@ -2482,14 +2413,26 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) case R_##_reg_: \ return stringify(_reg_) #define CASE_RANGE(_reg_) \ - case R_##_reg_...(R_##_reg_ + ((_reg_##_SIZE) / 4u) - 1u): \ + case R_##_reg_...(R_##_reg_ + (((_reg_##_SIZE) + 3u) / 4u) - 1u): \ + return stringify(_reg_) +#define CASE_DIGEST(_reg_) \ + case R_##_reg_...(R_##_reg_ + 1u): \ return stringify(_reg_) switch (swreg) { - CASE_RANGE(SCRATCH); - CASE_RANGE(VENDOR_TEST_DIGEST); + CASE_RANGE(VENDOR_TEST_SCRATCH); + CASE_DIGEST(VENDOR_TEST_DIGEST); + CASE_RANGE(CREATOR_SW_CFG_DIO_ATTR); CASE_RANGE(CREATOR_SW_CFG_AST_CFG); + CASE_RANGE(CREATOR_SW_CFG_AST_SPARES); + CASE_SCALAR(CREATOR_SW_CFG_AST_AVGSFUSECTL); + CASE_SCALAR(CREATOR_SW_CFG_AST_AVGSHDRCFG); + CASE_SCALAR(CREATOR_SW_CFG_AST_RINGOSC_TRIM_CTL); + CASE_SCALAR(CREATOR_SW_CFG_AST_RINGOSC_FREQ_COUNT_CTL); + CASE_SCALAR(CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_SLOW); + CASE_SCALAR(CREATOR_SW_CFG_AST_RINGOSC_FREQ_TH_FAST); CASE_SCALAR(CREATOR_SW_CFG_AST_INIT_EN); + CASE_RANGE(CREATOR_SW_CFG_OVERRIDES); CASE_SCALAR(CREATOR_SW_CFG_ROM_EXT_SKU); CASE_SCALAR(CREATOR_SW_CFG_SIGVERIFY_RSA_MOD_EXP_IBEX_EN); CASE_RANGE(CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN); @@ -2521,7 +2464,7 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) CASE_SCALAR(CREATOR_SW_CFG_RNG_ALERT_THRESHOLD); CASE_SCALAR(CREATOR_SW_CFG_RNG_HEALTH_CONFIG_DIGEST); CASE_SCALAR(CREATOR_SW_CFG_SRAM_KEY_RENEW_EN); - CASE_RANGE(CREATOR_SW_CFG_DIGEST); + CASE_DIGEST(CREATOR_SW_CFG_DIGEST); CASE_SCALAR(OWNER_SW_CFG_ROM_ERROR_REPORTING); CASE_SCALAR(OWNER_SW_CFG_ROM_BOOTSTRAP_DIS); CASE_SCALAR(OWNER_SW_CFG_ROM_ALERT_CLASS_EN); @@ -2539,64 +2482,63 @@ static const char *ot_otp_dj_swcfg_reg_name(unsigned swreg) CASE_SCALAR(OWNER_SW_CFG_ROM_KEYMGR_ROM_EXT_MEAS_EN); CASE_SCALAR(OWNER_SW_CFG_MANUF_STATE); CASE_SCALAR(OWNER_SW_CFG_ROM_RSTMGR_INFO_EN); - CASE_RANGE(OWNER_SW_CFG_DIGEST); + CASE_DIGEST(OWNER_SW_CFG_DIGEST); CASE_RANGE(OWNERSHIP_SLOT_STATE_ROT_OWNER_AUTH); CASE_RANGE(OWNERSHIP_SLOT_STATE_PLAT_INTEG_AUTH); CASE_RANGE(OWNERSHIP_SLOT_STATE_PLAT_OWNER_AUTH); CASE_RANGE(ROT_CREATOR_AUTH_NON_RAW_MFW_CODESIGN_KEY); - CASE_RANGE(ROT_CREATOR_AUTH_OWNERSHIP_STATE); CASE_RANGE(ROT_CREATOR_AUTH_ROM2_PATCH_SIGVERIFY_KEY); CASE_RANGE(ROT_CREATOR_AUTH_KEYMANIFEST_KEY); - CASE_RANGE(ROT_CREATOR_AUTH_UNLOCK4XFER_KEY); CASE_RANGE(ROT_CREATOR_AUTH_IDENTITY_CERT); - CASE_RANGE(ROT_CREATOR_AUTH_DIGEST); + CASE_RANGE(ROT_CREATOR_AUTH_IDENTITY_CERT_CMAC); + CASE_DIGEST(ROT_CREATOR_AUTH_DIGEST); CASE_RANGE(ROT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY); CASE_RANGE(ROT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY); - CASE_RANGE(ROT_OWNER_AUTH_SLOT0_DIGEST); + CASE_DIGEST(ROT_OWNER_AUTH_SLOT0_DIGEST); CASE_RANGE(ROT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY); CASE_RANGE(ROT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY); - CASE_RANGE(ROT_OWNER_AUTH_SLOT1_DIGEST); + CASE_DIGEST(ROT_OWNER_AUTH_SLOT1_DIGEST); CASE_RANGE(PLAT_INTEG_AUTH_SLOT0_KEYMANIFEST_KEY); CASE_RANGE(PLAT_INTEG_AUTH_SLOT0_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_INTEG_AUTH_SLOT0_DIGEST); + CASE_DIGEST(PLAT_INTEG_AUTH_SLOT0_DIGEST); CASE_RANGE(PLAT_INTEG_AUTH_SLOT1_KEYMANIFEST_KEY); CASE_RANGE(PLAT_INTEG_AUTH_SLOT1_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_INTEG_AUTH_SLOT1_DIGEST); + CASE_DIGEST(PLAT_INTEG_AUTH_SLOT1_DIGEST); CASE_RANGE(PLAT_OWNER_AUTH_SLOT0_KEYMANIFEST_KEY); CASE_RANGE(PLAT_OWNER_AUTH_SLOT0_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_OWNER_AUTH_SLOT0_DIGEST); + CASE_DIGEST(PLAT_OWNER_AUTH_SLOT0_DIGEST); CASE_RANGE(PLAT_OWNER_AUTH_SLOT1_KEYMANIFEST_KEY); CASE_RANGE(PLAT_OWNER_AUTH_SLOT1_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_OWNER_AUTH_SLOT1_DIGEST); + CASE_DIGEST(PLAT_OWNER_AUTH_SLOT1_DIGEST); CASE_RANGE(PLAT_OWNER_AUTH_SLOT2_KEYMANIFEST_KEY); CASE_RANGE(PLAT_OWNER_AUTH_SLOT2_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_OWNER_AUTH_SLOT2_DIGEST); + CASE_DIGEST(PLAT_OWNER_AUTH_SLOT2_DIGEST); CASE_RANGE(PLAT_OWNER_AUTH_SLOT3_KEYMANIFEST_KEY); CASE_RANGE(PLAT_OWNER_AUTH_SLOT3_UNLOCK4XFER_KEY); - CASE_RANGE(PLAT_OWNER_AUTH_SLOT3_DIGEST); + CASE_DIGEST(PLAT_OWNER_AUTH_SLOT3_DIGEST); CASE_RANGE(EXT_NVM_ANTIREPLAY_FRESHNESS_CNT); CASE_RANGE(ROM_PATCH_DATA); - CASE_RANGE(ROM_PATCH_DIGEST); - CASE_RANGE(DEVICE_ID); - CASE_RANGE(MANUF_STATE); - CASE_RANGE(HW_CFG0_DIGEST); - CASE_SCALAR(SOC_DBG_STATE); - CASE_SCALAR(EN_SRAM_IFETCH); - CASE_RANGE(HW_CFG1_DIGEST); - CASE_RANGE(TEST_UNLOCK_TOKEN); - CASE_RANGE(TEST_EXIT_TOKEN); - CASE_RANGE(SECRET0_DIGEST); - CASE_RANGE(FLASH_ADDR_KEY_SEED); - CASE_RANGE(FLASH_DATA_KEY_SEED); - CASE_RANGE(SRAM_DATA_KEY_SEED); - CASE_RANGE(SECRET1_DIGEST); - CASE_RANGE(RMA_TOKEN); - CASE_RANGE(CREATOR_ROOT_KEY_SHARE0); - CASE_RANGE(CREATOR_ROOT_KEY_SHARE1); - CASE_RANGE(CREATOR_SEED); - CASE_RANGE(SECRET2_DIGEST); - CASE_RANGE(OWNER_SEED); - CASE_RANGE(SECRET3_DIGEST); + CASE_DIGEST(ROM_PATCH_DIGEST); + CASE_RANGE(HW_CFG0_DEVICE_ID); + CASE_RANGE(HW_CFG0_MANUF_STATE); + CASE_DIGEST(HW_CFG0_DIGEST); + CASE_SCALAR(HW_CFG1_SOC_DBG_STATE); + CASE_SCALAR(HW_CFG1_EN_SRAM_IFETCH); + CASE_DIGEST(HW_CFG1_DIGEST); + CASE_RANGE(SECRET0_TEST_UNLOCK_TOKEN); + CASE_RANGE(SECRET0_TEST_EXIT_TOKEN); + CASE_DIGEST(SECRET0_DIGEST); + CASE_RANGE(SECRET1_FLASH_ADDR_KEY_SEED); + CASE_RANGE(SECRET1_FLASH_DATA_KEY_SEED); + CASE_RANGE(SECRET1_SRAM_DATA_KEY_SEED); + CASE_DIGEST(SECRET1_DIGEST); + CASE_RANGE(SECRET2_RMA_TOKEN); + CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE0); + CASE_RANGE(SECRET2_CREATOR_ROOT_KEY_SHARE1); + CASE_RANGE(SECRET2_CREATOR_SEED); + CASE_DIGEST(SECRET2_DIGEST); + CASE_RANGE(SECRET3_OWNER_SEED); + CASE_DIGEST(SECRET3_DIGEST); CASE_RANGE(LC_TRANSITION_CNT); CASE_RANGE(LC_STATE); default: @@ -2650,15 +2592,15 @@ static MemTxResult ot_otp_dj_swcfg_read_with_attrs( uint64_t pc; pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, ot_otp_dj_swcfg_reg_name(reg), - val32, pc); + trace_ot_otp_io_swcfg_read_out((uint32_t)addr, + ot_otp_dj_swcfg_reg_name(reg), val32, pc); *data = (uint64_t)val32; return MEMTX_OK; } -static uint64_t ot_otp_dj_csrs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ot_otp_dj_csr_read(void *opaque, hwaddr addr, unsigned size) { (void)opaque; (void)size; @@ -2685,13 +2627,13 @@ static uint64_t ot_otp_dj_csrs_read(void *opaque, hwaddr addr, unsigned size) } uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, CSR_NAME(reg), val32, pc); + trace_ot_otp_io_csr_read_out((uint32_t)addr, CSR_NAME(reg), val32, pc); return (uint64_t)val32; } -static void ot_otp_dj_csrs_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) +static void ot_otp_dj_csr_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { (void)opaque; (void)size; @@ -2700,7 +2642,7 @@ static void ot_otp_dj_csrs_write(void *opaque, hwaddr addr, uint64_t value, hwaddr reg = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_write((uint32_t)addr, CSR_NAME(reg), val32, pc); + trace_ot_otp_io_csr_write((uint32_t)addr, CSR_NAME(reg), val32, pc); switch (reg) { case R_CSR0: @@ -2758,13 +2700,13 @@ static void ot_otp_dj_load_hw_cfg(OtOTPDjState *s) OtOTPStorage *otp = s->otp; OtOTPHWCfg *hw_cfg = s->hw_cfg; - memcpy(hw_cfg->device_id, &otp->data[R_DEVICE_ID], + memcpy(hw_cfg->device_id, &otp->data[R_HW_CFG0_DEVICE_ID], sizeof(*hw_cfg->device_id)); - memcpy(hw_cfg->manuf_state, &otp->data[R_MANUF_STATE], + memcpy(hw_cfg->manuf_state, &otp->data[R_HW_CFG0_MANUF_STATE], sizeof(*hw_cfg->manuf_state)); - hw_cfg->soc_dbg_state = otp->data[R_SOC_DBG_STATE]; - hw_cfg->en_sram_ifetch = (uint8_t)otp->data[R_EN_SRAM_IFETCH]; + hw_cfg->soc_dbg_state = otp->data[R_HW_CFG1_SOC_DBG_STATE]; + hw_cfg->en_sram_ifetch = (uint8_t)otp->data[R_HW_CFG1_EN_SRAM_IFETCH]; } static void ot_otp_dj_load_tokens(OtOTPDjState *s) @@ -2783,15 +2725,15 @@ static void ot_otp_dj_load_tokens(OtOTPDjState *s) switch (tkx) { case OTP_TOKEN_TEST_UNLOCK: partition = OTP_PART_SECRET0; - reg = R_TEST_UNLOCK_TOKEN; + reg = R_SECRET0_TEST_UNLOCK_TOKEN; break; case OTP_TOKEN_TEST_EXIT: partition = OTP_PART_SECRET0; - reg = R_TEST_EXIT_TOKEN; + reg = R_SECRET0_TEST_EXIT_TOKEN; break; case OTP_TOKEN_RMA: partition = OTP_PART_SECRET2; - reg = R_RMA_TOKEN; + reg = R_SECRET2_RMA_TOKEN; break; default: g_assert_not_reached(); @@ -2914,8 +2856,9 @@ static void ot_otp_dj_generate_otp_sram_key(OtOTPDjState *s, OtOTPKey *key) bool valid = pctrl->locked && !pctrl->failed; g_assert(ot_otp_dj_is_buffered(OTP_PART_SECRET1)); const uint32_t *sram_data_key_seed = - &pctrl->buffer.data[R_SRAM_DATA_KEY_SEED - - OTP_PART_SECRET1_OFFSET / sizeof(uint32_t)]; + &pctrl->buffer + .data[R_SECRET1_SRAM_DATA_KEY_SEED - + OtOTPPartDescs[OTP_PART_SECRET1].offset / sizeof(uint32_t)]; uint32_t tmpkey[SRAM_KEY_WORDS]; for (unsigned rix = 0; rix < SRAM_KEY_WIDTH / 64u; rix++) { uint64_t data = RND_CNST_DIGEST_IV; @@ -3332,9 +3275,9 @@ static Property ot_otp_dj_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static const MemoryRegionOps ot_otp_dj_regs_ops = { - .read = &ot_otp_dj_regs_read, - .write = &ot_otp_dj_regs_write, +static const MemoryRegionOps ot_otp_dj_reg_ops = { + .read = &ot_otp_dj_reg_read, + .write = &ot_otp_dj_reg_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, @@ -3347,9 +3290,9 @@ static const MemoryRegionOps ot_otp_dj_swcfg_ops = { .impl.max_access_size = 4, }; -static const MemoryRegionOps ot_otp_dj_csrs_ops = { - .read = &ot_otp_dj_csrs_read, - .write = &ot_otp_dj_csrs_write, +static const MemoryRegionOps ot_otp_dj_csr_ops = { + .read = &ot_otp_dj_csr_read, + .write = &ot_otp_dj_csr_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, @@ -3448,7 +3391,7 @@ static void ot_otp_dj_init(Object *obj) SW_CFG_WINDOW + SW_CFG_WINDOW_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio.ctrl); - memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_dj_regs_ops, s, + memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_dj_reg_ops, s, TYPE_OT_OTP "-regs", REGS_SIZE); memory_region_add_subregion(&s->mmio.ctrl, 0u, &s->mmio.sub.regs); @@ -3458,7 +3401,7 @@ static void ot_otp_dj_init(Object *obj) memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, &s->mmio.sub.swcfg); - memory_region_init_io(&s->prim.csrs, obj, &ot_otp_dj_csrs_ops, s, + memory_region_init_io(&s->prim.csrs, obj, &ot_otp_dj_csr_ops, s, TYPE_OT_OTP "-prim", CSRS_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->prim.csrs); @@ -3516,6 +3459,9 @@ static void ot_otp_dj_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); (void)data; + g_assert(OTP_PART_LIFE_CYCLE_SIZE == + OtOTPPartDescs[OTP_PART_LIFE_CYCLE].size); + dc->reset = &ot_otp_dj_reset; dc->realize = &ot_otp_dj_realize; device_class_set_props(dc, ot_otp_dj_properties); diff --git a/hw/opentitan/ot_otp_eg.c b/hw/opentitan/ot_otp_eg.c index 30d60c23070c..377033bdb720 100644 --- a/hw/opentitan/ot_otp_eg.c +++ b/hw/opentitan/ot_otp_eg.c @@ -708,7 +708,7 @@ static void ot_otp_eg_direct_digest(OtOTPEgState *s) qemu_log_mask(LOG_UNIMP, "%s: OTP change is not supported\n", __func__); } -static uint64_t ot_otp_eg_regs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ot_otp_eg_reg_read(void *opaque, hwaddr addr, unsigned size) { OtOTPEgState *s = OT_OTP_EG(opaque); (void)size; @@ -826,13 +826,13 @@ static uint64_t ot_otp_eg_regs_read(void *opaque, hwaddr addr, unsigned size) } uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_otp_io_reg_read_out((uint32_t)addr, REG_NAME(reg), val32, pc); return (uint64_t)val32; } -static void ot_otp_eg_regs_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) +static void ot_otp_eg_reg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { OtOTPEgState *s = OT_OTP_EG(opaque); (void)size; @@ -841,7 +841,7 @@ static void ot_otp_eg_regs_write(void *opaque, hwaddr addr, uint64_t value, hwaddr reg = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_write((uint32_t)addr, REG_NAME(reg), val32, pc); + trace_ot_otp_io_reg_write((uint32_t)addr, REG_NAME(reg), val32, pc); switch (reg) { case R_INTR_STATE: @@ -1039,8 +1039,8 @@ static uint64_t ot_otp_eg_swcfg_read(void *opaque, hwaddr addr, unsigned size) uint64_t pc; pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, ot_otp_eg_swcfg_reg_name(reg), - val32, pc); + trace_ot_otp_io_swcfg_read_out((uint32_t)addr, + ot_otp_eg_swcfg_reg_name(reg), val32, pc); return (uint64_t)val32; } @@ -1060,7 +1060,7 @@ static void ot_otp_eg_swcfg_write(void *opaque, hwaddr addr, uint64_t value, addr, ot_otp_eg_swcfg_reg_name(reg)); } -static uint64_t ot_otp_eg_csrs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t ot_otp_eg_csr_read(void *opaque, hwaddr addr, unsigned size) { (void)opaque; (void)size; @@ -1087,13 +1087,13 @@ static uint64_t ot_otp_eg_csrs_read(void *opaque, hwaddr addr, unsigned size) } uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_read_out((uint32_t)addr, CSR_NAME(reg), val32, pc); + trace_ot_otp_io_csr_read_out((uint32_t)addr, CSR_NAME(reg), val32, pc); return (uint64_t)val32; } -static void ot_otp_eg_csrs_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) +static void ot_otp_eg_csr_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { (void)opaque; (void)size; @@ -1102,7 +1102,7 @@ static void ot_otp_eg_csrs_write(void *opaque, hwaddr addr, uint64_t value, hwaddr reg = R32_OFF(addr); uint32_t pc = ibex_get_current_pc(); - trace_ot_otp_io_write((uint32_t)addr, CSR_NAME(reg), val32, pc); + trace_ot_otp_io_csr_write((uint32_t)addr, CSR_NAME(reg), val32, pc); switch (reg) { case R_CSR0: @@ -1219,9 +1219,9 @@ static Property ot_otp_eg_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static const MemoryRegionOps ot_otp_eg_regs_ops = { - .read = &ot_otp_eg_regs_read, - .write = &ot_otp_eg_regs_write, +static const MemoryRegionOps ot_otp_eg_reg_ops = { + .read = &ot_otp_eg_reg_read, + .write = &ot_otp_eg_reg_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, @@ -1235,9 +1235,9 @@ static const MemoryRegionOps ot_otp_eg_swcfg_ops = { .impl.max_access_size = 4, }; -static const MemoryRegionOps ot_otp_eg_csrs_ops = { - .read = &ot_otp_eg_csrs_read, - .write = &ot_otp_eg_csrs_write, +static const MemoryRegionOps ot_otp_eg_csr_ops = { + .read = &ot_otp_eg_csr_read, + .write = &ot_otp_eg_csr_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl.min_access_size = 4, .impl.max_access_size = 4, @@ -1375,7 +1375,7 @@ static void ot_otp_eg_init(Object *obj) memory_region_init(&s->mmio.ctrl, obj, TYPE_OT_OTP ".ctrl", 0x2000u); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio.ctrl); - memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_eg_regs_ops, s, + memory_region_init_io(&s->mmio.sub.regs, obj, &ot_otp_eg_reg_ops, s, TYPE_OT_OTP ".regs", REGS_SIZE); memory_region_add_subregion(&s->mmio.ctrl, 0u, &s->mmio.sub.regs); @@ -1385,7 +1385,7 @@ static void ot_otp_eg_init(Object *obj) memory_region_add_subregion(&s->mmio.ctrl, SW_CFG_WINDOW, &s->mmio.sub.swcfg); - memory_region_init_io(&s->prim.csrs, obj, &ot_otp_eg_csrs_ops, s, + memory_region_init_io(&s->prim.csrs, obj, &ot_otp_eg_csr_ops, s, TYPE_OT_OTP ".prim", CSRS_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->prim.csrs); diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 98353b6dd53a..7d89b848e11e 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -289,21 +289,24 @@ ot_otbn_request_entropy(unsigned ep) "ep:%u" # ot_otp.c -ot_otp_addr_to_part(uint32_t addr, const char *part, unsigned pix) "addr 0x%08x partition %s (#%u)" ot_otp_access_error_on(int part, uint32_t addr, const char *msg) "part #%d, addr 0x%04x: %s" +ot_otp_addr_to_part(uint32_t addr, const char *part, unsigned pix) "addr 0x%08x partition %s (#%u)" ot_otp_dai_change_state(int line, const char *old, int nold, const char *new, int nnew) "@ %d [%s:%d] -> [%s:%d]" ot_otp_dai_read(const char* part, unsigned pix, uint32_t hi, uint32_t lo) "DAI read %s (#%u): 0x%08x%08x" ot_otp_initial_lifecycle(uint32_t lc_state, unsigned tcount) "lifecyle 0x%x, transition count %u" ot_otp_initialize(void) "" -ot_otp_io_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" -ot_otp_io_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_otp_integrity_report(const char* part, unsigned pix, const char *msg) "partition %s (#%u) %s" +ot_otp_io_csr_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_otp_io_csr_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_otp_io_reg_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_otp_io_reg_write(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" +ot_otp_io_swcfg_read_out(uint32_t addr, const char * regname, uint32_t val, uint32_t pc) "addr=0x%02x (%s), val=0x%x, pc=0x%x" ot_otp_keygen_entropy(unsigned slot, bool resched) "%u slots, resched: %u" ot_otp_lc_broadcast(unsigned sig, bool level) "bcast %u, level %u" -ot_otp_load_token(const char *token, unsigned tkx, uint64_t hi, uint64_t lo, const char *valid) "%s (%u) 0x%016" PRIx64 "%016" PRIx64 ": %svalid" ot_otp_lci_change_state(int line, const char *old, int nold, const char *new, int nnew) "@ %d [%s:%d] -> [%s:%d]" ot_otp_lci_write(unsigned pos, uint16_t cval, uint16_t nval) "@ %u 0x%04x -> 0x%04x" ot_otp_load_backend(unsigned ver, const char *mode) "loading OTP image v%u in %s mode" +ot_otp_load_token(const char *token, unsigned tkx, uint64_t hi, uint64_t lo, const char *valid) "%s (%u) 0x%016" PRIx64 "%016" PRIx64 ": %svalid" ot_otp_mismatch_digest(const char* part, unsigned pix, uint64_t sdig, uint64_t ldig) "Mismatch digest on %s (#%u), stored 0x%" PRIx64 " found 0x%" PRIx64 ot_otp_pwr_otp_req(const char *where) "%s" ot_otp_reset(void) "" From cd134c3edbd66c09ccf79f1330e8f2ec352798ef Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 3 Jul 2024 15:47:19 +0200 Subject: [PATCH 35/43] [ot] scripts/opentitan: otptool.py: fix a bug that hid some fields with --show Signed-off-by: Emmanuel Blot --- scripts/opentitan/ot/otp/partition.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/opentitan/ot/otp/partition.py b/scripts/opentitan/ot/otp/partition.py index 54cb6f30fba1..37408d53b2a2 100644 --- a/scripts/opentitan/ot/otp/partition.py +++ b/scripts/opentitan/ot/otp/partition.py @@ -164,11 +164,15 @@ def emit(fmt, *args): if itdef.get('ismubi'): emit('%-46s (decoded) %s', name, str(OtpMap.MUBI8_BOOLEANS.get(ival, ival))) - elif itsize == 4 and ival in OtpMap.HARDENED_BOOLEANS: + continue + if itsize == 4 and ival in OtpMap.HARDENED_BOOLEANS: emit('%-46s (decoded) %s', name, str(OtpMap.HARDENED_BOOLEANS[ival])) - else: - emit('%-46s %x', name, ival) + continue + emit('%-46s %x', name, ival) + if self._digest_bytes is not None: + emit('%-46s %s', f'{pname}:DIGEST', + hexlify(self._digest_bytes).decode()) class OtpLifecycleExtension(OtpPartitionDecoder): From 0b2125b555c5e01cf443ba09c49c9f3ee4f8f650 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 3 Jul 2024 15:48:05 +0200 Subject: [PATCH 36/43] [ot] hw/opentitan: ot_rstmgr: add a debug property to shutdown the VM on reset Signed-off-by: Emmanuel Blot --- docs/opentitan/darjeeling.md | 7 ++++++- hw/opentitan/ot_rstmgr.c | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/opentitan/darjeeling.md b/docs/opentitan/darjeeling.md index 800a9cfbbbc4..7bbfe7ad1371 100644 --- a/docs/opentitan/darjeeling.md +++ b/docs/opentitan/darjeeling.md @@ -135,7 +135,8 @@ See [`tools.md`](tools.md) is set to 10 MHz. This option is very useful/mandatory to run many OpenTitan tests that rely on time or CPU cycle to validate features. Using `-icount` option slows down execution speed though, so it is not recommended to use it when the main goal is to develop SW to run on the virtual - machine. + machine. An alternative is to use `-icount shift=auto`, which offers fatest emulation execution, + while preserving an accurate ratio between the vCPU clock and the virtual devices. * `no_epmp_cfg=true` can be appended to the machine option switch, _i.e._ `-M ot-darjeeeling,no_epmp_cfg=true` to disable the initial ePMP configuration, which can be very @@ -162,6 +163,10 @@ See [`tools.md`](tools.md) * `-cpu lowrisc-ibex,x-zbr=false` can be used to force disable the Zbr experimental-and-deprecated RISC-V bitmap extension for CRC32 extension. +* `-global ot-rstmgr.fatal_reset=N`, where `N` is an unsigned integer. Force QEMU VM to exit the + N^th^ time the reset manager received a reset request, rather than rebooting the whole machine as + the default behavior. + ### AES * `-global ot-aes.fast-mode=false` can be used to better emulate AES HW IP, as some OT tests expect diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 050e500ce9fa..77b8549d6ad2 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -44,6 +44,7 @@ #include "hw/riscv/ibex_irq.h" #include "hw/sysbus.h" #include "sysemu/hw_accel.h" +#include "sysemu/runstate.h" #include "trace.h" @@ -154,6 +155,7 @@ struct OtRstMgrState { uint32_t *regs; char *ot_id; + uint32_t fatal_reset; bool por; /* Power-On Reset property */ }; @@ -381,6 +383,14 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, * hardware." */ ibex_irq_set(&s->sw_reset, (int)true); + if (s->fatal_reset) { + s->fatal_reset--; + if (!s->fatal_reset) { + error_report("fatal reset triggered"); + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, 1); + } + } } break; case R_RESET_INFO: @@ -469,6 +479,7 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, static Property ot_rstmgr_properties[] = { DEFINE_PROP_STRING("ot_id", OtRstMgrState, ot_id), + DEFINE_PROP_UINT32("fatal_reset", OtRstMgrState, fatal_reset, 0), /* this property is only used to store initial reset reason state */ DEFINE_PROP_BOOL("por", OtRstMgrState, por, true), DEFINE_PROP_END_OF_LIST(), From 6f9184244f34f69735bfb8786ddae34c2faca46d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Wed, 3 Jul 2024 19:33:37 +0200 Subject: [PATCH 37/43] [ot] hw/opentitan: ot_otp_dj: DAI WRITE data are now committed to the back-end Also fix buffered partition digest computation with DAI DIGEST Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_otp_dj.c | 92 ++++++++++++++++++++++++++++----------- hw/opentitan/trace-events | 2 +- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/hw/opentitan/ot_otp_dj.c b/hw/opentitan/ot_otp_dj.c index 442a857afee1..103bc6a9749e 100644 --- a/hw/opentitan/ot_otp_dj.c +++ b/hw/opentitan/ot_otp_dj.c @@ -1032,6 +1032,8 @@ ot_otp_dj_lci_change_state_line(OtOTPDjState *s, OtOTPLCIState state, int line); #define LCI_CHANGE_STATE(_s_, _st_) \ ot_otp_dj_lci_change_state_line(_s_, _st_, __LINE__) +#define OT_OTP_PART_DATA_OFFSET(_pix_) \ + ((unsigned)(OtOTPPartDescs[(_pix_)].offset)) #define OT_OTP_PART_DATA_BYTE_SIZE(_pix_) \ ((unsigned)(OtOTPPartDescs[(_pix_)].size - \ sizeof(uint32_t) * NUM_DIGEST_WORDS)) @@ -1543,6 +1545,10 @@ static void ot_otp_dj_check_partition_integrity(OtOTPDjState *s, unsigned ix) if (digest != pctrl->buffer.digest) { trace_ot_otp_mismatch_digest(PART_NAME(ix), ix, digest, pctrl->buffer.digest); + + TRACE_OTP("%s: compute digest %016llx from %s\n", __func__, digest, + ot_otp_hexdump(pctrl->buffer.data, part_size)); + pctrl->failed = true; /* this is a fatal error */ ot_otp_dj_set_error(s, ix, OTP_CHECK_FAIL_ERROR); @@ -1575,6 +1581,20 @@ static bool ot_otp_dj_is_backend_writable(OtOTPDjState *s) return (s->blk != NULL) && blk_is_writable(s->blk); } +static inline int ot_otp_dj_write_backend(OtOTPDjState *s, void *buffer, + unsigned offset, size_t size) +{ + /* + * the blk_pwrite API is awful, isolate it so that linter exceptions are + * are not repeated over and over + */ + // NOLINTBEGIN(clang-analyzer-optin.core.EnumCastOutOfRange) + return blk_pwrite(s->blk, (int64_t)(intptr_t)offset, (int64_t)size, buffer, + /* a bitfield of enum is not an enum item */ + (BdrvRequestFlags)0); + // NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) +} + static void ot_otp_dj_dai_init(OtOTPDjState *s) { DAI_CHANGE_STATE(s, OTP_DAI_IDLE); @@ -1734,9 +1754,10 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) } unsigned waddr = address >> 2u; - + unsigned size; if (is_wide || is_digest) { waddr &= ~0b1u; + size = sizeof(uint64_t); uint32_t dst_lo = s->otp->data[waddr]; uint32_t dst_hi = s->otp->data[waddr + 1u]; @@ -1747,29 +1768,38 @@ static void ot_otp_dj_dai_write(OtOTPDjState *s) if ((dst_lo & ~lo) || (dst_hi & ~hi)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear OTP bits\n", __func__); - ot_otp_dj_dai_set_error(s, OTP_MACRO_WRITE_BLANK_ERROR); - return; + ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); } s->otp->data[waddr] = lo; s->otp->data[waddr + 1u] = hi; } else { + size = sizeof(uint32_t); + uint32_t dst = s->otp->data[waddr]; uint32_t data = s->regs[R_DIRECT_ACCESS_WDATA_0]; if (dst & ~data) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear OTP bits\n", __func__); - ot_otp_dj_dai_set_error(s, OTP_MACRO_WRITE_BLANK_ERROR); - return; + ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); } s->otp->data[waddr] = data; } - /* fake slow access to OTP cell */ + uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_dj_write_backend(s, &s->otp->data[waddr], + (unsigned)(offset + waddr * sizeof(uint32_t)), + size)) { + error_report("%s: cannot update OTP backend", __func__); + ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); + return; + } + DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); + /* fake slow access to OTP cell */ timer_mod(s->dai->delay, qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_WRITE_DELAY_NS); } @@ -1839,7 +1869,14 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) DAI_CHANGE_STATE(s, OTP_DAI_DIG_READ); - const uint8_t *data = (const uint8_t *)pctrl->buffer.data; + const uint8_t *data; + + if (OtOTPPartDescs[partition].buffered) { + data = ((const uint8_t *)s->otp->data) + + OT_OTP_PART_DATA_OFFSET(partition); + } else { + data = (const uint8_t *)pctrl->buffer.data; + } unsigned part_size = OT_OTP_PART_DATA_BYTE_SIZE(partition); DAI_CHANGE_STATE(s, OTP_DAI_DIG); @@ -1848,6 +1885,9 @@ static void ot_otp_dj_dai_digest(OtOTPDjState *s) ot_otp_dj_compute_partition_digest(s, data, part_size); s->dai->partition = partition; + TRACE_OTP("%s: next digest %016llx from %s\n", __func__, + pctrl->buffer.next_digest, ot_otp_hexdump(data, part_size)); + DAI_CHANGE_STATE(s, OTP_DAI_DIG_WAIT); /* fake slow access to OTP cell */ @@ -1865,29 +1905,31 @@ static void ot_otp_dj_dai_write_digest(void *opaque) OtOTPPartController *pctrl = &s->partctrls[s->dai->partition]; unsigned address = OtOTPPartDescs[s->dai->partition].digest_offset; - address >>= 3u; - uint64_t *dst = &((uint64_t *)s->otp->data)[address]; + unsigned dwaddr = address / sizeof(uint64_t); + uint64_t *dst = &((uint64_t *)s->otp->data)[dwaddr]; uint64_t data = pctrl->buffer.next_digest; pctrl->buffer.next_digest = 0; - OtOTPError error; + if (*dst & ~data) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Cannot clear OTP bits\n", __func__); - error = OTP_MACRO_WRITE_BLANK_ERROR; + ot_otp_dj_set_error(s, OTP_ENTRY_DAI, OTP_MACRO_WRITE_BLANK_ERROR); } else { *dst |= data; - error = OTP_NO_ERROR; } - if (error == OTP_NO_ERROR) { - /* fake slow access to OTP cell */ - DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); - - timer_mod(s->dai->delay, - qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_WRITE_DELAY_NS); - } else { - /* TODO: no idea on how to report the error since partition is undef */ - ot_otp_dj_dai_set_error(s, error); + uintptr_t offset = (uintptr_t)s->otp->data - (uintptr_t)s->otp->storage; + if (ot_otp_dj_write_backend(s, dst, (unsigned)(offset + address), + sizeof(uint64_t))) { + error_report("%s: cannot update OTP backend", __func__); + ot_otp_dj_dai_set_error(s, OTP_MACRO_ERROR); + return; } + + DAI_CHANGE_STATE(s, OTP_DAI_WRITE_WAIT); + + /* fake slow access to OTP cell */ + timer_mod(s->dai->delay, + qemu_clock_get_ns(OT_VIRTUAL_CLOCK) + DAI_WRITE_DELAY_NS); } static void ot_otp_dj_dai_complete(void *opaque) @@ -3029,11 +3071,9 @@ 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) + if (ot_otp_dj_write_backend(s, &s->otp->data[lc_data_off], + (unsigned)(offset + lcdesc->offset), + lcdesc->size)) { error_report("%s: cannot update OTP backend", __func__); if (lci->error == OTP_NO_ERROR) { lci->error = OTP_MACRO_ERROR; diff --git a/hw/opentitan/trace-events b/hw/opentitan/trace-events index 7d89b848e11e..16cee47fd4e2 100644 --- a/hw/opentitan/trace-events +++ b/hw/opentitan/trace-events @@ -307,7 +307,7 @@ ot_otp_lci_change_state(int line, const char *old, int nold, const char *new, in ot_otp_lci_write(unsigned pos, uint16_t cval, uint16_t nval) "@ %u 0x%04x -> 0x%04x" ot_otp_load_backend(unsigned ver, const char *mode) "loading OTP image v%u in %s mode" ot_otp_load_token(const char *token, unsigned tkx, uint64_t hi, uint64_t lo, const char *valid) "%s (%u) 0x%016" PRIx64 "%016" PRIx64 ": %svalid" -ot_otp_mismatch_digest(const char* part, unsigned pix, uint64_t sdig, uint64_t ldig) "Mismatch digest on %s (#%u), stored 0x%" PRIx64 " found 0x%" PRIx64 +ot_otp_mismatch_digest(const char* part, unsigned pix, uint64_t sdig, uint64_t ldig) "Mismatch digest on %s (#%u), computed 0x%" PRIx64 " stored 0x%" PRIx64 ot_otp_pwr_otp_req(const char *where) "%s" ot_otp_reset(void) "" ot_otp_set_error(unsigned pix, const char* err, unsigned eix) "#%u: %s (%u)" From a2ceca7daa027f9e4a1ebd93601afa6be3ef4672 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 11:33:17 +0200 Subject: [PATCH 38/43] [ot] hw/opentitan: ot_csrng: fix handling of flag0 multibitbool value Non-multibitbool values should not be considered as an error. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_csrng.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/opentitan/ot_csrng.c b/hw/opentitan/ot_csrng.c index bbf417de3d2e..6e7d644ed6f6 100644 --- a/hw/opentitan/ot_csrng.c +++ b/hw/opentitan/ot_csrng.c @@ -1029,8 +1029,7 @@ ot_csrng_handle_instantiate(OtCSRNGState *s, unsigned slot) uint32_t command = ot_fifo32_peek(&inst->cmd_fifo); uint32_t clen = FIELD_EX32(command, OT_CSNRG_CMD, CLEN); bool flag0 = - ot_csrng_check_multibitboot(s, FIELD_EX32(command, OT_CSNRG_CMD, FLAG0), - ALERT_STATUS_BIT(FLAG0)); + FIELD_EX32(command, OT_CSNRG_CMD, FLAG0) == OT_MULTIBITBOOL4_TRUE; uint32_t num; const uint32_t *buffer = @@ -1124,8 +1123,7 @@ static OtCSRNDCmdResult ot_csrng_handle_reseed(OtCSRNGState *s, unsigned slot) uint32_t command = ot_fifo32_peek(&inst->cmd_fifo); bool flag0 = - ot_csrng_check_multibitboot(s, FIELD_EX32(command, OT_CSNRG_CMD, FLAG0), - ALERT_STATUS_BIT(FLAG0)); + FIELD_EX32(command, OT_CSNRG_CMD, FLAG0) == OT_MULTIBITBOOL4_TRUE; xtrace_ot_csrng_info("reseed", flag0); From 3dce3404e72c9dd55ab2f907a3a7d9a73a4d58c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 11:33:53 +0200 Subject: [PATCH 39/43] [ot] scripts/opentitan: spidevflash.py: decrease trace level Signed-off-by: Emmanuel Blot --- scripts/opentitan/spidevflash.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/opentitan/spidevflash.py b/scripts/opentitan/spidevflash.py index 6c2f60115677..81190803a8b8 100755 --- a/scripts/opentitan/spidevflash.py +++ b/scripts/opentitan/spidevflash.py @@ -54,8 +54,8 @@ def program(self, data: memoryview, offset: int = 0): self._spidev.wait_idle() for pos in range(0, len(data), page_size): page = data[pos:pos+page_size] - log.info('Program page @ 0x%06x %d/%d, %d bytes', - pos + offset, pos//page_size, page_count, len(page)) + log.debug('Program page @ 0x%06x %d/%d, %d bytes', + pos + offset, pos//page_size, page_count, len(page)) self._spidev.enable_write() self._spidev.page_program(pos + offset, page) sleep(0.003) From 63b2c3b009a9a3fb74f4ccd77e6e2b09503de72b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 15:23:24 +0200 Subject: [PATCH 40/43] [ot] hw/opentitan: ot_rstmgr, ot_pwrmgr: replace dedicated API with IRQ line Reset request is now managed with an IRQ line, not a dedicated function call. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_pwrmgr.c | 29 +++++++++++------------ hw/opentitan/ot_rstmgr.c | 40 +++++++++++++++++++++----------- include/hw/opentitan/ot_pwrmgr.h | 1 + include/hw/opentitan/ot_rstmgr.h | 12 ++++------ 4 files changed, 47 insertions(+), 35 deletions(-) diff --git a/hw/opentitan/ot_pwrmgr.c b/hw/opentitan/ot_pwrmgr.c index af301eda60a0..74435ae4b749 100644 --- a/hw/opentitan/ot_pwrmgr.c +++ b/hw/opentitan/ot_pwrmgr.c @@ -239,16 +239,16 @@ struct OtPwrMgrState { IbexIRQ cpu_enable; IbexIRQ pwr_lc_req; IbexIRQ pwr_otp_req; + IbexIRQ reset_req; OtPwrMgrFastState f_state; OtPwrMgrSlowState s_state; OtPwrMgrEvents fsm_events; uint32_t *regs; - OtPwrMgrResetReq reset_req; + OtPwrMgrResetReq reset_request; char *ot_id; - OtRstMgrState *rstmgr; uint8_t num_rom; uint8_t version; bool main; /* main power manager (for machines w/ multiple PwrMgr) */ @@ -500,16 +500,16 @@ static void ot_pwrmgr_rst_req(void *opaque, int irq, int level) } s->regs[R_RESET_STATUS] |= rstbit; - g_assert(s->reset_req.domain == OT_PWRMGR_NO_DOMAIN); + g_assert(s->reset_request.domain == OT_PWRMGR_NO_DOMAIN); - s->reset_req.domain = OT_PWRMGR_SLOW_DOMAIN; + s->reset_request.domain = OT_PWRMGR_SLOW_DOMAIN; int req = PWRMGR_RESET_DISPATCH[s->version][src]; if (req < 0) { /* not yet implemented */ g_assert_not_reached(); } - s->reset_req.req = req; + s->reset_request.req = req; trace_ot_pwrmgr_reset_req(s->ot_id, "scheduling reset", src); @@ -538,10 +538,10 @@ static void ot_pwrmgr_sw_rst_req(void *opaque, int irq, int level) s->regs[R_RESET_STATUS] |= rstbit; - g_assert(s->reset_req.domain == OT_PWRMGR_NO_DOMAIN); + g_assert(s->reset_request.domain == OT_PWRMGR_NO_DOMAIN); - s->reset_req.req = OT_RSTMGR_RESET_SW; - s->reset_req.domain = OT_PWRMGR_FAST_DOMAIN; + s->reset_request.req = OT_RSTMGR_RESET_SW; + s->reset_request.domain = OT_PWRMGR_FAST_DOMAIN; trace_ot_pwrmgr_reset_req(s->ot_id, "scheduling SW reset", 0); @@ -643,9 +643,10 @@ static void ot_pwrmgr_fast_fsm_tick(OtPwrMgrState *s) /* fallthrough */ case OT_PWR_FAST_ST_RESET_PREP: PWR_CHANGE_FAST_STATE(s, RESET_WAIT); - ot_rstmgr_reset_req(s->rstmgr, (bool)s->reset_req.domain, - s->reset_req.req); - s->reset_req.domain = OT_PWRMGR_NO_DOMAIN; + ibex_irq_set(&s->reset_req, + OT_RSTMGR_RESET_REQUEST(s->reset_request.domain, + s->reset_request.req)); + s->reset_request.domain = OT_PWRMGR_NO_DOMAIN; break; /* NOLINTNEXTLINE */ case OT_PWR_FAST_ST_RESET_WAIT: @@ -880,8 +881,6 @@ static Property ot_pwrmgr_properties[] = { 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(), }; @@ -917,8 +916,6 @@ static void ot_pwrmgr_reset_enter(Object *obj, ResetType type) c->parent_phases.enter(obj, type); } - assert(s->rstmgr); - timer_del(s->cdc_sync); memset(s->regs, 0, REGS_SIZE); @@ -938,6 +935,7 @@ static void ot_pwrmgr_reset_enter(Object *obj, ResetType type) ibex_irq_set(&s->pwr_otp_req, 0); ibex_irq_set(&s->pwr_lc_req, 0); ibex_irq_set(&s->alert, 0); + ibex_irq_set(&s->reset_req, 0); } static void ot_pwrmgr_reset_exit(Object *obj) @@ -991,6 +989,7 @@ static void ot_pwrmgr_init(Object *obj) ibex_qdev_init_irq(obj, &s->pwr_otp_req, OT_PWRMGR_OTP_REQ); ibex_qdev_init_irq(obj, &s->cpu_enable, OT_PWRMGR_CPU_EN); ibex_qdev_init_irq(obj, &s->strap, OT_PWRMGR_STRAP); + ibex_qdev_init_irq(obj, &s->reset_req, OT_PWRMGR_RST_REQ); s->cdc_sync = timer_new_ns(OT_VIRTUAL_CLOCK, &ot_pwrmgr_cdc_sync, s); diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 77b8549d6ad2..208ac70e60be 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -200,19 +200,6 @@ static const char *OT_RST_MGR_REQUEST_NAMES[] = { OT_RST_MGR_REQUEST_NAMES[(_req_)] : \ "?" -/* -------------------------------------------------------------------------- */ -/* Public API */ -/* -------------------------------------------------------------------------- */ - -void ot_rstmgr_reset_req(OtRstMgrState *s, bool fastclk, OtRstMgrResetReq req) -{ - s->regs[R_RESET_INFO] = 1u << req; - - trace_ot_rstmgr_reset_req(REQ_NAME(req), req, fastclk); - - qemu_bh_schedule(s->bus_reset_bh); -} - /* -------------------------------------------------------------------------- */ /* Private implementation */ /* -------------------------------------------------------------------------- */ @@ -303,6 +290,30 @@ static void ot_rstmgr_update_sw_reset(OtRstMgrState *s, unsigned devix) g_free(desc.path); } +static void ot_rstmgr_reset_req(void *opaque, int irq, int level) +{ + OtRstMgrState *s = opaque; + + if (!level) { + /* reset line released */ + return; + } + + g_assert(irq == 0); + + bool fastclk = ((unsigned)level >> 8u) & 1u; + + level &= 0xff; + g_assert(level < OT_RSTMGR_RESET_COUNT); + + OtRstMgrResetReq req = (OtRstMgrResetReq)level; + s->regs[R_RESET_INFO] = 1u << req; + + trace_ot_rstmgr_reset_req(REQ_NAME(req), req, fastclk); + + qemu_bh_schedule(s->bus_reset_bh); +} + static uint64_t ot_rstmgr_regs_read(void *opaque, hwaddr addr, unsigned size) { OtRstMgrState *s = opaque; @@ -550,6 +561,9 @@ static void ot_rstmgr_init(Object *obj) ibex_qdev_init_irq(obj, &s->sw_reset, OT_RSTMGR_SW_RST); ibex_qdev_init_irqs(obj, s->alerts, OT_DEVICE_ALERT, PARAM_NUM_ALERTS); + qdev_init_gpio_in_named(DEVICE(obj), &ot_rstmgr_reset_req, + OT_RSTMGR_RST_REQ, 1); + s->bus_reset_bh = qemu_bh_new(&ot_rstmgr_reset_bus, s); } diff --git a/include/hw/opentitan/ot_pwrmgr.h b/include/hw/opentitan/ot_pwrmgr.h index fe5d5c6295c9..98f6d2eeff7d 100644 --- a/include/hw/opentitan/ot_pwrmgr.h +++ b/include/hw/opentitan/ot_pwrmgr.h @@ -57,6 +57,7 @@ typedef enum { #define OT_PWRMGR_OTP_REQ TYPE_OT_PWRMGR "-otp-req" #define OT_PWRMGR_CPU_EN TYPE_OT_PWRMGR "-cpu-en" #define OT_PWRMGR_STRAP TYPE_OT_PWRMGR "-strap" +#define OT_PWRMGR_RST_REQ TYPE_OT_PWRMGR "-reset-req" /* input lines */ #define OT_PWRMGR_LC_RSP TYPE_OT_PWRMGR "-lc-rsp" diff --git a/include/hw/opentitan/ot_rstmgr.h b/include/hw/opentitan/ot_rstmgr.h index 5ad44524bf82..028565c951b3 100644 --- a/include/hw/opentitan/ot_rstmgr.h +++ b/include/hw/opentitan/ot_rstmgr.h @@ -49,12 +49,10 @@ typedef enum { OT_RSTMGR_RESET_COUNT, } OtRstMgrResetReq; -/* - * Request a system reset - * - * @fastclk true for fast clock domain, @c false for aon/slow clock - * @req type of reset request - */ -void ot_rstmgr_reset_req(OtRstMgrState *s, bool fastclk, OtRstMgrResetReq req); +#define OT_RSTMGR_RESET_REQUEST(_fast_, _req_) \ + ((int)((1u << 31u) | (((int)(bool)_fast_) << 8u) | _req_)) + +/* input lines */ +#define OT_RSTMGR_RST_REQ TYPE_OT_RSTMGR "-reset-req" #endif /* HW_OPENTITAN_OT_RSTMGR_H */ From 5777b97286f33f8a562017d499b76a72137af67d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 15:24:05 +0200 Subject: [PATCH 41/43] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: connect PWRMGR reset request line to RSTMGR Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 7 +++---- hw/riscv/ot_earlgrey.c | 11 +++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index 571af630eece..a26b0316bcc0 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -1230,10 +1230,9 @@ static const IbexDeviceDef ot_dj_soc_devices[] = { OT_DJ_SOC_SIGNAL(OT_PWRMGR_CPU_EN, 0, IBEX_WRAPPER, OT_IBEX_WRAPPER_CPU_EN, OT_IBEX_PWRMGR_CPU_EN), OT_DJ_SOC_SIGNAL(OT_PWRMGR_STRAP, 0, GPIO, - OT_GPIO_STRAP_EN, 0) - ), - .link = IBEXDEVICELINKDEFS( - OT_DJ_SOC_DEVLINK("rstmgr", RSTMGR) + OT_GPIO_STRAP_EN, 0), + OT_DJ_SOC_SIGNAL(OT_PWRMGR_RST_REQ, 0, RSTMGR, + OT_RSTMGR_RST_REQ, 0) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("num-rom", 2u), diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index 5c61bafa94e8..ae6c9c29f7c4 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -639,19 +639,18 @@ static const IbexDeviceDef ot_eg_soc_devices[] = { OT_EG_SOC_GPIO_SYSBUS_IRQ(0, PLIC, 152), /* loopback signal since Earlgrey OTP signal are not supported yet*/ OT_EG_SOC_SIGNAL(OT_PWRMGR_OTP_REQ, 0, PWRMGR, - OT_PWRMGR_OTP_RSP, 0), + OT_PWRMGR_OTP_RSP, 0), OT_EG_SOC_REQ(OT_PWRMGR_LC, LC_CTRL), OT_EG_SOC_SIGNAL(OT_PWRMGR_CPU_EN, 0, IBEX_WRAPPER, - OT_IBEX_WRAPPER_CPU_EN, - OT_IBEX_PWRMGR_CPU_EN) + OT_IBEX_WRAPPER_CPU_EN, + OT_IBEX_PWRMGR_CPU_EN), + OT_EG_SOC_SIGNAL(OT_PWRMGR_RST_REQ, 0, RSTMGR, + OT_RSTMGR_RST_REQ, 0) ), .prop = IBEXDEVICEPROPDEFS( IBEX_DEV_UINT_PROP("num-rom", 1u), IBEX_DEV_UINT_PROP("version", OT_PWMGR_VERSION_EG) ), - .link = IBEXDEVICELINKDEFS( - OT_EG_SOC_DEVLINK("rstmgr", RSTMGR) - ), }, [OT_EG_SOC_DEV_RSTMGR] = { .type = TYPE_OT_RSTMGR, From 185fccb191d6988be9599be203522cc65fb0c31a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 15:26:58 +0200 Subject: [PATCH 42/43] [ot] hw/opentitan: ot_rstmgr: replace platform reset with IRQ line It is up to the machine to manage the reset signal. Signed-off-by: Emmanuel Blot --- hw/opentitan/ot_rstmgr.c | 15 ++++++--------- include/hw/opentitan/ot_rstmgr.h | 6 ++++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/hw/opentitan/ot_rstmgr.c b/hw/opentitan/ot_rstmgr.c index 208ac70e60be..357ce106a09d 100644 --- a/hw/opentitan/ot_rstmgr.c +++ b/hw/opentitan/ot_rstmgr.c @@ -43,7 +43,6 @@ #include "hw/riscv/ibex_common.h" #include "hw/riscv/ibex_irq.h" #include "hw/sysbus.h" -#include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "trace.h" @@ -147,6 +146,7 @@ struct OtRstMgrState { SysBusDevice parent_obj; MemoryRegion mmio; + IbexIRQ soc_reset; IbexIRQ sw_reset; IbexIRQ alerts[PARAM_NUM_ALERTS]; QEMUBH *bus_reset_bh; @@ -228,12 +228,7 @@ static void ot_rstmgr_reset_bus(void *opaque) } qemu_notify_event(); - cpu_synchronize_state(s->cpu); - /* Reset all OpenTitan devices connected to RSTMGR parent bus */ - bus_cold_reset(s->parent_obj.parent_obj.parent_bus); - cpu_synchronize_post_reset(s->cpu); - - /* TODO: manage reset tree (depending on power domains, etc.) */ + ibex_irq_raise(&s->soc_reset); } static int ot_rstmgr_sw_rst_walker(DeviceState *dev, void *opaque) @@ -393,7 +388,7 @@ static void ot_rstmgr_regs_write(void *opaque, hwaddr addr, uint64_t val64, * "Upon completion of reset, this bit is automatically cleared by * hardware." */ - ibex_irq_set(&s->sw_reset, (int)true); + ibex_irq_raise(&s->sw_reset); if (s->fatal_reset) { s->fatal_reset--; if (!s->fatal_reset) { @@ -544,7 +539,8 @@ static void ot_rstmgr_reset(DeviceState *dev) s->regs[R_SW_RST_CTRL_N_0 + ix] = 0x1u; } - ibex_irq_set(&s->sw_reset, 0); + ibex_irq_lower(&s->soc_reset); + ibex_irq_lower(&s->sw_reset); ot_rstmgr_update_alerts(s); } @@ -558,6 +554,7 @@ static void ot_rstmgr_init(Object *obj) s->regs = g_new0(uint32_t, REGS_COUNT); + ibex_qdev_init_irq(obj, &s->soc_reset, OT_RSTMGR_SOC_RST); ibex_qdev_init_irq(obj, &s->sw_reset, OT_RSTMGR_SW_RST); ibex_qdev_init_irqs(obj, s->alerts, OT_DEVICE_ALERT, PARAM_NUM_ALERTS); diff --git a/include/hw/opentitan/ot_rstmgr.h b/include/hw/opentitan/ot_rstmgr.h index 028565c951b3..546202d970bb 100644 --- a/include/hw/opentitan/ot_rstmgr.h +++ b/include/hw/opentitan/ot_rstmgr.h @@ -33,8 +33,6 @@ #define TYPE_OT_RSTMGR "ot-rstmgr" OBJECT_DECLARE_SIMPLE_TYPE(OtRstMgrState, OT_RSTMGR) -#define OT_RSTMGR_SW_RST TYPE_OT_RSTMGR "-sw-rst" - typedef enum { OT_RSTMGR_RESET_POR, OT_RSTMGR_RESET_LOW_POWER, @@ -52,6 +50,10 @@ typedef enum { #define OT_RSTMGR_RESET_REQUEST(_fast_, _req_) \ ((int)((1u << 31u) | (((int)(bool)_fast_) << 8u) | _req_)) +/* output lines */ +#define OT_RSTMGR_SOC_RST TYPE_OT_RSTMGR "-soc-reset" +#define OT_RSTMGR_SW_RST TYPE_OT_RSTMGR "-sw-reset" + /* input lines */ #define OT_RSTMGR_RST_REQ TYPE_OT_RSTMGR "-reset-req" From efd8f6c6a811e367ddd3cbf3ce9f034fdd8bc74d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 4 Jul 2024 15:43:16 +0200 Subject: [PATCH 43/43] [ot] hw/riscv: ot_darjeeling, ot_earlgrey: connect RSTMGR reset request line to SoC Signed-off-by: Emmanuel Blot --- hw/riscv/ot_darjeeling.c | 25 +++++++++++++++++++++++++ hw/riscv/ot_earlgrey.c | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/hw/riscv/ot_darjeeling.c b/hw/riscv/ot_darjeeling.c index a26b0316bcc0..7b4ad1fc099e 100644 --- a/hw/riscv/ot_darjeeling.c +++ b/hw/riscv/ot_darjeeling.c @@ -77,6 +77,7 @@ #include "hw/riscv/ot_darjeeling.h" #include "hw/ssi/ssi.h" #include "sysemu/blockdev.h" +#include "sysemu/hw_accel.h" #include "sysemu/reset.h" #include "sysemu/sysemu.h" @@ -341,6 +342,8 @@ static const uint32_t ot_dj_pmp_addrs[] = { #define OT_DJ_MSECCFG IBEX_MSECCFG(1, 1, 0) +#define OT_DJ_SOC_RST_REQ TYPE_RISCV_OT_DJ_SOC "-reset" + #define OT_DJ_SOC_GPIO(_irq_, _target_, _num_) \ IBEX_GPIO(_irq_, OT_DJ_SOC_DEV_##_target_, _num_) @@ -1498,6 +1501,20 @@ static void ot_dj_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, /* SoC */ /* ------------------------------------------------------------------------ */ +static void ot_dj_soc_hw_reset(void *opaque, int irq, int level) +{ + OtDjSoCState *s = opaque; + + g_assert(irq == 0); + + if (level) { + CPUState *cs = CPU(s->devices[OT_DJ_SOC_DEV_HART]); + cpu_synchronize_state(cs); + bus_cold_reset(sysbus_get_default()); + cpu_synchronize_post_reset(cs); + } +} + static void ot_dj_soc_reset_hold(Object *obj) { OtDjSoCClass *c = RISCV_OT_DJ_SOC_GET_CLASS(obj); @@ -1641,6 +1658,11 @@ static void ot_dj_soc_realize(DeviceState *dev, Error **errp) object_property_add_child(OBJECT(dev), "ctn-dma", oas); ot_address_space_set(OT_ADDRESS_SPACE(oas), ctn_dma_as); + qdev_connect_gpio_out_named(DEVICE(s->devices[OT_DJ_SOC_DEV_RSTMGR]), + OT_RSTMGR_SOC_RST, 0, + qdev_get_gpio_in_named(DEVICE(s), + OT_DJ_SOC_RST_REQ, 0)); + /* load kernel if provided */ ibex_load_kernel(cpu); } @@ -1651,6 +1673,9 @@ static void ot_dj_soc_init(Object *obj) s->devices = ibex_create_devices(ot_dj_soc_devices, ARRAY_SIZE(ot_dj_soc_devices), DEVICE(s)); + + qdev_init_gpio_in_named(DEVICE(obj), &ot_dj_soc_hw_reset, OT_DJ_SOC_RST_REQ, + 1); } static void ot_dj_soc_class_init(ObjectClass *oc, void *data) diff --git a/hw/riscv/ot_earlgrey.c b/hw/riscv/ot_earlgrey.c index ae6c9c29f7c4..fb1a7882f5ff 100644 --- a/hw/riscv/ot_earlgrey.c +++ b/hw/riscv/ot_earlgrey.c @@ -68,6 +68,7 @@ #include "hw/riscv/ot_earlgrey.h" #include "hw/ssi/ssi.h" #include "sysemu/blockdev.h" +#include "sysemu/hw_accel.h" #include "sysemu/sysemu.h" /* ------------------------------------------------------------------------ */ @@ -202,6 +203,8 @@ static const uint32_t ot_eg_pmp_addrs[] = { #define OT_EG_MSECCFG IBEX_MSECCFG(1, 1, 0) +#define OT_EG_SOC_RST_REQ TYPE_RISCV_OT_EG_SOC "-reset" + #define OT_EG_SOC_GPIO(_irq_, _target_, _num_) \ IBEX_GPIO(_irq_, OT_EG_SOC_DEV_##_target_, _num_) @@ -1148,6 +1151,20 @@ static void ot_eg_soc_uart_configure(DeviceState *dev, const IbexDeviceDef *def, /* SoC */ /* ------------------------------------------------------------------------ */ +static void ot_eg_soc_hw_reset(void *opaque, int irq, int level) +{ + OtEGSoCState *s = opaque; + + g_assert(irq == 0); + + if (level) { + CPUState *cs = CPU(s->devices[OT_EG_SOC_DEV_HART]); + cpu_synchronize_state(cs); + bus_cold_reset(sysbus_get_default()); + cpu_synchronize_post_reset(cs); + } +} + static void ot_eg_soc_reset_hold(Object *obj) { OtEGSoCClass *c = RISCV_OT_EG_SOC_GET_CLASS(obj); @@ -1208,6 +1225,11 @@ static void ot_eg_soc_realize(DeviceState *dev, Error **errp) ibex_map_devices(s->devices, mrs, ot_eg_soc_devices, ARRAY_SIZE(ot_eg_soc_devices)); + qdev_connect_gpio_out_named(DEVICE(s->devices[OT_EG_SOC_DEV_RSTMGR]), + OT_RSTMGR_SOC_RST, 0, + qdev_get_gpio_in_named(DEVICE(s), + OT_EG_SOC_RST_REQ, 0)); + /* load kernel if provided */ ibex_load_kernel(NULL); } @@ -1218,6 +1240,9 @@ static void ot_eg_soc_init(Object *obj) s->devices = ibex_create_devices(ot_eg_soc_devices, ARRAY_SIZE(ot_eg_soc_devices), DEVICE(s)); + + qdev_init_gpio_in_named(DEVICE(obj), &ot_eg_soc_hw_reset, OT_EG_SOC_RST_REQ, + 1); } static void ot_eg_soc_class_init(ObjectClass *oc, void *data)