Browse Source

hw, target, accel: whpx: change apic_in_platform to kernel_irqchip

Change terminology to match the KVM one, as APIC is x86-specific.

And move out whpx_irqchip_in_kernel() to make it usable from common
code even when not compiling with WHPX support.

Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Mohamed Mediouni 2 months ago
committed by Peter Maydell
parent
commit
e4c95f78a4
  1. 1
      MAINTAINERS
  2. 1
      accel/stubs/whpx-stub.c
  3. 2
      accel/whpx/whpx-accel-ops.c
  4. 10
      accel/whpx/whpx-common.c
  5. 9
      hw/arm/virt.c
  6. 4
      hw/i386/x86-cpu.c
  7. 3
      hw/intc/arm_gicv3_common.c
  8. 237
      hw/intc/arm_gicv3_whpx.c
  9. 1
      hw/intc/meson.build
  10. 3
      include/hw/intc/arm_gicv3_common.h
  11. 1
      include/system/whpx-internal.h
  12. 5
      include/system/whpx.h
  13. 2
      target/i386/cpu-apic.c
  14. 14
      target/i386/whpx/whpx-all.c

1
MAINTAINERS

@ -566,6 +566,7 @@ M: Mohamed Mediouni <mohamed@unpredictable.fr>
S: Supported
F: accel/whpx/
F: target/i386/whpx/
F: hw/intc/arm_gicv3_whpx.c
F: accel/stubs/whpx-stub.c
F: include/system/whpx.h
F: include/system/whpx-accel-ops.h

1
accel/stubs/whpx-stub.c

@ -10,3 +10,4 @@
#include "system/whpx.h"
bool whpx_allowed;
bool whpx_irqchip_in_kernel;

2
accel/whpx/whpx-accel-ops.c

@ -78,7 +78,7 @@ static void whpx_kick_vcpu_thread(CPUState *cpu)
static bool whpx_vcpu_thread_is_idle(CPUState *cpu)
{
return !whpx_apic_in_platform();
return !whpx_irqchip_in_kernel();
}
static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data)

10
accel/whpx/whpx-common.c

@ -35,6 +35,7 @@
#include <winhvplatformdefs.h>
bool whpx_allowed;
bool whpx_irqchip_in_kernel;
static bool whp_dispatch_initialized;
static HMODULE hWinHvPlatform;
#ifdef HOST_X86_64
@ -488,15 +489,6 @@ static const TypeInfo whpx_cpu_accel_type = {
.abstract = true,
};
/*
* Partition support
*/
bool whpx_apic_in_platform(void)
{
return whpx_global.apic_in_platform;
}
static void whpx_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);

9
hw/arm/virt.c

@ -49,6 +49,7 @@
#include "system/tcg.h"
#include "system/kvm.h"
#include "system/hvf.h"
#include "system/whpx.h"
#include "system/qtest.h"
#include "system/system.h"
#include "hw/core/loader.h"
@ -2114,6 +2115,8 @@ static void finalize_gic_version(VirtMachineState *vms)
/* KVM w/o kernel irqchip can only deal with GICv2 */
gics_supported |= VIRT_GIC_VERSION_2_MASK;
accel_name = "KVM with kernel-irqchip=off";
} else if (whpx_enabled()) {
gics_supported |= VIRT_GIC_VERSION_3_MASK;
} else if (tcg_enabled() || hvf_enabled() || qtest_enabled()) {
gics_supported |= VIRT_GIC_VERSION_2_MASK;
if (module_object_class_by_name("arm-gicv3")) {
@ -2154,6 +2157,8 @@ static void finalize_msi_controller(VirtMachineState *vms)
if (vms->msi_controller == VIRT_MSI_CTRL_AUTO) {
if (vms->gic_version == VIRT_GIC_VERSION_2) {
vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
} else if (whpx_enabled()) {
vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
} else {
vms->msi_controller = VIRT_MSI_CTRL_ITS;
}
@ -2169,6 +2174,10 @@ static void finalize_msi_controller(VirtMachineState *vms)
error_report("GICv2 + ITS is an invalid configuration.");
exit(1);
}
if (whpx_enabled()) {
error_report("ITS not supported on WHPX.");
exit(1);
}
}
assert(vms->msi_controller != VIRT_MSI_CTRL_AUTO);

4
hw/i386/x86-cpu.c

@ -45,7 +45,7 @@ static void pic_irq_request(void *opaque, int irq, int level)
trace_x86_pic_interrupt(irq, level);
if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() &&
!whpx_apic_in_platform()) {
!whpx_irqchip_in_kernel()) {
CPU_FOREACH(cs) {
cpu = X86_CPU(cs);
if (apic_accept_pic_intr(cpu->apic_state)) {
@ -71,7 +71,7 @@ int cpu_get_pic_interrupt(CPUX86State *env)
X86CPU *cpu = env_archcpu(env);
int intno;
if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) {
if (!kvm_irqchip_in_kernel() && !whpx_irqchip_in_kernel()) {
intno = apic_get_interrupt(cpu->apic_state);
if (intno >= 0) {
return intno;

3
hw/intc/arm_gicv3_common.c

@ -32,6 +32,7 @@
#include "gicv3_internal.h"
#include "hw/arm/linux-boot-if.h"
#include "system/kvm.h"
#include "system/whpx.h"
static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs)
@ -663,6 +664,8 @@ const char *gicv3_class_name(void)
{
if (kvm_irqchip_in_kernel()) {
return "kvm-arm-gicv3";
} else if (whpx_enabled()) {
return TYPE_WHPX_GICV3;
} else {
if (kvm_enabled()) {
error_report("Userspace GICv3 is not supported with KVM");

237
hw/intc/arm_gicv3_whpx.c

@ -0,0 +1,237 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ARM Generic Interrupt Controller using HVF platform support
*
* Copyright (c) 2025 Mohamed Mediouni
* Based on vGICv3 KVM code by Pavel Fedin
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/intc/arm_gicv3_common.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "system/runstate.h"
#include "system/whpx.h"
#include "system/whpx-internal.h"
#include "gicv3_internal.h"
#include "vgic_common.h"
#include "migration/blocker.h"
#include "qom/object.h"
#include "target/arm/cpregs.h"
#include "hw/arm/bsa.h"
#include <winhvplatform.h>
#include <winhvplatformdefs.h>
#include <winnt.h>
struct WHPXARMGICv3Class {
ARMGICv3CommonClass parent_class;
DeviceRealize parent_realize;
ResettablePhases parent_phases;
};
OBJECT_DECLARE_TYPE(GICv3State, WHPXARMGICv3Class, WHPX_GICV3)
/* TODO: Implement GIC state save-restore */
static void whpx_gicv3_check(GICv3State *s)
{
}
static void whpx_gicv3_put(GICv3State *s)
{
whpx_gicv3_check(s);
}
static void whpx_gicv3_get(GICv3State *s)
{
}
static void whpx_gicv3_set_irq(void *opaque, int irq, int level)
{
struct whpx_state *whpx = &whpx_global;
GICv3State *s = opaque;
WHV_INTERRUPT_CONTROL interrupt_control = {
.InterruptControl.InterruptType = WHvArm64InterruptTypeFixed,
.RequestedVector = GIC_INTERNAL + irq,
.InterruptControl.Asserted = level
};
if (irq > s->num_irq) {
return;
}
whp_dispatch.WHvRequestInterrupt(whpx->partition, &interrupt_control,
sizeof(interrupt_control));
}
static void whpx_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
{
GICv3CPUState *c;
c = env->gicv3state;
c->icc_pmr_el1 = 0;
/*
* Architecturally the reset value of the ICC_BPR registers
* is UNKNOWN. We set them all to 0 here; when the kernel
* uses these values to program the ICH_VMCR_EL2 fields that
* determine the guest-visible ICC_BPR register values, the
* hardware's "writing a value less than the minimum sets
* the field to the minimum value" behaviour will result in
* them effectively resetting to the correct minimum value
* for the host GIC.
*/
c->icc_bpr[GICV3_G0] = 0;
c->icc_bpr[GICV3_G1] = 0;
c->icc_bpr[GICV3_G1NS] = 0;
c->icc_sre_el1 = 0x7;
memset(c->icc_apr, 0, sizeof(c->icc_apr));
memset(c->icc_igrpen, 0, sizeof(c->icc_igrpen));
}
static void whpx_gicv3_reset_hold(Object *obj, ResetType type)
{
GICv3State *s = ARM_GICV3_COMMON(obj);
WHPXARMGICv3Class *kgc = WHPX_GICV3_GET_CLASS(s);
if (kgc->parent_phases.hold) {
kgc->parent_phases.hold(obj, type);
}
whpx_gicv3_put(s);
}
/*
* CPU interface registers of GIC needs to be reset on CPU reset.
* For the calling arm_gicv3_icc_reset() on CPU reset, we register
* below ARMCPRegInfo. As we reset the whole cpu interface under single
* register reset, we define only one register of CPU interface instead
* of defining all the registers.
*/
static const ARMCPRegInfo gicv3_cpuif_reginfo[] = {
{ .name = "ICC_CTLR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 4,
/*
* If ARM_CP_NOP is used, resetfn is not called,
* So ARM_CP_NO_RAW is appropriate type.
*/
.type = ARM_CP_NO_RAW,
.access = PL1_RW,
.readfn = arm_cp_read_zero,
.writefn = arm_cp_write_ignore,
/*
* We hang the whole cpu interface reset routine off here
* rather than parcelling it out into one little function
* per register
*/
.resetfn = whpx_gicv3_icc_reset,
},
};
static void whpx_set_reg(CPUState *cpu, WHV_REGISTER_NAME reg, WHV_REGISTER_VALUE val)
{
struct whpx_state *whpx = &whpx_global;
HRESULT hr;
hr = whp_dispatch.WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
&reg, 1, &val);
if (FAILED(hr)) {
error_report("WHPX: Failed to set register %08x, hr=%08lx", reg, hr);
}
}
static void whpx_gicv3_realize(DeviceState *dev, Error **errp)
{
ERRP_GUARD();
GICv3State *s = WHPX_GICV3(dev);
WHPXARMGICv3Class *kgc = WHPX_GICV3_GET_CLASS(s);
int i;
kgc->parent_realize(dev, errp);
if (*errp) {
return;
}
if (s->revision != 3) {
error_setg(errp, "unsupported GIC revision %d for platform GIC",
s->revision);
return;
}
if (s->security_extn) {
error_setg(errp, "the platform vGICv3 does not implement the "
"security extensions");
return;
}
if (s->nmi_support) {
error_setg(errp, "NMI is not supported with the platform GIC");
return;
}
if (s->nb_redist_regions > 1) {
error_setg(errp, "Multiple VGICv3 redistributor regions are not "
"supported by WHPX");
error_append_hint(errp, "A maximum of %d VCPUs can be used",
s->redist_region_count[0]);
return;
}
gicv3_init_irqs_and_mmio(s, whpx_gicv3_set_irq, NULL);
for (i = 0; i < s->num_cpu; i++) {
CPUState *cpu_state = qemu_get_cpu(i);
ARMCPU *cpu = ARM_CPU(cpu_state);
WHV_REGISTER_VALUE val = {.Reg64 = 0x080A0000 + (GICV3_REDIST_SIZE * i)};
whpx_set_reg(cpu_state, WHvArm64RegisterGicrBaseGpa, val);
define_arm_cp_regs(cpu, gicv3_cpuif_reginfo);
}
if (s->maint_irq) {
error_setg(errp, "Nested virtualisation not currently supported by WHPX.");
return;
}
error_setg(&s->migration_blocker,
"Live migration disabled because GIC state save/restore not supported on WHPX");
if (migrate_add_blocker(&s->migration_blocker, errp) < 0) {
error_report_err(*errp);
}
}
static void whpx_gicv3_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
WHPXARMGICv3Class *kgc = WHPX_GICV3_CLASS(klass);
agcc->pre_save = whpx_gicv3_get;
agcc->post_load = whpx_gicv3_put;
device_class_set_parent_realize(dc, whpx_gicv3_realize,
&kgc->parent_realize);
resettable_class_set_parent_phases(rc, NULL, whpx_gicv3_reset_hold, NULL,
&kgc->parent_phases);
}
static const TypeInfo whpx_arm_gicv3_info = {
.name = TYPE_WHPX_GICV3,
.parent = TYPE_ARM_GICV3_COMMON,
.instance_size = sizeof(GICv3State),
.class_init = whpx_gicv3_class_init,
.class_size = sizeof(WHPXARMGICv3Class),
};
static void whpx_gicv3_register_types(void)
{
type_register_static(&whpx_arm_gicv3_info);
}
type_init(whpx_gicv3_register_types)

1
hw/intc/meson.build

@ -41,6 +41,7 @@ specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c'))
arm_common_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c'))
arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c'))
specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c'))
specific_ss.add(when: ['CONFIG_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c'))
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c'))

3
include/hw/intc/arm_gicv3_common.h

@ -313,6 +313,9 @@ typedef struct ARMGICv3CommonClass ARMGICv3CommonClass;
DECLARE_OBJ_CHECKERS(GICv3State, ARMGICv3CommonClass,
ARM_GICV3_COMMON, TYPE_ARM_GICV3_COMMON)
/* Types for GICv3 kernel-irqchip */
#define TYPE_WHPX_GICV3 "whpx-arm-gicv3"
struct ARMGICv3CommonClass {
/*< private >*/
SysBusDeviceClass parent_class;

1
include/system/whpx-internal.h

@ -45,7 +45,6 @@ struct whpx_state {
bool kernel_irqchip_allowed;
bool kernel_irqchip_required;
bool apic_in_platform;
};
extern struct whpx_state whpx_global;

5
include/system/whpx.h

@ -25,11 +25,12 @@
#ifdef CONFIG_WHPX_IS_POSSIBLE
extern bool whpx_allowed;
extern bool whpx_irqchip_in_kernel;
#define whpx_enabled() (whpx_allowed)
bool whpx_apic_in_platform(void);
#define whpx_irqchip_in_kernel() (whpx_irqchip_in_kernel)
#else /* !CONFIG_WHPX_IS_POSSIBLE */
#define whpx_enabled() 0
#define whpx_apic_in_platform() (0)
#define whpx_irqchip_in_kernel() (0)
#endif /* !CONFIG_WHPX_IS_POSSIBLE */
#endif /* QEMU_WHPX_H */

2
target/i386/cpu-apic.c

@ -33,7 +33,7 @@ APICCommonClass *apic_get_class(Error **errp)
apic_type = "kvm-apic";
} else if (xen_enabled()) {
apic_type = "xen-apic";
} else if (whpx_apic_in_platform()) {
} else if (whpx_irqchip_in_kernel()) {
apic_type = "whpx-apic";
}

14
target/i386/whpx/whpx-all.c

@ -607,7 +607,7 @@ void whpx_get_registers(CPUState *cpu)
hr);
}
if (whpx_apic_in_platform()) {
if (whpx_irqchip_in_kernel()) {
/*
* Fetch the TPR value from the emulated APIC. It may get overwritten
* below with the value from CR8 returned by
@ -749,7 +749,7 @@ void whpx_get_registers(CPUState *cpu)
assert(idx == RTL_NUMBER_OF(whpx_register_names));
if (whpx_apic_in_platform()) {
if (whpx_irqchip_in_kernel()) {
whpx_apic_get(x86_cpu->apic_state);
}
@ -1379,7 +1379,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
}
/* Get pending hard interruption or replay one that was overwritten */
if (!whpx_apic_in_platform()) {
if (!whpx_irqchip_in_kernel()) {
if (!vcpu->interruption_pending &&
vcpu->interruptable && (env->eflags & IF_MASK)) {
assert(!new_int.InterruptionPending);
@ -1553,7 +1553,7 @@ int whpx_vcpu_run(CPUState *cpu)
if (exclusive_step_mode == WHPX_STEP_NONE) {
whpx_vcpu_process_async_events(cpu);
if (cpu->halted && !whpx_apic_in_platform()) {
if (cpu->halted && !whpx_irqchip_in_kernel()) {
cpu->exception_index = EXCP_HLT;
qatomic_set(&cpu->exit_request, false);
return 0;
@ -1642,7 +1642,7 @@ int whpx_vcpu_run(CPUState *cpu)
break;
case WHvRunVpExitReasonX64ApicEoi:
assert(whpx_apic_in_platform());
assert(whpx_irqchip_in_kernel());
ioapic_eoi_broadcast(vcpu->exit_ctx.ApicEoi.InterruptVector);
break;
@ -2187,7 +2187,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
} else {
whpx->apic_in_platform = true;
whpx_irqchip_in_kernel = true;
}
}
@ -2196,7 +2196,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
prop.ExtendedVmExits.X64MsrExit = 1;
prop.ExtendedVmExits.X64CpuidExit = 1;
prop.ExtendedVmExits.ExceptionExit = 1;
if (whpx_apic_in_platform()) {
if (whpx_irqchip_in_kernel()) {
prop.ExtendedVmExits.X64ApicInitSipiExitTrap = 1;
}

Loading…
Cancel
Save