## Goal The goal is to specify the minimum useful design for a combined `qemu-system` binary and use it as a basis for iteration, not to define multi-target support for every QEMU machine, CPU, and device.
The prototype combines two configured `SysEmuTarget`s that provide machines with the same public name, `virt` (for example, AArch64 and RISC-V). Its first runnable case is concrete: start AArch64 `virt` with CPU `max` using an explicit target: ```text qemu-system -target aarch64 -machine virt -cpu max ``` It also defines what happens before a target is chosen. With target `none`, QMP can inspect the complete machine inventory, and each `MachineInfo` reports its supported targets. The CLI must not guess which `virt` to run; it reports the targets that provide that name. Inferring a target from `argv[0]` is deferred because the current build does not provide target-named symlinks to the combined `qemu-system` binary. Existing per-target binaries and their familiar machine names remain unchanged. Implementation branches: - [single-binary-arm-riscv](https://gitlab.com/lygstate-qemu/qemu/-/tree/single-binary-arm-riscv?ref_type=heads) combines `none-softmmu`, `aarch64-softmmu`, `arm-softmmu`, and `microblaze-softmmu`. [CI pipeline for single-binary-arm-riscv](https://gitlab.com/lygstate-qemu/qemu/-/pipelines/2906623802). - [combined-binary-multi-targets](https://gitlab.com/lygstate-qemu/qemu/-/tree/combined-binary-multi-targets?ref_type=heads) extends the combined target set with `riscv32-softmmu`, `riscv64-softmmu`, `i386-softmmu`, and `x86_64-softmmu`. [CI pipeline for combined-binary-multi-targets](https://gitlab.com/lygstate-qemu/qemu/-/pipelines/2906685964). ## Design There are three possible policies for deciding which target a combined emulator should run: 1. **Infer from `argv[0]`.** A target-named symlink to the combined `qemu-system` binary could provide a target-specific invocation name and allow target selection from `argv[0]`. Defer this policy: the current build does not provide those symlinks, so there is no target-specific invocation name to inspect. 2. **Require an explicit `-target` for execution.** This is the current selection mechanism. Parse `-target` early, select its `TargetInfo` before module and QOM initialization, then resolve `-machine` and `-cpu` within that target. For example: ```text qemu-system -target aarch64 -machine virt -cpu max -S -display none ``` This makes target choice explicit when starting a target-specific machine; it does not prevent a target-neutral inspection mode. 1. **Expose no target-specific machine for execution while the target is `none`.** Do not guess which same-named machine to start. This is not a policy of hiding machines from management clients: `query-machines` still reports the combined inventory and each machine's supported targets. The implementation below combines the second and third policies. The first can be considered later without changing the inventory contract. The design has three stages: register the combined type set, use target availability when resolving a requested machine, and expose the complete inventory through QMP. ### 1. Combined Registration Before QOM Hooks Before the optional QOM hooks described below, `type_register_internal()` skipped availability filtering in the combined binary and retained `TypeInfo`s from all configured targets. Single-target binaries continued to filter each type against the selected target during registration. Retaining the full type set lets the target-`none` inventory report machines and the targets that provide them. `type_init()` functions are collected before they run, so `TypeInfo` registration is not guaranteed to be parent-first. A child may register before its parent. When a `TypeInfo` is registered, `type_new()` copies its `is_available` function pointer into the new `TypeImpl`. The registered type retains the callback value supplied by that `TypeInfo` for later processing. ### 2. Resolve Machine Names Without Guessing For a selected target, normal machine lookup sees only machine classes available to that target. If lookup fails, the error path scans the complete registered machine list and evaluates each matching name or alias against all registered `TargetInfo` values. For example, the existing qtest exercises the `none` case. The target is selected as `none`, but machine registration has retained the target-specific classes, allowing the error path to explain where `virt` is available: ```text $ qemu-system -machine virt unsupported machine type: "virt" for target "none" Available for targets: aarch64 arm Use -machine help to list supported machines ``` The command fails rather than selecting an arbitrary `virt`. This also lets users of a target-specific binary continue to use the familiar machine name; the combined binary's explicit target path remains available when starting a machine: ```text qemu-system -target aarch64 -machine virt -cpu max -S -display none ``` Here `-target aarch64` selects the target before QOM and machine setup, and `-S` leaves the guest paused. This is an example of the existing target selection path, not a new CLI proposal in this RFC. ### 3. Report the Combined Inventory `query-machines` iterates over the registered machine classes. For each class, it returns the public machine `name`, the actual QOM `typename`, and every configured target for which the class's availability callback returns true. This allows clients to distinguish machines that share a public name and look up the right QOM type without deriving it from `-M`. An abbreviated response for a combined build containing ARM-family and RISC-V targets can look like this; the exact records depend on the configured targets and machines: ```json { "return": [ { "name": "virt", "typename": "arm-virt-machine", "targets": [{ "arch": "aarch64" }, { "arch": "arm" }] }, { "name": "virt", "typename": "riscv-virt-machine", "targets": [{ "arch": "riscv32" }, { "arch": "riscv64" }] } ] } ``` A client can then use the returned QOM type directly: ```json { "execute": "qom-list-properties", "arguments": { "typename": "arm-virt-machine" } } ``` This RFC describes inventory and diagnostics; it does not change the target selection CLI or propose globally filtering devices. ### Optional: QOM before/after hooks The hooks are an optional implementation of post-registration processing: they run around `MODULE_INIT_QOM` to validate parent chains after registration batches. The after-hook walks the full registered-type set, validates parent links, and inherits each missing `is_available` callback from its parent. `TYPE_OBJECT` and `TYPE_INTERFACE` are distinct roots; each root's `TypeInfo` explicitly sets the callback to `target_is_any`. A child may provide its own callback or leave it NULL to inherit from its parent. The completed pass therefore gives every registered type an effective callback before filtering. Filtering is optional; when used, it runs after registration and parent validation, reclaiming runtime QOM metadata but not compiled code. #### ARM and AArch64 example The unit test registers a shared ARM-family parent, narrower ARM and AArch64 parents, and callback-free children. It uses `TYPE_OBJECT` as the test root: ```c #define TYPE_ARM_BASE_MACHINE_FAMILY "test-avail-arm-base-machine-family" #define TYPE_VIRT_MACHINE_VERSION "test-avail-virt-machine-version" #define TYPE_ARM_MACHINE_FAMILY "test-avail-arm-machine-family" #define TYPE_ARM_MACHINE "test-avail-arm-machine" #define TYPE_AARCH64_MACHINE_FAMILY "test-avail-aarch64-machine-family" #define TYPE_AARCH64_MACHINE "test-avail-aarch64-machine" static const TypeInfo arm_base_machine_info = { .name = TYPE_ARM_BASE_MACHINE_FAMILY, .parent = TYPE_OBJECT, .abstract = true, .is_available = target_is_base_arm, }; static const TypeInfo virt_machine_version_info = { .name = TYPE_VIRT_MACHINE_VERSION, .parent = TYPE_ARM_BASE_MACHINE_FAMILY, }; static const TypeInfo arm_machine_family_info = { .name = TYPE_ARM_MACHINE_FAMILY, .parent = TYPE_ARM_BASE_MACHINE_FAMILY, .abstract = true, .is_available = target_is_arm, }; static const TypeInfo arm_machine_info = { .name = TYPE_ARM_MACHINE, .parent = TYPE_ARM_MACHINE_FAMILY, }; static const TypeInfo aarch64_machine_family_info = { .name = TYPE_AARCH64_MACHINE_FAMILY, .parent = TYPE_ARM_BASE_MACHINE_FAMILY, .abstract = true, .is_available = target_is_aarch64, }; static const TypeInfo aarch64_machine_info = { .name = TYPE_AARCH64_MACHINE, .parent = TYPE_AARCH64_MACHINE_FAMILY, }; static void register_target_family_types(void) { type_register_static(&virt_machine_version_info); type_register_static(&arm_machine_info); type_register_static(&aarch64_machine_info); type_register_static(&arm_base_machine_info); type_register_static(&arm_machine_family_info); type_register_static(&aarch64_machine_family_info); } ``` The test selects an ARM-family target, verifies that its child inherits the parent callback, and checks that the other family's child is filtered out. Production ARM `virt` uses the same shape with `TYPE_ARM_BASE_MACHINE` parented to target-neutral `TYPE_MACHINE`; each versioned `virt` TypeInfo inherits from that family parent. `DSO_BEFORE` registrations defer validation until normal QOM initialization, so parent types from the static `type_init()` batch have a chance to register. `DSO_AFTER` registrations are checked after each batch. This hook arrangement provides incremental validation for late registrations; it is an implementation choice, separate from the combined binary's requirement to register types before applying target filtering. ## Reviewer Concerns and Design Responses The responses below describe the current prototype. Broader policies that remain open are listed under [Remaining Questions](#remaining-questions). - **Pierrick Bouvier: RFC request and machine-selection ambiguity.** Pierrick requested this focused RFC: > @Yonggang: can you try to provide a RFC based on this, to see what it > could look like? No need to cover all machines/cpus, just select one of > machine+cpu, ideally with multiple SysEmuTarget (like virt and max cpu > for aarch64), and let's iterate from this. On target-qualified machine names and ambiguous lookup, he wrote: > That's my point, the current abstraction will create something ambiguous > by design. When people will specify -M virt, without $arch:, a random > machine will be selected. At least, maybe this series should ensure there > is only one virt alias, or make it forbidden and uses unambiguous names > only. > > "$arch:" is just a workaround to not add -target option, except that it > comes with additional problems, creating inconsistent command lines. I > already expressed my skepticism about the "machine prefix" based approach, > being more in favor to strongly filter types per target with an > unambiguous -target option. **Response:** the prototype uses the dedicated `-target` option, selecting `TargetInfo` before QOM setup, while preserving `virt` as the `-M` name. With target `none`, `-M virt` fails and reports the targets that provide the name instead of choosing one. A spelling such as `-machine aarch64-virt` is not a reliable general alternative: hyphens already occur in machine names such as `virt-11.1` and `sbsa-ref`, so a parser cannot safely split a machine name at the first hyphen. Resolving the name by searching registered machine classes would require QOM initialization, but the target must be selected before QOM setup. A literal alias could be added for one machine, but that would be a per-machine convention, not general target selection. The concrete run and failure cases are shown in [Design](#design). - **Daniel P. Berrange: target metadata and device compatibility.** Daniel wrote: > It would require that we introduce a new field in MachineClass > that returned that information about what target a machine is > associated with. > > I note the SysEmuTarget covers both 32-bit and 64-bit variants. > > It might be better use QEMU_ARCH which correlates directly to > the TCG targets, rather than the traditional binary target > names? > > Likely the CPU class would also want to declare its list of > supported targets. > > IMHO just leave devices to raise an error when passed to '-device' > if not compatible with the requested machine. **Response:** the prototype represents availability with `TypeInfo.is_available(TargetInfo *)` on each type, rather than adding a separate target list to each machine and CPU class. A NULL callback inherits availability from the parent; the root types default to `target_is_any`. `SysEmuTarget` is retained because it distinguishes system-emulation variants such as 32-bit and 64-bit targets. `query-machines` evaluates the availability callback for each configured target and returns the resulting `MachineInfo.targets`; the `-target aarch64 -machine virt -cpu max` example demonstrates selecting a compatible machine/CPU pair. The prototype does not add exhaustive per-device target metadata: devices may fail setup when their required machine bus is absent, with better diagnostics left as follow-up work. Target identity is kept distinct from the base architecture with `TARGET_SYS_EMU`. Meson defaults it to `TARGET_NAME` for softmmu targets and to `TARGET_ARCH` for other targets, unless a target config overrides it. Thus system-emulation variants such as `arm` and `aarch64` remain distinct even when they share a base `TARGET_ARCH`. `TargetInfo.target_arch` is built from `TARGET_SYS_EMU`: ```diff - .target_arch = glue(SYS_EMU_TARGET_, TARGET_ARCH), + .target_arch = glue(SYS_EMU_TARGET_, TARGET_SYS_EMU), ``` Full change: [GitLab commit diff](https://gitlab.com/lygstate-qemu/qemu/-/commit/b765db23b4294a7ca829e15e8577af228f229f37). - **Philippe Mathieu-Daude: target discovery and scope.** Philippe wrote: > Why mix target information parsing at machine level when the > machine already know it is target specific? People wanting to > use the ARM virt machine don't want to know (and don't care) > about the M68k virt machine. > > You still did not explained what you are trying to achieve > with all your contributions, so my view is surely biased by > what we envisioned 3 years ago when we started to pave our > way for heterogenous emulation. > > My options so far for a single binary: > > - infer from argv[] > - enforce with CLI -target > - not expose any machine **Response:** The goal here is the target `none` inspection case, not a requirement that ARM users select or even list unrelated machines to run ARM `virt`. All registered machines remain visible through `query-machines`, with each `MachineInfo` carrying its supported `targets`; this lets management clients discover the combined inventory. For CLI execution, a target-specific machine is resolved only after explicit `-target` selection. At target `none`, a request such as `-M virt` fails with the supported-target hints instead of choosing a machine. Inferring from argv is deferred, and the inventory/QOM-name work is kept separate from the broader heterogeneous-emulation selection design. - **Markus Armbruster: QOM identity in `query-machines`.** Markus's message quoted the patch rationale and acked it: > query-machines returned only the -M name, so management apps inferred > the QOM type as that name plus "-machine". A prefixed type such as > arm-virt-11.1-machine no longer matches -M virt-11.1. Return the QOM > type in MachineInfo typename from object_class_get_name() in > qmp_query_machines(). test_query_machines_typename checks that > qom-list-properties accepts it. > > Acked-by: Markus Armbruster <[email protected]> **Response:** `query-machines` returns the public machine `name`, the actual QOM `typename`, and supported `targets`. The QOM type is obtained from `object_class_get_name()`, so management clients can pass it to `qom-list-properties` instead of deriving it from the `-M` name. `test_query_machines_typename` covers this lookup. ## Existing Tests - `test_combined_errors` verifies that `-M virt` at target `none` fails, includes the available targets, and does not fall through to an unrelated target-help hint. - `test_combined_machine_list` verifies the combined machine inventory against the configured target list. - `test-qom-is-available` checks callback inheritance for ARM and AArch64 family parents, target filtering, late registration, class-data destruction, missing parents, and parent cycles. - The combined machine-option tests also exercise explicit target selection and machine startup, including the AArch64 `virt` machine. ## Remaining Questions - Is the current `-M virt` error with supported-target hints sufficient, or should a later CLI provide a separate way to choose among candidates? - Is `MachineInfo.targets` sufficient for management clients to interpret the combined inventory? - Should devices receive explicit target-availability annotations, or is machine setup failure with useful diagnostics sufficient? - After registration, should the combined emulator free QOM metadata for types unavailable to the selected target to save runtime memory, or keep all types registered and require every lookup and listing path to check availability? Keeping all types simplifies the complete inventory but retains metadata for targets that are not selected. -- 此致 礼 罗勇刚 Yours sincerely, Yonggang Luo
