forked from Minki/linux
kvm/x86: Hyper-V synthetic interrupt controller
SynIC (synthetic interrupt controller) is a lapic extension, which is controlled via MSRs and maintains for each vCPU - 16 synthetic interrupt "lines" (SINT's); each can be configured to trigger a specific interrupt vector optionally with auto-EOI semantics - a message page in the guest memory with 16 256-byte per-SINT message slots - an event flag page in the guest memory with 16 2048-bit per-SINT event flag areas The host triggers a SINT whenever it delivers a new message to the corresponding slot or flips an event flag bit in the corresponding area. The guest informs the host that it can try delivering a message by explicitly asserting EOI in lapic or writing to End-Of-Message (EOM) MSR. The userspace (qemu) triggers interrupts and receives EOM notifications via irqfd with resampler; for that, a GSI is allocated for each configured SINT, and irq_routing api is extended to support GSI-SINT mapping. Changes v4: * added activation of SynIC by vcpu KVM_ENABLE_CAP * added per SynIC active flag * added deactivation of APICv upon SynIC activation Changes v3: * added KVM_CAP_HYPERV_SYNIC and KVM_IRQ_ROUTING_HV_SINT notes into docs Changes v2: * do not use posted interrupts for Hyper-V SynIC AutoEOI vectors * add Hyper-V SynIC vectors into EOI exit bitmap * Hyper-V SyniIC SINT msr write logic simplified Signed-off-by: Andrey Smetanin <asmetanin@virtuozzo.com> Reviewed-by: Roman Kagan <rkagan@virtuozzo.com> Signed-off-by: Denis V. Lunev <den@openvz.org> CC: Gleb Natapov <gleb@kernel.org> CC: Paolo Bonzini <pbonzini@redhat.com> CC: Roman Kagan <rkagan@virtuozzo.com> CC: Denis V. Lunev <den@openvz.org> CC: qemu-devel@nongnu.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
d62caabb41
commit
5c919412fe
@ -1451,6 +1451,7 @@ struct kvm_irq_routing_entry {
|
||||
struct kvm_irq_routing_irqchip irqchip;
|
||||
struct kvm_irq_routing_msi msi;
|
||||
struct kvm_irq_routing_s390_adapter adapter;
|
||||
struct kvm_irq_routing_hv_sint hv_sint;
|
||||
__u32 pad[8];
|
||||
} u;
|
||||
};
|
||||
@ -1459,6 +1460,7 @@ struct kvm_irq_routing_entry {
|
||||
#define KVM_IRQ_ROUTING_IRQCHIP 1
|
||||
#define KVM_IRQ_ROUTING_MSI 2
|
||||
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
|
||||
#define KVM_IRQ_ROUTING_HV_SINT 4
|
||||
|
||||
No flags are specified so far, the corresponding field must be set to zero.
|
||||
|
||||
@ -1482,6 +1484,10 @@ struct kvm_irq_routing_s390_adapter {
|
||||
__u32 adapter_id;
|
||||
};
|
||||
|
||||
struct kvm_irq_routing_hv_sint {
|
||||
__u32 vcpu;
|
||||
__u32 sint;
|
||||
};
|
||||
|
||||
4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
|
||||
|
||||
@ -3685,3 +3691,16 @@ available, means that that the kernel has an implementation of the
|
||||
H_RANDOM hypercall backed by a hardware random-number generator.
|
||||
If present, the kernel H_RANDOM handler can be enabled for guest use
|
||||
with the KVM_CAP_PPC_ENABLE_HCALL capability.
|
||||
|
||||
8.2 KVM_CAP_HYPERV_SYNIC
|
||||
|
||||
Architectures: x86
|
||||
This capability, if KVM_CHECK_EXTENSION indicates that it is
|
||||
available, means that that the kernel has an implementation of the
|
||||
Hyper-V Synthetic interrupt controller(SynIC). Hyper-V SynIC is
|
||||
used to support Windows Hyper-V based guest paravirt drivers(VMBus).
|
||||
|
||||
In order to use SynIC, it has to be activated by setting this
|
||||
capability via KVM_ENABLE_CAP ioctl on the vcpu fd. Note that this
|
||||
will disable the use of APIC hardware virtualization even if supported
|
||||
by the CPU, as it's incompatible with SynIC auto-EOI behavior.
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/pvclock_gtod.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/irqbypass.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
#include <asm/pvclock-abi.h>
|
||||
#include <asm/desc.h>
|
||||
@ -374,10 +375,24 @@ struct kvm_mtrr {
|
||||
struct list_head head;
|
||||
};
|
||||
|
||||
/* Hyper-V synthetic interrupt controller (SynIC)*/
|
||||
struct kvm_vcpu_hv_synic {
|
||||
u64 version;
|
||||
u64 control;
|
||||
u64 msg_page;
|
||||
u64 evt_page;
|
||||
atomic64_t sint[HV_SYNIC_SINT_COUNT];
|
||||
atomic_t sint_to_gsi[HV_SYNIC_SINT_COUNT];
|
||||
DECLARE_BITMAP(auto_eoi_bitmap, 256);
|
||||
DECLARE_BITMAP(vec_bitmap, 256);
|
||||
bool active;
|
||||
};
|
||||
|
||||
/* Hyper-V per vcpu emulation context */
|
||||
struct kvm_vcpu_hv {
|
||||
u64 hv_vapic;
|
||||
s64 runtime_offset;
|
||||
struct kvm_vcpu_hv_synic synic;
|
||||
};
|
||||
|
||||
struct kvm_vcpu_arch {
|
||||
|
@ -23,13 +23,314 @@
|
||||
|
||||
#include "x86.h"
|
||||
#include "lapic.h"
|
||||
#include "ioapic.h"
|
||||
#include "hyperv.h"
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/apicdef.h>
|
||||
#include <trace/events/kvm.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static inline u64 synic_read_sint(struct kvm_vcpu_hv_synic *synic, int sint)
|
||||
{
|
||||
return atomic64_read(&synic->sint[sint]);
|
||||
}
|
||||
|
||||
static inline int synic_get_sint_vector(u64 sint_value)
|
||||
{
|
||||
if (sint_value & HV_SYNIC_SINT_MASKED)
|
||||
return -1;
|
||||
return sint_value & HV_SYNIC_SINT_VECTOR_MASK;
|
||||
}
|
||||
|
||||
static bool synic_has_vector_connected(struct kvm_vcpu_hv_synic *synic,
|
||||
int vector)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
|
||||
if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool synic_has_vector_auto_eoi(struct kvm_vcpu_hv_synic *synic,
|
||||
int vector)
|
||||
{
|
||||
int i;
|
||||
u64 sint_value;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
|
||||
sint_value = synic_read_sint(synic, i);
|
||||
if (synic_get_sint_vector(sint_value) == vector &&
|
||||
sint_value & HV_SYNIC_SINT_AUTO_EOI)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint, u64 data)
|
||||
{
|
||||
int vector;
|
||||
|
||||
vector = data & HV_SYNIC_SINT_VECTOR_MASK;
|
||||
if (vector < 16)
|
||||
return 1;
|
||||
/*
|
||||
* Guest may configure multiple SINTs to use the same vector, so
|
||||
* we maintain a bitmap of vectors handled by synic, and a
|
||||
* bitmap of vectors with auto-eoi behavior. The bitmaps are
|
||||
* updated here, and atomically queried on fast paths.
|
||||
*/
|
||||
|
||||
atomic64_set(&synic->sint[sint], data);
|
||||
|
||||
if (synic_has_vector_connected(synic, vector))
|
||||
__set_bit(vector, synic->vec_bitmap);
|
||||
else
|
||||
__clear_bit(vector, synic->vec_bitmap);
|
||||
|
||||
if (synic_has_vector_auto_eoi(synic, vector))
|
||||
__set_bit(vector, synic->auto_eoi_bitmap);
|
||||
else
|
||||
__clear_bit(vector, synic->auto_eoi_bitmap);
|
||||
|
||||
/* Load SynIC vectors into EOI exit bitmap */
|
||||
kvm_make_request(KVM_REQ_SCAN_IOAPIC, synic_to_vcpu(synic));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vcpu_hv_synic *synic;
|
||||
|
||||
if (vcpu_id >= atomic_read(&kvm->online_vcpus))
|
||||
return NULL;
|
||||
vcpu = kvm_get_vcpu(kvm, vcpu_id);
|
||||
if (!vcpu)
|
||||
return NULL;
|
||||
synic = vcpu_to_synic(vcpu);
|
||||
return (synic->active) ? synic : NULL;
|
||||
}
|
||||
|
||||
static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
|
||||
{
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
int gsi, idx;
|
||||
|
||||
vcpu_debug(vcpu, "Hyper-V SynIC acked sint %d\n", sint);
|
||||
|
||||
idx = srcu_read_lock(&kvm->irq_srcu);
|
||||
gsi = atomic_read(&vcpu_to_synic(vcpu)->sint_to_gsi[sint]);
|
||||
if (gsi != -1)
|
||||
kvm_notify_acked_gsi(kvm, gsi);
|
||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||
}
|
||||
|
||||
static int synic_set_msr(struct kvm_vcpu_hv_synic *synic,
|
||||
u32 msr, u64 data, bool host)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
|
||||
int ret;
|
||||
|
||||
if (!synic->active)
|
||||
return 1;
|
||||
|
||||
vcpu_debug(vcpu, "Hyper-V SynIC set msr 0x%x 0x%llx host %d\n",
|
||||
msr, data, host);
|
||||
ret = 0;
|
||||
switch (msr) {
|
||||
case HV_X64_MSR_SCONTROL:
|
||||
synic->control = data;
|
||||
break;
|
||||
case HV_X64_MSR_SVERSION:
|
||||
if (!host) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
synic->version = data;
|
||||
break;
|
||||
case HV_X64_MSR_SIEFP:
|
||||
if (data & HV_SYNIC_SIEFP_ENABLE)
|
||||
if (kvm_clear_guest(vcpu->kvm,
|
||||
data & PAGE_MASK, PAGE_SIZE)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
synic->evt_page = data;
|
||||
break;
|
||||
case HV_X64_MSR_SIMP:
|
||||
if (data & HV_SYNIC_SIMP_ENABLE)
|
||||
if (kvm_clear_guest(vcpu->kvm,
|
||||
data & PAGE_MASK, PAGE_SIZE)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
synic->msg_page = data;
|
||||
break;
|
||||
case HV_X64_MSR_EOM: {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
|
||||
kvm_hv_notify_acked_sint(vcpu, i);
|
||||
break;
|
||||
}
|
||||
case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
|
||||
ret = synic_set_sint(synic, msr - HV_X64_MSR_SINT0, data);
|
||||
break;
|
||||
default:
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!synic->active)
|
||||
return 1;
|
||||
|
||||
ret = 0;
|
||||
switch (msr) {
|
||||
case HV_X64_MSR_SCONTROL:
|
||||
*pdata = synic->control;
|
||||
break;
|
||||
case HV_X64_MSR_SVERSION:
|
||||
*pdata = synic->version;
|
||||
break;
|
||||
case HV_X64_MSR_SIEFP:
|
||||
*pdata = synic->evt_page;
|
||||
break;
|
||||
case HV_X64_MSR_SIMP:
|
||||
*pdata = synic->msg_page;
|
||||
break;
|
||||
case HV_X64_MSR_EOM:
|
||||
*pdata = 0;
|
||||
break;
|
||||
case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
|
||||
*pdata = atomic64_read(&synic->sint[msr - HV_X64_MSR_SINT0]);
|
||||
break;
|
||||
default:
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
|
||||
struct kvm_lapic_irq irq;
|
||||
int ret, vector;
|
||||
|
||||
if (sint >= ARRAY_SIZE(synic->sint))
|
||||
return -EINVAL;
|
||||
|
||||
vector = synic_get_sint_vector(synic_read_sint(synic, sint));
|
||||
if (vector < 0)
|
||||
return -ENOENT;
|
||||
|
||||
memset(&irq, 0, sizeof(irq));
|
||||
irq.dest_id = kvm_apic_id(vcpu->arch.apic);
|
||||
irq.dest_mode = APIC_DEST_PHYSICAL;
|
||||
irq.delivery_mode = APIC_DM_FIXED;
|
||||
irq.vector = vector;
|
||||
irq.level = 1;
|
||||
|
||||
ret = kvm_irq_delivery_to_apic(vcpu->kvm, NULL, &irq, NULL);
|
||||
vcpu_debug(vcpu, "Hyper-V SynIC set irq ret %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
|
||||
{
|
||||
struct kvm_vcpu_hv_synic *synic;
|
||||
|
||||
synic = synic_get(kvm, vcpu_id);
|
||||
if (!synic)
|
||||
return -EINVAL;
|
||||
|
||||
return synic_set_irq(synic, sint);
|
||||
}
|
||||
|
||||
void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector)
|
||||
{
|
||||
struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
|
||||
int i;
|
||||
|
||||
vcpu_debug(vcpu, "Hyper-V SynIC send eoi vec %d\n", vector);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
|
||||
if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
|
||||
kvm_hv_notify_acked_sint(vcpu, i);
|
||||
}
|
||||
|
||||
static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vcpu_id, u32 sint, int gsi)
|
||||
{
|
||||
struct kvm_vcpu_hv_synic *synic;
|
||||
|
||||
synic = synic_get(kvm, vcpu_id);
|
||||
if (!synic)
|
||||
return -EINVAL;
|
||||
|
||||
if (sint >= ARRAY_SIZE(synic->sint_to_gsi))
|
||||
return -EINVAL;
|
||||
|
||||
atomic_set(&synic->sint_to_gsi[sint], gsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_hv_irq_routing_update(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_irq_routing_table *irq_rt;
|
||||
struct kvm_kernel_irq_routing_entry *e;
|
||||
u32 gsi;
|
||||
|
||||
irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
|
||||
lockdep_is_held(&kvm->irq_lock));
|
||||
|
||||
for (gsi = 0; gsi < irq_rt->nr_rt_entries; gsi++) {
|
||||
hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
|
||||
if (e->type == KVM_IRQ_ROUTING_HV_SINT)
|
||||
kvm_hv_set_sint_gsi(kvm, e->hv_sint.vcpu,
|
||||
e->hv_sint.sint, gsi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void synic_init(struct kvm_vcpu_hv_synic *synic)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(synic, 0, sizeof(*synic));
|
||||
synic->version = HV_SYNIC_VERSION_1;
|
||||
for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
|
||||
atomic64_set(&synic->sint[i], HV_SYNIC_SINT_MASKED);
|
||||
atomic_set(&synic->sint_to_gsi[i], -1);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
synic_init(vcpu_to_synic(vcpu));
|
||||
}
|
||||
|
||||
int kvm_hv_activate_synic(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/*
|
||||
* Hyper-V SynIC auto EOI SINT's are
|
||||
* not compatible with APICV, so deactivate APICV
|
||||
*/
|
||||
kvm_vcpu_deactivate_apicv(vcpu);
|
||||
vcpu_to_synic(vcpu)->active = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool kvm_hv_msr_partition_wide(u32 msr)
|
||||
{
|
||||
bool r = false;
|
||||
@ -226,6 +527,13 @@ static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
|
||||
return 1;
|
||||
hv->runtime_offset = data - current_task_runtime_100ns();
|
||||
break;
|
||||
case HV_X64_MSR_SCONTROL:
|
||||
case HV_X64_MSR_SVERSION:
|
||||
case HV_X64_MSR_SIEFP:
|
||||
case HV_X64_MSR_SIMP:
|
||||
case HV_X64_MSR_EOM:
|
||||
case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
|
||||
return synic_set_msr(vcpu_to_synic(vcpu), msr, data, host);
|
||||
default:
|
||||
vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
|
||||
msr, data);
|
||||
@ -304,6 +612,13 @@ static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
|
||||
case HV_X64_MSR_VP_RUNTIME:
|
||||
data = current_task_runtime_100ns() + hv->runtime_offset;
|
||||
break;
|
||||
case HV_X64_MSR_SCONTROL:
|
||||
case HV_X64_MSR_SVERSION:
|
||||
case HV_X64_MSR_SIEFP:
|
||||
case HV_X64_MSR_SIMP:
|
||||
case HV_X64_MSR_EOM:
|
||||
case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
|
||||
return synic_get_msr(vcpu_to_synic(vcpu), msr, pdata);
|
||||
default:
|
||||
vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
|
||||
return 1;
|
||||
|
@ -29,4 +29,27 @@ int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
|
||||
bool kvm_hv_hypercall_enabled(struct kvm *kvm);
|
||||
int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint);
|
||||
void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
|
||||
|
||||
static inline struct kvm_vcpu_hv_synic *vcpu_to_synic(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return &vcpu->arch.hyperv.synic;
|
||||
}
|
||||
|
||||
static inline struct kvm_vcpu *synic_to_vcpu(struct kvm_vcpu_hv_synic *synic)
|
||||
{
|
||||
struct kvm_vcpu_hv *hv;
|
||||
struct kvm_vcpu_arch *arch;
|
||||
|
||||
hv = container_of(synic, struct kvm_vcpu_hv, synic);
|
||||
arch = container_of(hv, struct kvm_vcpu_arch, hyperv);
|
||||
return container_of(arch, struct kvm_vcpu, arch);
|
||||
}
|
||||
void kvm_hv_irq_routing_update(struct kvm *kvm);
|
||||
|
||||
void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
|
||||
|
||||
int kvm_hv_activate_synic(struct kvm_vcpu *vcpu);
|
||||
|
||||
#endif
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include "lapic.h"
|
||||
|
||||
#include "hyperv.h"
|
||||
|
||||
static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
@ -219,6 +221,16 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
|
||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||
}
|
||||
|
||||
static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e,
|
||||
struct kvm *kvm, int irq_source_id, int level,
|
||||
bool line_status)
|
||||
{
|
||||
if (!level)
|
||||
return -1;
|
||||
|
||||
return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint);
|
||||
}
|
||||
|
||||
int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
|
||||
const struct kvm_irq_routing_entry *ue)
|
||||
{
|
||||
@ -257,6 +269,11 @@ int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
|
||||
e->msi.address_hi = ue->u.msi.address_hi;
|
||||
e->msi.data = ue->u.msi.data;
|
||||
break;
|
||||
case KVM_IRQ_ROUTING_HV_SINT:
|
||||
e->set = kvm_hv_set_sint;
|
||||
e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
|
||||
e->hv_sint.sint = ue->u.hv_sint.sint;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
@ -376,3 +393,20 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
srcu_read_unlock(&kvm->irq_srcu, idx);
|
||||
}
|
||||
|
||||
int kvm_arch_set_irq(struct kvm_kernel_irq_routing_entry *irq, struct kvm *kvm,
|
||||
int irq_source_id, int level, bool line_status)
|
||||
{
|
||||
switch (irq->type) {
|
||||
case KVM_IRQ_ROUTING_HV_SINT:
|
||||
return kvm_hv_set_sint(irq, kvm, irq_source_id, level,
|
||||
line_status);
|
||||
default:
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arch_irq_routing_update(struct kvm *kvm)
|
||||
{
|
||||
kvm_hv_irq_routing_update(kvm);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "trace.h"
|
||||
#include "x86.h"
|
||||
#include "cpuid.h"
|
||||
#include "hyperv.h"
|
||||
|
||||
#ifndef CONFIG_X86_64
|
||||
#define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
|
||||
@ -128,11 +129,6 @@ static inline int apic_enabled(struct kvm_lapic *apic)
|
||||
(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \
|
||||
APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)
|
||||
|
||||
static inline int kvm_apic_id(struct kvm_lapic *apic)
|
||||
{
|
||||
return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
|
||||
}
|
||||
|
||||
/* The logical map is definitely wrong if we have multiple
|
||||
* modes at the same time. (Physical map is always right.)
|
||||
*/
|
||||
@ -975,6 +971,9 @@ static int apic_set_eoi(struct kvm_lapic *apic)
|
||||
apic_clear_isr(vector, apic);
|
||||
apic_update_ppr(apic);
|
||||
|
||||
if (test_bit(vector, vcpu_to_synic(apic->vcpu)->vec_bitmap))
|
||||
kvm_hv_synic_send_eoi(apic->vcpu, vector);
|
||||
|
||||
kvm_ioapic_send_eoi(apic, vector);
|
||||
kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
|
||||
return vector;
|
||||
@ -1884,6 +1883,12 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
|
||||
apic_set_isr(vector, apic);
|
||||
apic_update_ppr(apic);
|
||||
apic_clear_irr(vector, apic);
|
||||
|
||||
if (test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap)) {
|
||||
apic_clear_isr(vector, apic);
|
||||
apic_update_ppr(apic);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,11 @@ static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
|
||||
return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
|
||||
}
|
||||
|
||||
static inline int kvm_apic_id(struct kvm_lapic *apic)
|
||||
{
|
||||
return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
|
||||
}
|
||||
|
||||
bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
|
||||
|
||||
void wait_lapic_expire(struct kvm_vcpu *vcpu);
|
||||
|
@ -966,6 +966,7 @@ static u32 emulated_msrs[] = {
|
||||
HV_X64_MSR_RESET,
|
||||
HV_X64_MSR_VP_INDEX,
|
||||
HV_X64_MSR_VP_RUNTIME,
|
||||
HV_X64_MSR_SCONTROL,
|
||||
HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
|
||||
MSR_KVM_PV_EOI_EN,
|
||||
|
||||
@ -2541,6 +2542,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_HYPERV:
|
||||
case KVM_CAP_HYPERV_VAPIC:
|
||||
case KVM_CAP_HYPERV_SPIN:
|
||||
case KVM_CAP_HYPERV_SYNIC:
|
||||
case KVM_CAP_PCI_SEGMENT:
|
||||
case KVM_CAP_DEBUGREGS:
|
||||
case KVM_CAP_X86_ROBUST_SINGLESTEP:
|
||||
@ -3193,6 +3195,20 @@ static int kvm_set_guest_paused(struct kvm_vcpu *vcpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
|
||||
struct kvm_enable_cap *cap)
|
||||
{
|
||||
if (cap->flags)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cap->cap) {
|
||||
case KVM_CAP_HYPERV_SYNIC:
|
||||
return kvm_hv_activate_synic(vcpu);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
long kvm_arch_vcpu_ioctl(struct file *filp,
|
||||
unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
@ -3457,6 +3473,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
||||
r = kvm_set_guest_paused(vcpu);
|
||||
goto out;
|
||||
}
|
||||
case KVM_ENABLE_CAP: {
|
||||
struct kvm_enable_cap cap;
|
||||
|
||||
r = -EFAULT;
|
||||
if (copy_from_user(&cap, argp, sizeof(cap)))
|
||||
goto out;
|
||||
r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
r = -EINVAL;
|
||||
}
|
||||
@ -6309,6 +6334,8 @@ static void process_smi(struct kvm_vcpu *vcpu)
|
||||
|
||||
static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 eoi_exit_bitmap[4];
|
||||
|
||||
if (!kvm_apic_hw_enabled(vcpu->arch.apic))
|
||||
return;
|
||||
|
||||
@ -6321,8 +6348,9 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
|
||||
kvm_x86_ops->sync_pir_to_irr(vcpu);
|
||||
kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
|
||||
}
|
||||
kvm_x86_ops->load_eoi_exitmap(vcpu,
|
||||
(u64 *)vcpu->arch.ioapic_handled_vectors);
|
||||
bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
|
||||
vcpu_to_synic(vcpu)->vec_bitmap, 256);
|
||||
kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
|
||||
}
|
||||
|
||||
static void kvm_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
|
||||
@ -7594,6 +7622,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
|
||||
|
||||
vcpu->arch.pending_external_vector = -1;
|
||||
|
||||
kvm_hv_vcpu_init(vcpu);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_free_mce_banks:
|
||||
|
@ -318,6 +318,11 @@ struct kvm_s390_adapter_int {
|
||||
u32 adapter_id;
|
||||
};
|
||||
|
||||
struct kvm_hv_sint {
|
||||
u32 vcpu;
|
||||
u32 sint;
|
||||
};
|
||||
|
||||
struct kvm_kernel_irq_routing_entry {
|
||||
u32 gsi;
|
||||
u32 type;
|
||||
@ -331,6 +336,7 @@ struct kvm_kernel_irq_routing_entry {
|
||||
} irqchip;
|
||||
struct msi_msg msi;
|
||||
struct kvm_s390_adapter_int adapter;
|
||||
struct kvm_hv_sint hv_sint;
|
||||
};
|
||||
struct hlist_node link;
|
||||
};
|
||||
|
@ -831,6 +831,7 @@ struct kvm_ppc_smmu_info {
|
||||
#define KVM_CAP_GUEST_DEBUG_HW_WPS 120
|
||||
#define KVM_CAP_SPLIT_IRQCHIP 121
|
||||
#define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
|
||||
#define KVM_CAP_HYPERV_SYNIC 123
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
@ -854,10 +855,16 @@ struct kvm_irq_routing_s390_adapter {
|
||||
__u32 adapter_id;
|
||||
};
|
||||
|
||||
struct kvm_irq_routing_hv_sint {
|
||||
__u32 vcpu;
|
||||
__u32 sint;
|
||||
};
|
||||
|
||||
/* gsi routing entry types */
|
||||
#define KVM_IRQ_ROUTING_IRQCHIP 1
|
||||
#define KVM_IRQ_ROUTING_MSI 2
|
||||
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
|
||||
#define KVM_IRQ_ROUTING_HV_SINT 4
|
||||
|
||||
struct kvm_irq_routing_entry {
|
||||
__u32 gsi;
|
||||
@ -868,6 +875,7 @@ struct kvm_irq_routing_entry {
|
||||
struct kvm_irq_routing_irqchip irqchip;
|
||||
struct kvm_irq_routing_msi msi;
|
||||
struct kvm_irq_routing_s390_adapter adapter;
|
||||
struct kvm_irq_routing_hv_sint hv_sint;
|
||||
__u32 pad[8];
|
||||
} u;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user