:p
atchew
Login
Hi all, The ASPEED AST2700fc SoC integrates the Caliptra Root of Trust (Caliptra 1.2 core). Moving forward, we expect more SoCs to adopt Caliptra cores (e.g., Caliptra 2.x). Currently, QEMU does not have a native Caliptra emulator. This RFC explores possible approaches for supporting Caliptra in QEMU. The included patch provides a Proof of Concept (PoC) integration using the external `caliptra-sw` C-binding emulator to enable booting the AST2700fc machine. This RFC does not propose merging the current PoC design as-is. Instead, it is intended to gather early feedback on architecture and integration strategy. In this PoC, the `caliptra-sw` emulator is used as a reference model to enable early bring-up and validation of the AST2700fc secure boot flow, rather than as a final implementation choice. The integration is based on the `caliptra-1.2` branch of the official repository: https://github.com/chipsalliance/caliptra-sw/tree/caliptra-1.2 To avoid making normal ASPEED/AArch64 builds depend on a local external checkout, the c-binding backend is compiled only when this PoC detects the generated `caliptra-sw` c-binding header and static library in an expected sibling source tree. A mergeable version would need an explicit configure option and a proper dependency integration. Since QEMU currently lacks a `bootmcu` model and the associated mailbox interactions with Caliptra, the PoC simplifies the boot flow by using a background thread and the `caliptra_model_boot_default` C-binding to simulate early boot stages. To approximate the hardware behavior where the Cortex-A35 CPUs remain in reset until secure boot is completed, the PoC uses `vm_stop_force_state()` as a temporary approximation. This approach is intentionally simplified and not intended to reflect the final QEMU design. In particular, the use of a background thread and global VM control APIs may not align with QEMU device model expectations. Support for the `bootmcu` model and a more accurate secure boot flow is planned for future work. Discussion points: 1. Emulator strategy: For early enablement, is it acceptable to rely on an external reference model such as `caliptra-sw`, or would the community prefer focusing directly on a native QEMU QOM device model? 2. Build system integration: If using the external `caliptra-sw` library is acceptable, what is the preferred integration method (e.g., git submodule, meson subproject, or system-installed library via pkg-config)? 3. Boot synchronization: In real hardware, CPUs are held in reset until the secure boot flow completes. What is the recommended way in QEMU to model such dependencies between devices and CPU startup? Feedback and suggestions are highly appreciated. Thanks, Steven Steven Lee (1): hw/arm: aspeed: Add PoC integration for Caliptra Root of Trust MAINTAINERS | 2 + docs/system/arm/aspeed.rst | 20 +++ include/hw/arm/aspeed_caliptra_emu.h | 17 ++ hw/arm/aspeed_ast27x0-fc.c | 60 +++++++ hw/arm/aspeed_caliptra_emu.c | 259 +++++++++++++++++++++++++++ hw/arm/meson.build | 31 +++- 6 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 include/hw/arm/aspeed_caliptra_emu.h create mode 100644 hw/arm/aspeed_caliptra_emu.c -- 2.43.0
The ASPEED AST2700fc SoC integrates the Caliptra Root of Trust (Caliptra 1.2 core). Moving forward, we expect more SoCs to adopt Caliptra cores (e.g., Caliptra 2.x). Currently, QEMU does not have a native Caliptra emulator. The official `caliptra-sw` project provides a fully functional emulator with C-bindings. As a Proof of Concept, this RFC integrates the external `caliptra-sw` C-binding model into QEMU to enable booting the AST2700fc machine. This RFC does not propose merging the current PoC design as-is. Instead, it is intended to explore possible approaches for adding Caliptra support in QEMU and to gather early feedback from the community on architecture and integration strategy. In this PoC, the `caliptra-sw` emulator is used as a reference model to enable early bring-up and validation of the AST2700fc secure boot flow, rather than as a final implementation choice. The integration is based on the `caliptra-1.2` branch of the official repository: https://github.com/chipsalliance/caliptra-sw/tree/caliptra-1.2 To avoid making normal ASPEED/AArch64 builds depend on a local external checkout, the c-binding backend is compiled only when the PoC detects the generated `caliptra-sw` c-binding header and static library in the expected sibling source tree. A mergeable version would need an explicit configure option and a proper dependency integration. For context, the actual hardware boot flow on the AST2700fc relies on a dedicated RISC-V core (bootmcu) to orchestrate the secure boot: 1. bootmcu executes its ROM code. 2. bootmcu releases the Caliptra core from reset. 3. Caliptra requests its FMC and Runtime (RT) firmware. 4. bootmcu loads the Caliptra FMC/RT images into Caliptra's mailbox. 5. Caliptra ROM verifies the signature of the Caliptra FMC/RT images. 6. Caliptra boots into its FMC and RT environments. 7. bootmcu requests Caliptra RT to verify the bootmcu's own FMC. 8. bootmcu executes its FMC. 9. bootmcu requests Caliptra to verify the BMC (Cortex-A35) firmware. 10. BMC (Cortex-A35) firmware begins execution. Since QEMU currently lacks a `bootmcu` model and the associated mailbox interactions with Caliptra, this PoC simplifies the boot flow by using a background thread and the `caliptra_model_boot_default` C-binding to simulate steps 1 through 6. To approximate the hardware behavior where the Cortex-A35 CPUs remain in reset until secure boot is completed, this PoC uses `vm_stop_force_state()` as a temporary approximation. This approach is intentionally simplified and not intended to reflect the final QEMU design. In particular, the use of a background thread and global VM control APIs may not align with QEMU device model expectations, and alternative approaches are encouraged. Signed-off-by: Steven Lee <steven_lee@aspeedtech.com> --- MAINTAINERS | 2 + docs/system/arm/aspeed.rst | 20 +++ include/hw/arm/aspeed_caliptra_emu.h | 17 ++ hw/arm/aspeed_ast27x0-fc.c | 60 +++++++ hw/arm/aspeed_caliptra_emu.c | 259 +++++++++++++++++++++++++++ hw/arm/meson.build | 31 +++- 6 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 include/hw/arm/aspeed_caliptra_emu.h create mode 100644 hw/arm/aspeed_caliptra_emu.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/*aspeed* F: include/hw/*/*aspeed* +F: hw/arm/aspeed_caliptra_emu.c +F: include/hw/arm/aspeed_caliptra_emu.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -XXX,XX +XXX,XX @@ Steps to boot the AST2700fc machine: -snapshot \ -S -nographic +The ``ast2700fc`` machine can also boot a Caliptra emulator through the +``caliptra-sw`` c-binding before starting the Cortex-A35 processors. The ROM +and firmware bundle are configured with the ``caliptra-rom`` and +``caliptra-firmware`` machine properties. + +This RFC PoC backend is built only when a local ``../caliptra-sw`` checkout +contains the generated c-binding header and static library. + +For example, to run Caliptra firmware from ``caliptra-sw``: + +.. code-block:: bash + + $ qemu-system-aarch64 \ + -M ast2700fc,caliptra-rom=images/caliptra-rom-with-log.bin,caliptra-firmware=images/caliptra-fw.bundle \ + ... + +QEMU boots the c-binding model to the Caliptra runtime-ready boot status before +starting the Cortex-A35 CPUs. If these properties are omitted, no Caliptra +backend is launched. + After launching QEMU, serial devices will be automatically redirected. Example output: diff --git a/include/hw/arm/aspeed_caliptra_emu.h b/include/hw/arm/aspeed_caliptra_emu.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/arm/aspeed_caliptra_emu.h @@ -XXX,XX +XXX,XX @@ +/* + * ASPEED Caliptra emulator backend + * + * Copyright (C) 2026 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_CALIPTRA_EMU_H +#define ASPEED_CALIPTRA_EMU_H + +#include "chardev/char.h" + +void aspeed_caliptra_emu_start(Chardev *console, const char *rom_path, + const char *firmware_path); + +#endif /* ASPEED_CALIPTRA_EMU_H */ diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -XXX,XX +XXX,XX @@ #include "hw/arm/boot.h" #include "hw/block/flash.h" #include "hw/arm/aspeed_coprocessor.h" +#include "hw/arm/aspeed_caliptra_emu.h" #include "hw/arm/machines-qom.h" #define TYPE_AST2700FC MACHINE_TYPE_NAME("ast2700fc") @@ -XXX,XX +XXX,XX @@ static struct arm_boot_info ast2700fc_board_info = { struct Ast2700FCState { MachineState parent_obj; + char *caliptra_rom; + char *caliptra_firmware; + MemoryRegion ca35_memory; MemoryRegion ca35_dram; MemoryRegion ca35_boot_rom; @@ -XXX,XX +XXX,XX @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) static void ast2700fc_init(MachineState *machine) { + Ast2700FCState *s = AST2700FC(machine); + ast2700fc_ca35_init(machine, &error_abort); ast2700fc_ssp_init(machine, &error_abort); ast2700fc_tsp_init(machine, &error_abort); + aspeed_caliptra_emu_start(serial_hd(0), s->caliptra_rom, + s->caliptra_firmware); +} + +static char *ast2700fc_get_caliptra_rom(Object *obj, Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + return g_strdup(s->caliptra_rom ? s->caliptra_rom : ""); +} + +static void ast2700fc_set_caliptra_rom(Object *obj, const char *value, + Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + g_free(s->caliptra_rom); + s->caliptra_rom = g_strdup(value); +} + +static char *ast2700fc_get_caliptra_firmware(Object *obj, Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + return g_strdup(s->caliptra_firmware ? s->caliptra_firmware : ""); +} + +static void ast2700fc_set_caliptra_firmware(Object *obj, const char *value, + Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + g_free(s->caliptra_firmware); + s->caliptra_firmware = g_strdup(value); +} + +static void ast2700fc_instance_finalize(Object *obj) +{ + Ast2700FCState *s = AST2700FC(obj); + + g_free(s->caliptra_rom); + g_free(s->caliptra_firmware); } static void ast2700fc_class_init(ObjectClass *oc, const void *data) @@ -XXX,XX +XXX,XX @@ static void ast2700fc_class_init(ObjectClass *oc, const void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->min_cpus = mc->max_cpus = mc->default_cpus = 6; + + object_class_property_add_str(oc, "caliptra-rom", + ast2700fc_get_caliptra_rom, + ast2700fc_set_caliptra_rom); + object_class_property_set_description(oc, "caliptra-rom", + "Caliptra ROM image used by the c-binding emulator backend"); + object_class_property_add_str(oc, "caliptra-firmware", + ast2700fc_get_caliptra_firmware, + ast2700fc_set_caliptra_firmware); + object_class_property_set_description(oc, "caliptra-firmware", + "Caliptra firmware bundle used by the c-binding emulator backend"); } static const TypeInfo ast2700fc_types[] = { @@ -XXX,XX +XXX,XX @@ static const TypeInfo ast2700fc_types[] = { .parent = TYPE_MACHINE, .class_init = ast2700fc_class_init, .instance_size = sizeof(Ast2700FCState), + .instance_finalize = ast2700fc_instance_finalize, .interfaces = aarch64_machine_interfaces, }, }; diff --git a/hw/arm/aspeed_caliptra_emu.c b/hw/arm/aspeed_caliptra_emu.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/aspeed_caliptra_emu.c @@ -XXX,XX +XXX,XX @@ +/* + * ASPEED Caliptra emulator backend + * + * Copyright (C) 2026 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "chardev/char.h" +#include "hw/arm/aspeed_caliptra_emu.h" +#include "qemu/error-report.h" + +#ifdef CONFIG_ASPEED_CALIPTRA_CBINDING +#include "qemu/main-loop.h" +#include "qemu/thread.h" +#include "system/runstate.h" + +#include "caliptra_model.h" +#endif + +#define CALIPTRA_EMU_PREFIX "[caliptra] " + +#ifdef CONFIG_ASPEED_CALIPTRA_CBINDING +#define CALIPTRA_RUNTIME_READY_BOOT_STATUS 0x600 + +typedef struct AspeedCaliptraEmuState { + QemuThread thread; + VMChangeStateEntry *vmstate; + struct caliptra_model *model; + Chardev *console; + char *rom_path; + char *firmware_path; + char *error; + bool at_start_of_line; + bool waiting_for_runtime; + bool startup_pause_done; +} AspeedCaliptraEmuState; + +static void caliptra_emu_write(AspeedCaliptraEmuState *s, + const uint8_t *buf, size_t len) +{ + g_autofree uint8_t *out = NULL; + size_t prefix_len = strlen(CALIPTRA_EMU_PREFIX); + size_t extra = 0; + size_t i; + size_t out_pos = 0; + + if (!s->console || len == 0) { + return; + } + + for (i = 0; i < len; i++) { + if (s->at_start_of_line || (i > 0 && buf[i - 1] == '\n')) { + extra += prefix_len; + } + } + + out = g_malloc(len + extra); + for (i = 0; i < len; i++) { + if (s->at_start_of_line) { + memcpy(out + out_pos, CALIPTRA_EMU_PREFIX, prefix_len); + out_pos += prefix_len; + s->at_start_of_line = false; + } + + out[out_pos++] = buf[i]; + if (buf[i] == '\n') { + s->at_start_of_line = true; + } + } + + qemu_chr_write_all(s->console, out, out_pos); +} + +static void caliptra_emu_note(AspeedCaliptraEmuState *s, const char *msg) +{ + if (!s->at_start_of_line) { + static const uint8_t newline = '\n'; + qemu_chr_write_all(s->console, &newline, 1); + s->at_start_of_line = true; + } + caliptra_emu_write(s, (const uint8_t *)msg, strlen(msg)); +} + +static bool caliptra_emu_read_file(const char *path, const char *name, + gchar **contents, gsize *len, char **error) +{ + g_autoptr(GError) err = NULL; + + if (!g_file_get_contents(path, contents, len, &err)) { + *error = g_strdup_printf("Failed to read Caliptra %s image '%s': %s", + name, path, err->message); + return false; + } + + return true; +} + +static void caliptra_emu_release_vm(AspeedCaliptraEmuState *s, + const char *reason) +{ + if (!s->waiting_for_runtime) { + return; + } + + s->waiting_for_runtime = false; + caliptra_emu_note(s, reason); + if (s->startup_pause_done) { + vm_start(); + } +} + +static void caliptra_emu_vm_state_change(void *opaque, bool running, + RunState state) +{ + AspeedCaliptraEmuState *s = opaque; + + if (!running || !s->waiting_for_runtime || s->startup_pause_done) { + return; + } + + s->startup_pause_done = true; + /* + * FIXME: RFC ONLY - Using vm_stop_force_state is a hack. + * We need advice on the proper way to hold main CPUs in reset + * until the Caliptra thread finishes booting. + */ + vm_stop_force_state(RUN_STATE_PAUSED); +} + +static void caliptra_emu_boot_done_bh(void *opaque) +{ + AspeedCaliptraEmuState *s = opaque; + + if (s->error) { + error_report("%s", s->error); + caliptra_emu_release_vm(s, + "runtime boot failed, releasing CA35\n"); + } else { + caliptra_emu_release_vm(s, "runtime ready, releasing CA35\n"); + } + + qemu_del_vm_change_state_handler(s->vmstate); + if (s->model) { + caliptra_model_destroy(s->model); + } + g_free(s->rom_path); + g_free(s->firmware_path); + g_free(s->error); + g_free(s); +} + +static void *caliptra_emu_thread(void *opaque) +{ + AspeedCaliptraEmuState *s = opaque; + g_autofree gchar *rom = NULL; + g_autofree gchar *firmware = NULL; + gsize rom_len = 0; + gsize firmware_len = 0; + static const uint8_t empty; + struct caliptra_model_init_params params; + int rc; + + if (!caliptra_emu_read_file(s->rom_path, "ROM", &rom, &rom_len, + &s->error) || + !caliptra_emu_read_file(s->firmware_path, "firmware", &firmware, + &firmware_len, &s->error)) { + goto out; + } + + params = (struct caliptra_model_init_params) { + .rom = { + .data = (const uint8_t *)rom, + .len = rom_len, + }, + .dccm = { + .data = &empty, + .len = 0, + }, + .iccm = { + .data = &empty, + .len = 0, + }, + .security_state = CALIPTRA_SEC_STATE_DBG_UNLOCKED_UNPROVISIONED, + }; + + rc = caliptra_model_init_default(params, &s->model); + if (rc != CALIPTRA_MODEL_STATUS_OK) { + s->error = g_strdup_printf( + "Failed to initialize Caliptra c-binding model: %d", rc); + goto out; + } + + rc = caliptra_model_boot_default(s->model, + (struct caliptra_buffer) { + .data = (const uint8_t *)firmware, + .len = firmware_len, + }, + CALIPTRA_RUNTIME_READY_BOOT_STATUS); + if (rc != CALIPTRA_MODEL_STATUS_OK) { + s->error = g_strdup_printf("Failed to boot Caliptra runtime: %d", rc); + goto out; + } + +out: + aio_bh_schedule_oneshot(qemu_get_aio_context(), caliptra_emu_boot_done_bh, + s); + return NULL; +} + +void aspeed_caliptra_emu_start(Chardev *console, const char *rom_path, + const char *firmware_path) +{ + AspeedCaliptraEmuState *s; + + if (!rom_path && !firmware_path) { + return; + } + + if (!rom_path || !rom_path[0] || !firmware_path || !firmware_path[0]) { + error_report("Both caliptra-rom and caliptra-firmware must be set"); + return; + } + + if (!console) { + error_report("Caliptra emulator backend is set " + "but serial0 is unavailable"); + return; + } + + s = g_new0(AspeedCaliptraEmuState, 1); + s->console = console; + s->rom_path = g_strdup(rom_path); + s->firmware_path = g_strdup(firmware_path); + s->at_start_of_line = true; + s->waiting_for_runtime = true; + + s->vmstate = qemu_add_vm_change_state_handler(caliptra_emu_vm_state_change, + s); + caliptra_emu_note(s, "starting c-binding model thread\n"); + caliptra_emu_note(s, "waiting for runtime ready before starting CA35\n"); + + qemu_thread_create(&s->thread, "caliptra-emu", caliptra_emu_thread, s, + QEMU_THREAD_DETACHED); +} +#else +void aspeed_caliptra_emu_start(Chardev *console, const char *rom_path, + const char *firmware_path) +{ + (void)console; + + if (!rom_path && !firmware_path) { + return; + } + + error_report("Caliptra c-binding backend was not compiled in"); +} +#endif diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_ss = ss.source_set() arm_common_ss = ss.source_set() + +# RFC ONLY: this local-tree probe is for PoC discussion. A mergeable version +# needs an explicit configure option and a proper dependency integration. +caliptra_cbinding_root = meson.project_source_root() / '..' / 'caliptra-sw' +caliptra_cbinding_inc = caliptra_cbinding_root / 'hw-model/c-binding/out' +caliptra_cbinding_header = caliptra_cbinding_inc / 'caliptra_model.h' +caliptra_cbinding_incdir = include_directories( + '../../../caliptra-sw/hw-model/c-binding/out') +caliptra_cbinding_lib = caliptra_cbinding_root / 'target' / 'debug' +caliptra_cbinding_lib_name = 'libcaliptra_hw_model_c_binding.a' +caliptra_cbinding_lib = caliptra_cbinding_lib / caliptra_cbinding_lib_name +caliptra_cbinding_dep = [] +caliptra_cbinding_found = fs.exists(caliptra_cbinding_header) +if caliptra_cbinding_found + caliptra_cbinding_found = fs.exists(caliptra_cbinding_lib) +endif +if caliptra_cbinding_found + caliptra_cbinding_dep = declare_dependency( + compile_args: ['-DCONFIG_ASPEED_CALIPTRA_CBINDING'], + include_directories: caliptra_cbinding_incdir, + link_args: [ + caliptra_cbinding_lib, + '-lstdc++', + '-ldl', + '-lrt', + '-lm', + ]) +endif arm_common_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_common_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( 'aspeed_ast1700.c', 'aspeed_ast27x0.c', 'aspeed_ast27x0_evb.c', + 'aspeed_caliptra_emu.c', 'aspeed_ast27x0-fc.c', 'aspeed_ast27x0-ssp.c', 'aspeed_ast27x0-tsp.c', - 'aspeed_coprocessor_common.c')) + 'aspeed_coprocessor_common.c') + [caliptra_cbinding_dep]) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_common_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) -- 2.43.0
Hi, This series is an RFC for modelling the Caliptra Root of Trust found in the ASPEED ast27x0 family. ast27x0 integrates a Caliptra 1.2 core as an on-die RoT block. From the host side, the visible interface is a Caliptra APB register window rather than a standardized external command protocol. QEMU does not currently have a Caliptra emulator, and there is not yet an agreed upstream model for how ASPEED Caliptra support should be structured. On the real ast27x0, Caliptra bring-up and secure-boot orchestration are handled by bootmcu rather than by the CA35 host CPUs. In particular, bootmcu is expected to own the mailbox-driven interaction with the Caliptra core during firmware loading and verification. At a high level: - bootmcu releases the Caliptra core from reset - bootmcu loads the Caliptra FMC/runtime firmware through the mailbox path - Caliptra verifies the images and boots to runtime-ready - bootmcu then relies on Caliptra services as part of the platform secure-boot flow before CA35 firmware starts QEMU does not currently model bootmcu or that orchestration flow. Accordingly, this RFC does not attempt to model the final ast27x0 boot architecture. Instead, it uses the CA35-visible Caliptra MMIO/APB window as an initial integration point to validate the transport/backend split. A subsequent revision would wire this into the proper bootmcu-owned boot path. Changes since RFC v1: - reworked the PoC to remove the direct `caliptra-sw` C-binding dependency from the QEMU build. - dropped the sibling-tree auto-detection of external headers and libraries. - replaced the in-process Caliptra model with an external `caliptra-server` backend reached over a Unix socket. - replaced the v1 worker-thread-based boot sequencing with a model where the backend is started independently and is already runtime-ready when QEMU connects. - removed the use of `vm_stop_force_state()` to hold the CA35 until Caliptra boot completion. - narrowed the QEMU-side PoC to the CA35-visible Caliptra MMIO/APB window on ast2700fc machine. The patch in this RFC: - adds an ASPEED-specific sysbus device for Caliptra emulation. - exposes a 128 KiB MMIO window at 0x14c60000 on ast2700fc machine. - forwards guest MMIO accesses to an external backend as APB read/write transactions over a Unix socket. - assumes the backend is already running and ready before QEMU starts. Current limitations of the PoC: - no boot handshake between QEMU and the backend. - synchronous socket round-trips on guest register access. - no reset/migration/lifecycle integration. - minimal wire protocol, designed to be replaced with a standardized IPC scheme. - no modeling of bootmcu or the full secure-boot orchestration flow. Testing: # Terminal 1: start the external backend and wait for runtime-ready. caliptra-server \ --socket /tmp/caliptra.sock \ --rom images/caliptra-rom.bin \ --firmware images/caliptra-fw.bundle # The server boots caliptra-sw independently, then prints that it is # listening on the socket after boot_status reaches runtime-ready. # Terminal 2: launch QEMU after the socket is ready. qemu-system-aarch64 \ -M ast2700fc,caliptra-socket=/tmp/caliptra.sock \ ... # In the guest, exercise the Caliptra MMIO window and confirm the # APB transactions are forwarded to caliptra-server. # # The sequence below drives the mailbox CSR block at 0x30020000 # through the AST2700fc window: # 0x00 LOCK - confirm the mailbox CSR block is reachable # 0x08 CMD - program the mailbox command opcode # 0x0c DLEN - declare the payload length # 0x10 DATAIN - provide the checksum word required by CAPS # 0x18 EXECUTE - doorbell to start command processing # 0x1c STATUS - read back the command result md 0x14c60000 1 # Read LOCK to confirm the Caliptra mailbox CSR window is accessible. mw 0x14c60008 0x43415053 # Write CMD = OP_CAPABILITIES ("CAPS"). mw 0x14c6000c 4 # Write DLEN = 4 bytes; CAPABILITIES carries only the checksum word. mw 0x14c60010 0xFFFFFED9 # Write the checksum word expected by caliptra-sw for this CAPS request. mw 0x14c60018 1 # Write EXECUTE = 1 to kick off mailbox command processing. md 0x14c6001c 1 # Read STATUS to verify the command completed successfully. The backend used for evaluation is currently based on the `caliptra-1.2` branch of: https://github.com/chipsalliance/caliptra-sw The reference `caliptra-server` implementation used in this RFC is available at: https://github.com/stevenlee7189/caliptra-server The intent here is not to propose this PoC as merge-ready. The goal is to get feedback on whether this general direction makes sense for ASPEED. In particular, feedback is welcome on the following: - Is a socket-connected external backend an acceptable direction for supporting a Caliptra core on ASPEED platforms, or should we pursue an alternative approach such as an in-process model or a different IPC mechanism? Other comments on the design are also welcome. Thanks, Steven Lee Steven Lee (1): hw/arm: aspeed: PoC integration for Caliptra Root of Trust MAINTAINERS | 2 + docs/system/arm/aspeed.rst | 35 +++ include/hw/arm/aspeed_caliptra_emu.h | 29 +++ hw/arm/aspeed_ast27x0-fc.c | 55 +++++ hw/arm/aspeed_caliptra_emu.c | 338 +++++++++++++++++++++++++++ hw/arm/meson.build | 1 + 6 files changed, 460 insertions(+) create mode 100644 include/hw/arm/aspeed_caliptra_emu.h create mode 100644 hw/arm/aspeed_caliptra_emu.c -- 2.43.0
ast27x0 integrates a Caliptra Root of Trust core, but QEMU does not currently have an agreed model for ASPEED Caliptra emulation. This patch adds an ASPEED-specific sysbus device that exposes the Caliptra mailbox/SOC-IFC MMIO window and forwards guest register accesses to an external `caliptra-server` backend over a Unix socket. This allows evaluation of the QEMU/Caliptra integration shape without pulling the upstream `caliptra-sw` hw-model into the QEMU build. The reference `caliptra-server` implementation used in this RFC is available at: https://github.com/stevenlee7189/caliptra-server The device provides a 128 KiB MMIO window at 0x14c60000, matching the ast27x0 bootmcu Caliptra mailbox node, and maps it onto the backend APB register space. https://github.com/AspeedTech-BMC/zephyr/blob/v00.03.07/dts/riscv/aspeed/ast27xx.dtsi#L310-L312 On the real ast27x0, Caliptra bring-up and secure-boot orchestration are handled by bootmcu; CA35 is not involved in that path. This patch uses the CA35-visible Caliptra MMIO window as an initial integration point to validate the socket-backed external backend approach, not to model the final boot architecture. This is an RFC PoC only. It assumes the backend is already running, does not implement boot/reset handshaking or migration support, and uses a minimal wire protocol designed to be replaced with a standardized IPC scheme. Signed-off-by: Steven Lee <steven_lee@aspeedtech.com> --- MAINTAINERS | 2 + docs/system/arm/aspeed.rst | 35 +++ include/hw/arm/aspeed_caliptra_emu.h | 29 +++ hw/arm/aspeed_ast27x0-fc.c | 55 +++++ hw/arm/aspeed_caliptra_emu.c | 338 +++++++++++++++++++++++++++ hw/arm/meson.build | 1 + 6 files changed, 460 insertions(+) create mode 100644 include/hw/arm/aspeed_caliptra_emu.h create mode 100644 hw/arm/aspeed_caliptra_emu.c diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/*aspeed* F: include/hw/*/*aspeed* +F: hw/arm/aspeed_caliptra_emu.c +F: include/hw/arm/aspeed_caliptra_emu.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -XXX,XX +XXX,XX @@ Steps to boot the AST2700fc machine: -snapshot \ -S -nographic +The ``ast2700fc`` machine can also forward CA35 accesses to the on-die +Caliptra Root-of-Trust core to an external Caliptra backend over a Unix +socket. The backend (``caliptra-server``) is launched independently with +the Caliptra ROM and firmware bundle, boots the Caliptra emulated model +to runtime-ready, then listens on a Unix socket. QEMU connects to that +socket from the device's ``realize()`` and forwards every CA35 read/write +in the Caliptra MMIO window as an APB transaction on the socket. + +The socket path is configured with the ``caliptra-socket`` machine +property. If the property is not set, no Caliptra backend is attached +and the Caliptra MMIO window is left unmapped. + +For example, to run Caliptra firmware from ``caliptra-sw``: + +.. code-block:: bash + + # Terminal 1: start caliptra-server with the ROM/firmware images. + # It boots the model to runtime-ready before opening the socket. + $ caliptra-server \ + --socket /tmp/caliptra.sock \ + --rom images/caliptra-rom-with-log.bin \ + --firmware images/caliptra-fw.bundle + + # Terminal 2: launch QEMU; the CA35 starts running immediately. + $ qemu-system-aarch64 \ + -M ast2700fc,caliptra-socket=/tmp/caliptra.sock \ + ... + +There is no boot handshake between QEMU and the backend: by the time the +device's ``realize()`` runs, ``caliptra-server`` is already at +runtime-ready, so the CA35 is not held in reset. The wire protocol +between QEMU and the backend is APB-level: ``APB_READ`` / ``APB_WRITE`` +requests from QEMU are answered by ``APB_RDATA`` / ``APB_WACK`` from the +backend. This protocol is RFC-stage and may change. + After launching QEMU, serial devices will be automatically redirected. Example output: diff --git a/include/hw/arm/aspeed_caliptra_emu.h b/include/hw/arm/aspeed_caliptra_emu.h new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/include/hw/arm/aspeed_caliptra_emu.h @@ -XXX,XX +XXX,XX @@ +/* + * ASPEED Caliptra emulator backend + * + * Copyright (C) 2026 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ASPEED_CALIPTRA_EMU_H +#define ASPEED_CALIPTRA_EMU_H + +#define TYPE_ASPEED_CALIPTRA_EMU "aspeed-caliptra-emu" + +/* + * RFC/PoC integration hook only. + * + * This address is used by the current QEMU experiment to attach the + * external Caliptra backend to the AST2700fc machine. It must not be + * interpreted as the final functional interface used by real hardware: + * early Caliptra interaction is bootmcu-owned, and CA35 does not use + * this path for functional access to the Caliptra core. + * + * The 128 KiB region covers: + * +0x00000 mailbox CSR (caliptra APB 0x30020000) + * +0x10000 SOC IFC (caliptra APB 0x30030000) + */ +#define ASPEED_CALIPTRA_MMIO_BASE 0x14c60000ULL + +#endif /* ASPEED_CALIPTRA_EMU_H */ diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -XXX,XX +XXX,XX @@ #include "hw/arm/boot.h" #include "hw/block/flash.h" #include "hw/arm/aspeed_coprocessor.h" +#include "hw/arm/aspeed_caliptra_emu.h" +#include "hw/core/sysbus.h" #include "hw/arm/machines-qom.h" #define TYPE_AST2700FC MACHINE_TYPE_NAME("ast2700fc") @@ -XXX,XX +XXX,XX @@ static struct arm_boot_info ast2700fc_board_info = { struct Ast2700FCState { MachineState parent_obj; + char *caliptra_socket; + MemoryRegion ca35_memory; MemoryRegion ca35_dram; MemoryRegion ca35_boot_rom; @@ -XXX,XX +XXX,XX @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) static void ast2700fc_init(MachineState *machine) { + Ast2700FCState *s = AST2700FC(machine); + ast2700fc_ca35_init(machine, &error_abort); ast2700fc_ssp_init(machine, &error_abort); ast2700fc_tsp_init(machine, &error_abort); + + if (s->caliptra_socket) { + SysBusDevice *sbd; + DeviceState *dev = qdev_new(TYPE_ASPEED_CALIPTRA_EMU); + + qdev_prop_set_string(dev, "socket-path", s->caliptra_socket); + sbd = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbd, &error_abort); + /* + * Map the Caliptra APB MMIO region into the CA35 address space. + * 0x14000000..0x14FFFFFF. + */ + memory_region_add_subregion_overlap(&s->ca35_memory, + ASPEED_CALIPTRA_MMIO_BASE, + sysbus_mmio_get_region(sbd, 0), + 1); + } +} + +static char *ast2700fc_get_caliptra_socket(Object *obj, Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + return g_strdup(s->caliptra_socket ? s->caliptra_socket : ""); +} + +static void ast2700fc_set_caliptra_socket(Object *obj, const char *value, + Error **errp) +{ + Ast2700FCState *s = AST2700FC(obj); + + g_free(s->caliptra_socket); + s->caliptra_socket = g_strdup(value); +} + +static void ast2700fc_instance_finalize(Object *obj) +{ + Ast2700FCState *s = AST2700FC(obj); + + g_free(s->caliptra_socket); } static void ast2700fc_class_init(ObjectClass *oc, const void *data) @@ -XXX,XX +XXX,XX @@ static void ast2700fc_class_init(ObjectClass *oc, const void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->min_cpus = mc->max_cpus = mc->default_cpus = 6; + + object_class_property_add_str(oc, "caliptra-socket", + ast2700fc_get_caliptra_socket, + ast2700fc_set_caliptra_socket); + object_class_property_set_description(oc, "caliptra-socket", + "Unix socket path for the external Caliptra backend (caliptra-server). " + "caliptra-server is launched independently with the ROM/firmware images " + "and must be listening before QEMU is started."); } static const TypeInfo ast2700fc_types[] = { @@ -XXX,XX +XXX,XX @@ static const TypeInfo ast2700fc_types[] = { .parent = TYPE_MACHINE, .class_init = ast2700fc_class_init, .instance_size = sizeof(Ast2700FCState), + .instance_finalize = ast2700fc_instance_finalize, .interfaces = aarch64_machine_interfaces, }, }; diff --git a/hw/arm/aspeed_caliptra_emu.c b/hw/arm/aspeed_caliptra_emu.c new file mode 100644 index XXXXXXX..XXXXXXX --- /dev/null +++ b/hw/arm/aspeed_caliptra_emu.c @@ -XXX,XX +XXX,XX @@ +/* + * ASPEED Caliptra external backend (PoC) + * + * Forwards CA35 MMIO accesses on the Caliptra APB window to an external + * caliptra-server process over a Unix socket. caliptra-server is expected + * to be started independently with the Caliptra ROM and firmware bundle + * and to be listening on socket-path before QEMU is launched. + * + * Copyright (C) 2026 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/aspeed_caliptra_emu.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "qapi/error.h" +#include "qemu/bswap.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/units.h" +#include "system/memory.h" + +/* + * Wire protocol (little-endian). Symmetric with caliptra-server. + * + * Header (12 bytes): + * u32 magic -- 0x43505452 ("CPTR") + * u16 version -- 1 + * u16 command + * u32 payload_len + * + * Commands (after the connection is established): + * APB_READ QEMU -> server {u32 apb_addr} + * APB_RDATA server -> QEMU {u32 data} + * APB_WRITE QEMU -> server {u32 apb_addr, u32 data} + * APB_WACK server -> QEMU {} (empty payload) + * + * There is no boot handshake: caliptra-server boots its emulated model to + * runtime-ready before listen()/accept(), and QEMU just connects and starts + * forwarding APB traffic. + */ +#define CALIPTRA_SOCKET_MAGIC 0x43505452 +#define CALIPTRA_SOCKET_VERSION 1 +#define CALIPTRA_SOCKET_CMD_APB_READ 3 +#define CALIPTRA_SOCKET_CMD_APB_RDATA 4 +#define CALIPTRA_SOCKET_CMD_APB_WRITE 5 +#define CALIPTRA_SOCKET_CMD_APB_WACK 6 +#define CALIPTRA_SOCKET_MAX_PAYLOAD (4 * MiB) + +/* + * Base address of the Caliptra APB register space as seen by caliptra-hw-model. + * Mailbox CSR starts at 0x30020000; SOC IFC starts at 0x30030000. + * Total mapped size: 0x20000 (128 KiB). + * + * MMIO offset from ASPEED_CALIPTRA_MMIO_BASE in the CA35 address space is + * mapped 1:1 to APB address CALIPTRA_APB_BASE + offset. + */ +#define CALIPTRA_APB_BASE 0x30020000 +#define CALIPTRA_MMIO_SIZE 0x20000 + +typedef struct QEMU_PACKED CaliptraSocketHeader { + uint32_t magic; + uint16_t version; + uint16_t command; + uint32_t payload_len; +} CaliptraSocketHeader; + +OBJECT_DECLARE_SIMPLE_TYPE(AspeedCaliptraEmuState, ASPEED_CALIPTRA_EMU) + +struct AspeedCaliptraEmuState { + SysBusDevice parent_obj; + char *socket_path; + QIOChannelSocket *sioc; + QemuMutex ioc_mutex; + MemoryRegion mmio; +}; + +static bool caliptra_socket_write_msg(QIOChannel *ioc, uint16_t command, + const uint8_t *payload, size_t len, + Error **errp) +{ + CaliptraSocketHeader hdr = { + .magic = cpu_to_le32(CALIPTRA_SOCKET_MAGIC), + .version = cpu_to_le16(CALIPTRA_SOCKET_VERSION), + .command = cpu_to_le16(command), + .payload_len = cpu_to_le32(len), + }; + + if (qio_channel_write_all(ioc, (const char *)&hdr, sizeof(hdr), errp) < 0) { + return false; + } + if (len && + qio_channel_write_all(ioc, (const char *)payload, len, errp) < 0) { + return false; + } + return true; +} + +static bool caliptra_socket_read_header(QIOChannel *ioc, uint16_t command, + CaliptraSocketHeader *hdr, + Error **errp) +{ + if (qio_channel_read_all(ioc, (char *)hdr, sizeof(*hdr), errp) < 0) { + return false; + } + + hdr->magic = le32_to_cpu(hdr->magic); + hdr->version = le16_to_cpu(hdr->version); + hdr->command = le16_to_cpu(hdr->command); + hdr->payload_len = le32_to_cpu(hdr->payload_len); + + if (hdr->magic != CALIPTRA_SOCKET_MAGIC) { + error_setg(errp, "Invalid Caliptra socket magic 0x%08x", hdr->magic); + return false; + } + if (hdr->version != CALIPTRA_SOCKET_VERSION) { + error_setg(errp, "Unsupported Caliptra socket version %u", + hdr->version); + return false; + } + if (hdr->command != command) { + error_setg(errp, "Unexpected Caliptra socket command %u (expected %u)", + hdr->command, command); + return false; + } + if (hdr->payload_len > CALIPTRA_SOCKET_MAX_PAYLOAD) { + error_setg(errp, "Caliptra socket response too large: %u bytes", + hdr->payload_len); + return false; + } + return true; +} + +static bool caliptra_apb_send_recv(AspeedCaliptraEmuState *s, + uint16_t req_cmd, uint16_t rsp_cmd, + const uint8_t *req_payload, size_t req_len, + uint8_t *rsp_buf, size_t rsp_len, + Error **errp) +{ + CaliptraSocketHeader hdr; + QIOChannel *ioc; + + if (!s->sioc) { + error_setg(errp, "Caliptra APB access with no backend connection"); + return false; + } + ioc = QIO_CHANNEL(s->sioc); + + if (!caliptra_socket_write_msg(ioc, req_cmd, req_payload, req_len, errp)) { + return false; + } + if (!caliptra_socket_read_header(ioc, rsp_cmd, &hdr, errp)) { + return false; + } + + if (rsp_len) { + if (hdr.payload_len < rsp_len) { + error_setg(errp, + "Caliptra short response for cmd %u: got %u, expected %zu", + rsp_cmd, hdr.payload_len, rsp_len); + return false; + } + if (qio_channel_read_all(ioc, (char *)rsp_buf, rsp_len, errp) < 0) { + return false; + } + /* Discard any trailing bytes. */ + if (hdr.payload_len > rsp_len) { + g_autofree uint8_t *extra = g_malloc(hdr.payload_len - rsp_len); + if (qio_channel_read_all(ioc, (char *)extra, + hdr.payload_len - rsp_len, errp) < 0) { + return false; + } + } + } else if (hdr.payload_len) { + g_autofree uint8_t *extra = g_malloc(hdr.payload_len); + if (qio_channel_read_all(ioc, (char *)extra, + hdr.payload_len, errp) < 0) { + return false; + } + } + return true; +} + +static uint64_t caliptra_mmio_read(void *opaque, hwaddr offset, unsigned size) +{ + AspeedCaliptraEmuState *s = opaque; + uint8_t req[4], rsp[4] = {}; + Error *err = NULL; + + stl_le_p(req, CALIPTRA_APB_BASE + (uint32_t)offset); + + /* + * Release the BQL before blocking on the socket so the QEMU event loop + * stays responsive while caliptra-server processes the request. + */ + bql_unlock(); + qemu_mutex_lock(&s->ioc_mutex); + caliptra_apb_send_recv(s, + CALIPTRA_SOCKET_CMD_APB_READ, + CALIPTRA_SOCKET_CMD_APB_RDATA, + req, sizeof(req), + rsp, sizeof(rsp), &err); + qemu_mutex_unlock(&s->ioc_mutex); + bql_lock(); + + if (err) { + error_report("caliptra APB read 0x%08" PRIx32 ": %s", + CALIPTRA_APB_BASE + (uint32_t)offset, + error_get_pretty(err)); + error_free(err); + return 0xdeadbeef; + } + return ldl_le_p(rsp); +} + +static void caliptra_mmio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedCaliptraEmuState *s = opaque; + uint8_t req[8]; + Error *err = NULL; + + stl_le_p(req + 0, CALIPTRA_APB_BASE + (uint32_t)offset); + stl_le_p(req + 4, (uint32_t)value); + + bql_unlock(); + qemu_mutex_lock(&s->ioc_mutex); + caliptra_apb_send_recv(s, + CALIPTRA_SOCKET_CMD_APB_WRITE, + CALIPTRA_SOCKET_CMD_APB_WACK, + req, sizeof(req), + NULL, 0, &err); + qemu_mutex_unlock(&s->ioc_mutex); + bql_lock(); + + if (err) { + error_report("caliptra APB write 0x%08" PRIx32 " = 0x%08" PRIx32 ": %s", + CALIPTRA_APB_BASE + (uint32_t)offset, (uint32_t)value, + error_get_pretty(err)); + error_free(err); + } +} + +static const MemoryRegionOps caliptra_mmio_ops = { + .read = caliptra_mmio_read, + .write = caliptra_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void aspeed_caliptra_emu_realize(DeviceState *dev, Error **errp) +{ + AspeedCaliptraEmuState *s = ASPEED_CALIPTRA_EMU(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SocketAddress addr; + QIOChannelSocket *sioc; + + if (!s->socket_path || !s->socket_path[0]) { + error_setg(errp, "caliptra-emu: socket-path must be set"); + return; + } + + memory_region_init_io(&s->mmio, OBJECT(s), &caliptra_mmio_ops, s, + "caliptra-apb", CALIPTRA_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + /* + * Connect to caliptra-server synchronously. The server is expected to be + * already listening with the Caliptra ROM/firmware booted to runtime-ready. + */ + memset(&addr, 0, sizeof(addr)); + addr.type = SOCKET_ADDRESS_TYPE_UNIX; + addr.u.q_unix.path = s->socket_path; + + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, &addr, errp) < 0) { + object_unref(OBJECT(sioc)); + return; + } + if (!qio_channel_set_blocking(QIO_CHANNEL(sioc), true, errp)) { + object_unref(OBJECT(sioc)); + return; + } + s->sioc = sioc; +} + +static void aspeed_caliptra_emu_instance_init(Object *obj) +{ + AspeedCaliptraEmuState *s = ASPEED_CALIPTRA_EMU(obj); + + qemu_mutex_init(&s->ioc_mutex); +} + +static void aspeed_caliptra_emu_finalize(Object *obj) +{ + AspeedCaliptraEmuState *s = ASPEED_CALIPTRA_EMU(obj); + + if (s->sioc) { + object_unref(OBJECT(s->sioc)); + } + qemu_mutex_destroy(&s->ioc_mutex); +} + +static const Property aspeed_caliptra_emu_props[] = { + DEFINE_PROP_STRING("socket-path", AspeedCaliptraEmuState, socket_path), +}; + +static void aspeed_caliptra_emu_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = aspeed_caliptra_emu_realize; + dc->desc = "ASPEED Caliptra external backend (PoC)"; + device_class_set_props(dc, aspeed_caliptra_emu_props); + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo aspeed_caliptra_emu_type_info = { + .name = TYPE_ASPEED_CALIPTRA_EMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedCaliptraEmuState), + .instance_init = aspeed_caliptra_emu_instance_init, + .instance_finalize = aspeed_caliptra_emu_finalize, + .class_init = aspeed_caliptra_emu_class_init, +}; + +static void aspeed_caliptra_emu_register_type(void) +{ + type_register_static(&aspeed_caliptra_emu_type_info); +} +type_init(aspeed_caliptra_emu_register_type) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index XXXXXXX..XXXXXXX 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -XXX,XX +XXX,XX @@ arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( 'aspeed_ast1700.c', 'aspeed_ast27x0.c', 'aspeed_ast27x0_evb.c', + 'aspeed_caliptra_emu.c', 'aspeed_ast27x0-fc.c', 'aspeed_ast27x0-ssp.c', 'aspeed_ast27x0-tsp.c', -- 2.43.0