Add KVM_GET_LAPIC2 and KVM_SET_LAPIC2 ioctls to save and restore APIC state using a 4KB buffer (kvm_lapic_state2). The larger buffer allows saving additional APIC registers beyond the standard APIC registers supported by the existing 1KB KVM_GET/SET_LAPIC ioctls. The 4KB buffer size matches the LAPIC2 capability, which enables the full APIC register page including extended APIC registers for AMD processors. KVM_GET/SET_LAPIC continue to work as before for backward compatibility. Document the new ioctls in Documentation/virt/kvm/api.rst. Suggested-by: Sean Christopherson Signed-off-by: Manali Shukla --- Documentation/virt/kvm/api.rst | 45 +++++++++++++++++++++++++ arch/x86/include/uapi/asm/kvm.h | 5 +++ arch/x86/kvm/x86.c | 59 +++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 2 ++ 4 files changed, 111 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 71b4d24f009a..c49cf3104b2c 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -6517,6 +6517,51 @@ the capability to be present. `flags` must currently be zero. +4.144 KVM_GET_LAPIC2 +---------------------- + +:Capability: KVM_CAP_LAPIC2 +:Architectures: x86 +:Type: vcpu ioctl +:Parameters: struct kvm_lapic_state2 (out) +:Returns: 0 on success, negative on failure + +Reads the extended Local APIC registers, including both the standard APIC +register space (offsets 0h-3FFh) and the extended APIC register space (offsets +400h-500h and beyond). + +This ioctl is similar to KVM_GET_LAPIC but operates on a 4KB APIC +register space that includes extended LVT registers available on AMD processors +with the ExtApicSpace feature. + +:: + + #define KVM_APIC_EXT_REG_SIZE 0x1000 + struct kvm_lapic_state2 { + char regs[KVM_APIC_EXT_REG_SIZE]; + }; + +4.145 KVM_SET_LAPIC2 +---------------------- + +:Capability: KVM_CAP_LAPIC2 +:Architectures: x86 +:Type: vcpu ioctl +:Parameters: struct kvm_lapic_state2 (in) +:Returns: 0 on success, negative on failure + +Sets the extended Local APIC registers, including both the standard APIC +register space and the extended APIC register space. + +This ioctl is similar to KVM_SET_LAPIC but operates on a 4KB APIC register space +that includes extended LVT registers for AMD processors. + +:: + + #define KVM_APIC_EXT_REG_SIZE 0x1000 + struct kvm_lapic_stat2 { + char regs[KVM_APIC_EXT_REG_SIZE]; + }; .. _kvm_run: diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index 7ceff6583652..516d4a0be25a 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -129,6 +129,11 @@ struct kvm_lapic_state { char regs[KVM_APIC_REG_SIZE]; }; +#define KVM_APIC_EXT_REG_SIZE 0x1000 +struct kvm_lapic_state2 { + char regs[KVM_APIC_EXT_REG_SIZE]; +}; + struct kvm_segment { __u64 base; __u32 limit; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 669c894f1061..ccd16bdff56a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5331,6 +5331,17 @@ static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu, return kvm_apic_get_state(vcpu, s->regs, sizeof(*s)); } +static int kvm_vcpu_ioctl_get_lapic2(struct kvm_vcpu *vcpu, + struct kvm_lapic_state2 *s) +{ + if (vcpu->arch.apic->guest_apic_protected) + return -EINVAL; + + kvm_x86_call(sync_pir_to_irr)(vcpu); + + return kvm_apic_get_state(vcpu, s->regs, sizeof(*s)); +} + static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) { @@ -5347,6 +5358,22 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu, return 0; } +static int kvm_vcpu_ioctl_set_lapic2(struct kvm_vcpu *vcpu, + struct kvm_lapic_state2 *s) +{ + int r; + + if (vcpu->arch.apic->guest_apic_protected) + return -EINVAL; + + r = kvm_apic_set_state(vcpu, s->regs, sizeof(*s)); + if (r) + return r; + update_cr8_intercept(vcpu); + + return 0; +} + static int kvm_cpu_accept_dm_intr(struct kvm_vcpu *vcpu) { /* @@ -6203,6 +6230,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, union { struct kvm_sregs2 *sregs2; struct kvm_lapic_state *lapic; + struct kvm_lapic_state2 *lapic2; struct kvm_xsave *xsave; struct kvm_xcrs *xcrs; void *buffer; @@ -6243,6 +6271,37 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = kvm_vcpu_ioctl_set_lapic(vcpu, u.lapic); break; } + case KVM_GET_LAPIC2: { + r = -EINVAL; + if (!lapic_in_kernel(vcpu)) + goto out; + u.lapic2 = kzalloc(sizeof(struct kvm_lapic_state2), GFP_KERNEL); + + r = -ENOMEM; + if (!u.lapic2) + goto out; + r = kvm_vcpu_ioctl_get_lapic2(vcpu, u.lapic2); + if (r) + goto out; + r = -EFAULT; + if (copy_to_user(argp, u.lapic2, sizeof(struct kvm_lapic_state2))) + goto out; + r = 0; + break; + } + case KVM_SET_LAPIC2: { + r = -EINVAL; + if (!lapic_in_kernel(vcpu)) + goto out; + u.lapic2 = memdup_user(argp, sizeof(*u.lapic2)); + if (IS_ERR(u.lapic2)) { + r = PTR_ERR(u.lapic2); + goto out_nofree; + } + + r = kvm_vcpu_ioctl_set_lapic2(vcpu, u.lapic2); + break; + } case KVM_INTERRUPT: { struct kvm_interrupt irq; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index cb27eeb09bdb..f45d313e30ae 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1339,6 +1339,8 @@ struct kvm_vfio_spapr_tce { #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) +#define KVM_GET_LAPIC2 _IOR(KVMIO, 0x8e, struct kvm_lapic_state2) +#define KVM_SET_LAPIC2 _IOW(KVMIO, 0x8f, struct kvm_lapic_state2) #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.43.0