There are four additional extended LVT registers available in extended APIC register space which can be used for additional interrupt sources like instruction based sampling and many more. Please refer to AMD programmers's manual Volume 2, Section 16.4.5 for more details on extapic. https://bugzilla.kernel.org/attachment.cgi?id=304653 Adds two new vcpu-based IOCTLs to save and restore the local APIC registers with extended APIC register space for a single vcpu. It works same as KVM_GET_LAPIC and KVM_SET_LAPIC IOCTLs. The only differece is the size of APIC page which is copied/restored by kernel. In case of KVM_GET_LAPIC_W_EXTAPIC and KVM_SET_LAPIC_W_EXTAPIC IOCTLs, kernel copies/restores the APIC page with extended APIC register space located at APIC offsets 400h-530h. KVM_GET_LAPIC_W_EXTAPIC and KVM_SET_LAPIC_W_EXTAPIC IOCTLs are used when extended APIC is enabled in the guest. Document KVM_GET_LAPIC_W_EXTAPIC, KVM_SET_LAPIC_W_EXTAPIC ioctls. Signed-off-by: Manali Shukla <manali.shukla@xxxxxxx> --- Documentation/virt/kvm/api.rst | 23 +++++++++++++++++++++++ arch/x86/include/uapi/asm/kvm.h | 5 +++++ arch/x86/kvm/lapic.c | 12 +++++++----- arch/x86/kvm/lapic.h | 6 ++++-- arch/x86/kvm/x86.c | 24 +++++++++++++----------- include/uapi/linux/kvm.h | 10 ++++++++++ 6 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 73db30cb60fb..7239d4f1ecf3 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -1961,6 +1961,18 @@ error. Reads the Local APIC registers and copies them into the input argument. The data format and layout are the same as documented in the architecture manual. +:: + + #define KVM_APIC_EXT_REG_SIZE 0x540 + struct kvm_lapic_state_w_extapic { + __u8 regs[KVM_APIC_EXT_REG_SIZE]; + }; + +Applications should use KVM_GET_LAPIC_W_EXTAPIC ioctl if extended APIC is +enabled. KVM_GET_LAPIC_W_EXTAPIC reads Local APIC registers with extended +APIC register space located at offsets 500h-530h and copies them into input +argument. + If KVM_X2APIC_API_USE_32BIT_IDS feature of KVM_CAP_X2APIC_API is enabled, then the format of APIC_ID register depends on the APIC mode (reported by MSR_IA32_APICBASE) of its VCPU. x2APIC stores APIC ID in @@ -1992,6 +2004,17 @@ always uses xAPIC format. Copies the input argument into the Local APIC registers. The data format and layout are the same as documented in the architecture manual. +:: + + #define KVM_APIC_EXT_REG_SIZE 0x540 + struct kvm_lapic_state_w_extapic { + __u8 regs[KVM_APIC_EXT_REG_SIZE]; + }; + +Applications should use KVM_SET_LAPIC_W_EXTAPIC ioctl if extended APIC is enabled. +KVM_SET_LAPIC_W_EXTAPIC copies input arguments with extended APIC register into +Local APIC and extended APIC registers. + The format of the APIC ID register (bytes 32-35 of struct kvm_lapic_state's regs field) depends on the state of the KVM_CAP_X2APIC_API capability. See the note in KVM_GET_LAPIC. diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 1a6a1f987949..d5bed64fd73d 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -123,6 +123,11 @@ struct kvm_lapic_state { char regs[KVM_APIC_REG_SIZE]; }; +#define KVM_APIC_EXT_REG_SIZE 0x540 +struct kvm_lapic_state_w_extapic { + __u8 regs[KVM_APIC_EXT_REG_SIZE]; +}; + struct kvm_segment { __u64 base; __u32 limit; diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index dcd60b39e794..7c1bd8594f1b 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -2921,7 +2921,7 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu) } static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu, - struct kvm_lapic_state *s, bool set) + struct kvm_lapic_state_w_extapic *s, bool set) { if (apic_x2apic_mode(vcpu->arch.apic)) { u32 *id = (u32 *)(s->regs + APIC_ID); @@ -2958,9 +2958,10 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu, return 0; } -int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) +int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state_w_extapic *s, + unsigned int size) { - memcpy(s->regs, vcpu->arch.apic->regs, sizeof(*s)); + memcpy(s->regs, vcpu->arch.apic->regs, size); /* * Get calculated timer current count for remaining timer period (if @@ -2972,7 +2973,8 @@ int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) return kvm_apic_state_fixup(vcpu, s, false); } -int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) +int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state_w_extapic *s, + unsigned int size) { struct kvm_lapic *apic = vcpu->arch.apic; int r; @@ -2986,7 +2988,7 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) kvm_recalculate_apic_map(vcpu->kvm); return r; } - memcpy(vcpu->arch.apic->regs, s->regs, sizeof(*s)); + memcpy(vcpu->arch.apic->regs, s->regs, size); atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY); kvm_recalculate_apic_map(vcpu->kvm); diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 0a0ea4b5dd8c..ad6c48938733 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -121,8 +121,10 @@ void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high); u64 kvm_get_apic_base(struct kvm_vcpu *vcpu); int kvm_set_apic_base(struct kvm_vcpu *vcpu, struct msr_data *msr_info); -int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); -int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s); +int kvm_apic_get_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state_w_extapic *s, + unsigned int size); +int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state_w_extapic *s, + unsigned int size); enum lapic_mode kvm_get_apic_mode(struct kvm_vcpu *vcpu); int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ddab7d0bb52b..e80a6d598753 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4925,19 +4925,19 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) } static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu, - struct kvm_lapic_state *s) + struct kvm_lapic_state_w_extapic *s, unsigned int size) { static_call_cond(kvm_x86_sync_pir_to_irr)(vcpu); - return kvm_apic_get_state(vcpu, s); + return kvm_apic_get_state(vcpu, s, size); } static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, - struct kvm_lapic_state *s) + struct kvm_lapic_state_w_extapic *s, unsigned int size) { int r; - r = kvm_apic_set_state(vcpu, s); + r = kvm_apic_set_state(vcpu, s, size); if (r) return r; update_cr8_intercept(vcpu); @@ -5636,7 +5636,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, int r; union { struct kvm_sregs2 *sregs2; - struct kvm_lapic_state *lapic; + struct kvm_lapic_state_w_extapic *lapic; struct kvm_xsave *xsave; struct kvm_xcrs *xcrs; void *buffer; @@ -5646,36 +5646,38 @@ long kvm_arch_vcpu_ioctl(struct file *filp, u.buffer = NULL; switch (ioctl) { + case KVM_GET_LAPIC_W_EXTAPIC: case KVM_GET_LAPIC: { r = -EINVAL; if (!lapic_in_kernel(vcpu)) goto out; - u.lapic = kzalloc(sizeof(struct kvm_lapic_state), - GFP_KERNEL_ACCOUNT); + u.lapic = kzalloc(_IOC_SIZE(ioctl), GFP_KERNEL_ACCOUNT); r = -ENOMEM; if (!u.lapic) goto out; - r = kvm_vcpu_ioctl_get_lapic(vcpu, u.lapic); + r = kvm_vcpu_ioctl_get_lapic(vcpu, u.lapic, _IOC_SIZE(ioctl)); if (r) goto out; r = -EFAULT; - if (copy_to_user(argp, u.lapic, sizeof(struct kvm_lapic_state))) + if (copy_to_user(argp, u.lapic, _IOC_SIZE(ioctl))) goto out; r = 0; break; } + case KVM_SET_LAPIC_W_EXTAPIC: case KVM_SET_LAPIC: { r = -EINVAL; if (!lapic_in_kernel(vcpu)) goto out; - u.lapic = memdup_user(argp, sizeof(*u.lapic)); + u.lapic = memdup_user(argp, _IOC_SIZE(ioctl)); + if (IS_ERR(u.lapic)) { r = PTR_ERR(u.lapic); goto out_nofree; } - r = kvm_vcpu_ioctl_set_lapic(vcpu, u.lapic); + r = kvm_vcpu_ioctl_set_lapic(vcpu, u.lapic, _IOC_SIZE(ioctl)); break; } case KVM_INTERRUPT: { diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 13065dd96132..e1dc04e0bf44 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1591,6 +1591,16 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_FPU _IOW(KVMIO, 0x8d, struct kvm_fpu) #define KVM_GET_LAPIC _IOR(KVMIO, 0x8e, struct kvm_lapic_state) #define KVM_SET_LAPIC _IOW(KVMIO, 0x8f, struct kvm_lapic_state) +/* + * Added to save/restore local APIC registers with extended APIC (extapic) + * register space. + * + * Qemu emulates extapic logic only when KVM enables extapic functionality via + * KVM capability. In the condition where Qemu sets extapic registers, but KVM doesn't + * set extapic capability, Qemu ends up using KVM_GET_LAPIC and KVM_SET_LAPIC. + */ +#define KVM_GET_LAPIC_W_EXTAPIC _IOR(KVMIO, 0x8e, struct kvm_lapic_state_w_extapic) +#define KVM_SET_LAPIC_W_EXTAPIC _IOW(KVMIO, 0x8f, struct kvm_lapic_state_w_extapic) #define KVM_SET_CPUID2 _IOW(KVMIO, 0x90, struct kvm_cpuid2) #define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2) /* Available with KVM_CAP_VAPIC */ -- 2.34.1