mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:17 +00:00
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>
This commit is contained in:
committed by
Peter Maydell
parent
7573977b4e
commit
e4c95f78a4
@@ -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
|
||||
|
||||
@@ -10,3 +10,4 @@
|
||||
#include "system/whpx.h"
|
||||
|
||||
bool whpx_allowed;
|
||||
bool whpx_irqchip_in_kernel;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Normal file
237
hw/intc/arm_gicv3_whpx.c
Normal file
@@ -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,
|
||||
®, 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)
|
||||
@@ -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'))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user