Skip to content

Commit 18f40c5

Browse files
ssuthiku-amdbonzini
authored andcommitted
svm: Add VMEXIT handlers for AVIC
This patch introduces VMEXIT handlers, avic_incomplete_ipi_interception() and avic_unaccelerated_access_interception() along with two trace points (trace_kvm_avic_incomplete_ipi and trace_kvm_avic_unaccelerated_access). Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 340d3bc commit 18f40c5

File tree

6 files changed

+350
-1
lines changed

6 files changed

+350
-1
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ struct kvm_arch {
775775
bool disabled_lapic_found;
776776

777777
/* Struct members for AVIC */
778+
u32 ldr_mode;
778779
struct page *avic_logical_id_table_page;
779780
struct page *avic_physical_id_table_page;
780781
};

arch/x86/include/uapi/asm/svm.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
#define SVM_EXIT_MWAIT_COND 0x08c
7474
#define SVM_EXIT_XSETBV 0x08d
7575
#define SVM_EXIT_NPF 0x400
76+
#define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401
77+
#define SVM_EXIT_AVIC_UNACCELERATED_ACCESS 0x402
7678

7779
#define SVM_EXIT_ERR -1
7880

@@ -107,8 +109,10 @@
107109
{ SVM_EXIT_SMI, "smi" }, \
108110
{ SVM_EXIT_INIT, "init" }, \
109111
{ SVM_EXIT_VINTR, "vintr" }, \
112+
{ SVM_EXIT_CR0_SEL_WRITE, "cr0_sel_write" }, \
110113
{ SVM_EXIT_CPUID, "cpuid" }, \
111114
{ SVM_EXIT_INVD, "invd" }, \
115+
{ SVM_EXIT_PAUSE, "pause" }, \
112116
{ SVM_EXIT_HLT, "hlt" }, \
113117
{ SVM_EXIT_INVLPG, "invlpg" }, \
114118
{ SVM_EXIT_INVLPGA, "invlpga" }, \
@@ -127,7 +131,10 @@
127131
{ SVM_EXIT_MONITOR, "monitor" }, \
128132
{ SVM_EXIT_MWAIT, "mwait" }, \
129133
{ SVM_EXIT_XSETBV, "xsetbv" }, \
130-
{ SVM_EXIT_NPF, "npf" }
134+
{ SVM_EXIT_NPF, "npf" }, \
135+
{ SVM_EXIT_RSM, "rsm" }, \
136+
{ SVM_EXIT_AVIC_INCOMPLETE_IPI, "avic_incomplete_ipi" }, \
137+
{ SVM_EXIT_AVIC_UNACCELERATED_ACCESS, "avic_unaccelerated_access" }
131138

132139

133140
#endif /* _UAPI__SVM_H */

arch/x86/kvm/lapic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#define KVM_APIC_SIPI 1
1010
#define KVM_APIC_LVT_NUM 6
1111

12+
#define KVM_APIC_SHORT_MASK 0xc0000
13+
#define KVM_APIC_DEST_MASK 0x800
14+
1215
struct kvm_timer {
1316
struct hrtimer timer;
1417
s64 period; /* unit: ns */

arch/x86/kvm/svm.c

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ MODULE_DEVICE_TABLE(x86cpu, svm_cpu_id);
9191
*/
9292
#define AVIC_MAX_PHYSICAL_ID_COUNT 255
9393

94+
#define AVIC_UNACCEL_ACCESS_WRITE_MASK 1
95+
#define AVIC_UNACCEL_ACCESS_OFFSET_MASK 0xFF0
96+
#define AVIC_UNACCEL_ACCESS_VECTOR_MASK 0xFFFFFFFF
97+
9498
static bool erratum_383_found __read_mostly;
9599

96100
static const u32 host_save_user_msrs[] = {
@@ -176,6 +180,7 @@ struct vcpu_svm {
176180
/* cached guest cpuid flags for faster access */
177181
bool nrips_enabled : 1;
178182

183+
u32 ldr_reg;
179184
struct page *avic_backing_page;
180185
u64 *avic_physical_id_cache;
181186
};
@@ -3492,6 +3497,278 @@ static int mwait_interception(struct vcpu_svm *svm)
34923497
return nop_interception(svm);
34933498
}
34943499

3500+
enum avic_ipi_failure_cause {
3501+
AVIC_IPI_FAILURE_INVALID_INT_TYPE,
3502+
AVIC_IPI_FAILURE_TARGET_NOT_RUNNING,
3503+
AVIC_IPI_FAILURE_INVALID_TARGET,
3504+
AVIC_IPI_FAILURE_INVALID_BACKING_PAGE,
3505+
};
3506+
3507+
static int avic_incomplete_ipi_interception(struct vcpu_svm *svm)
3508+
{
3509+
u32 icrh = svm->vmcb->control.exit_info_1 >> 32;
3510+
u32 icrl = svm->vmcb->control.exit_info_1;
3511+
u32 id = svm->vmcb->control.exit_info_2 >> 32;
3512+
u32 index = svm->vmcb->control.exit_info_2 && 0xFF;
3513+
struct kvm_lapic *apic = svm->vcpu.arch.apic;
3514+
3515+
trace_kvm_avic_incomplete_ipi(svm->vcpu.vcpu_id, icrh, icrl, id, index);
3516+
3517+
switch (id) {
3518+
case AVIC_IPI_FAILURE_INVALID_INT_TYPE:
3519+
/*
3520+
* AVIC hardware handles the generation of
3521+
* IPIs when the specified Message Type is Fixed
3522+
* (also known as fixed delivery mode) and
3523+
* the Trigger Mode is edge-triggered. The hardware
3524+
* also supports self and broadcast delivery modes
3525+
* specified via the Destination Shorthand(DSH)
3526+
* field of the ICRL. Logical and physical APIC ID
3527+
* formats are supported. All other IPI types cause
3528+
* a #VMEXIT, which needs to emulated.
3529+
*/
3530+
kvm_lapic_reg_write(apic, APIC_ICR2, icrh);
3531+
kvm_lapic_reg_write(apic, APIC_ICR, icrl);
3532+
break;
3533+
case AVIC_IPI_FAILURE_TARGET_NOT_RUNNING: {
3534+
int i;
3535+
struct kvm_vcpu *vcpu;
3536+
struct kvm *kvm = svm->vcpu.kvm;
3537+
struct kvm_lapic *apic = svm->vcpu.arch.apic;
3538+
3539+
/*
3540+
* At this point, we expect that the AVIC HW has already
3541+
* set the appropriate IRR bits on the valid target
3542+
* vcpus. So, we just need to kick the appropriate vcpu.
3543+
*/
3544+
kvm_for_each_vcpu(i, vcpu, kvm) {
3545+
bool m = kvm_apic_match_dest(vcpu, apic,
3546+
icrl & KVM_APIC_SHORT_MASK,
3547+
GET_APIC_DEST_FIELD(icrh),
3548+
icrl & KVM_APIC_DEST_MASK);
3549+
3550+
if (m && !avic_vcpu_is_running(vcpu))
3551+
kvm_vcpu_wake_up(vcpu);
3552+
}
3553+
break;
3554+
}
3555+
case AVIC_IPI_FAILURE_INVALID_TARGET:
3556+
break;
3557+
case AVIC_IPI_FAILURE_INVALID_BACKING_PAGE:
3558+
WARN_ONCE(1, "Invalid backing page\n");
3559+
break;
3560+
default:
3561+
pr_err("Unknown IPI interception\n");
3562+
}
3563+
3564+
return 1;
3565+
}
3566+
3567+
static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat)
3568+
{
3569+
struct kvm_arch *vm_data = &vcpu->kvm->arch;
3570+
int index;
3571+
u32 *logical_apic_id_table;
3572+
int dlid = GET_APIC_LOGICAL_ID(ldr);
3573+
3574+
if (!dlid)
3575+
return NULL;
3576+
3577+
if (flat) { /* flat */
3578+
index = ffs(dlid) - 1;
3579+
if (index > 7)
3580+
return NULL;
3581+
} else { /* cluster */
3582+
int cluster = (dlid & 0xf0) >> 4;
3583+
int apic = ffs(dlid & 0x0f) - 1;
3584+
3585+
if ((apic < 0) || (apic > 7) ||
3586+
(cluster >= 0xf))
3587+
return NULL;
3588+
index = (cluster << 2) + apic;
3589+
}
3590+
3591+
logical_apic_id_table = (u32 *) page_address(vm_data->avic_logical_id_table_page);
3592+
3593+
return &logical_apic_id_table[index];
3594+
}
3595+
3596+
static int avic_ldr_write(struct kvm_vcpu *vcpu, u8 g_physical_id, u32 ldr,
3597+
bool valid)
3598+
{
3599+
bool flat;
3600+
u32 *entry, new_entry;
3601+
3602+
flat = kvm_lapic_get_reg(vcpu->arch.apic, APIC_DFR) == APIC_DFR_FLAT;
3603+
entry = avic_get_logical_id_entry(vcpu, ldr, flat);
3604+
if (!entry)
3605+
return -EINVAL;
3606+
3607+
new_entry = READ_ONCE(*entry);
3608+
new_entry &= ~AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK;
3609+
new_entry |= (g_physical_id & AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK);
3610+
if (valid)
3611+
new_entry |= AVIC_LOGICAL_ID_ENTRY_VALID_MASK;
3612+
else
3613+
new_entry &= ~AVIC_LOGICAL_ID_ENTRY_VALID_MASK;
3614+
WRITE_ONCE(*entry, new_entry);
3615+
3616+
return 0;
3617+
}
3618+
3619+
static int avic_handle_ldr_update(struct kvm_vcpu *vcpu)
3620+
{
3621+
int ret;
3622+
struct vcpu_svm *svm = to_svm(vcpu);
3623+
u32 ldr = kvm_lapic_get_reg(vcpu->arch.apic, APIC_LDR);
3624+
3625+
if (!ldr)
3626+
return 1;
3627+
3628+
ret = avic_ldr_write(vcpu, vcpu->vcpu_id, ldr, true);
3629+
if (ret && svm->ldr_reg) {
3630+
avic_ldr_write(vcpu, 0, svm->ldr_reg, false);
3631+
svm->ldr_reg = 0;
3632+
} else {
3633+
svm->ldr_reg = ldr;
3634+
}
3635+
return ret;
3636+
}
3637+
3638+
static int avic_handle_apic_id_update(struct kvm_vcpu *vcpu)
3639+
{
3640+
u64 *old, *new;
3641+
struct vcpu_svm *svm = to_svm(vcpu);
3642+
u32 apic_id_reg = kvm_lapic_get_reg(vcpu->arch.apic, APIC_ID);
3643+
u32 id = (apic_id_reg >> 24) & 0xff;
3644+
3645+
if (vcpu->vcpu_id == id)
3646+
return 0;
3647+
3648+
old = avic_get_physical_id_entry(vcpu, vcpu->vcpu_id);
3649+
new = avic_get_physical_id_entry(vcpu, id);
3650+
if (!new || !old)
3651+
return 1;
3652+
3653+
/* We need to move physical_id_entry to new offset */
3654+
*new = *old;
3655+
*old = 0ULL;
3656+
to_svm(vcpu)->avic_physical_id_cache = new;
3657+
3658+
/*
3659+
* Also update the guest physical APIC ID in the logical
3660+
* APIC ID table entry if already setup the LDR.
3661+
*/
3662+
if (svm->ldr_reg)
3663+
avic_handle_ldr_update(vcpu);
3664+
3665+
return 0;
3666+
}
3667+
3668+
static int avic_handle_dfr_update(struct kvm_vcpu *vcpu)
3669+
{
3670+
struct vcpu_svm *svm = to_svm(vcpu);
3671+
struct kvm_arch *vm_data = &vcpu->kvm->arch;
3672+
u32 dfr = kvm_lapic_get_reg(vcpu->arch.apic, APIC_DFR);
3673+
u32 mod = (dfr >> 28) & 0xf;
3674+
3675+
/*
3676+
* We assume that all local APICs are using the same type.
3677+
* If this changes, we need to flush the AVIC logical
3678+
* APID id table.
3679+
*/
3680+
if (vm_data->ldr_mode == mod)
3681+
return 0;
3682+
3683+
clear_page(page_address(vm_data->avic_logical_id_table_page));
3684+
vm_data->ldr_mode = mod;
3685+
3686+
if (svm->ldr_reg)
3687+
avic_handle_ldr_update(vcpu);
3688+
return 0;
3689+
}
3690+
3691+
static int avic_unaccel_trap_write(struct vcpu_svm *svm)
3692+
{
3693+
struct kvm_lapic *apic = svm->vcpu.arch.apic;
3694+
u32 offset = svm->vmcb->control.exit_info_1 &
3695+
AVIC_UNACCEL_ACCESS_OFFSET_MASK;
3696+
3697+
switch (offset) {
3698+
case APIC_ID:
3699+
if (avic_handle_apic_id_update(&svm->vcpu))
3700+
return 0;
3701+
break;
3702+
case APIC_LDR:
3703+
if (avic_handle_ldr_update(&svm->vcpu))
3704+
return 0;
3705+
break;
3706+
case APIC_DFR:
3707+
avic_handle_dfr_update(&svm->vcpu);
3708+
break;
3709+
default:
3710+
break;
3711+
}
3712+
3713+
kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
3714+
3715+
return 1;
3716+
}
3717+
3718+
static bool is_avic_unaccelerated_access_trap(u32 offset)
3719+
{
3720+
bool ret = false;
3721+
3722+
switch (offset) {
3723+
case APIC_ID:
3724+
case APIC_EOI:
3725+
case APIC_RRR:
3726+
case APIC_LDR:
3727+
case APIC_DFR:
3728+
case APIC_SPIV:
3729+
case APIC_ESR:
3730+
case APIC_ICR:
3731+
case APIC_LVTT:
3732+
case APIC_LVTTHMR:
3733+
case APIC_LVTPC:
3734+
case APIC_LVT0:
3735+
case APIC_LVT1:
3736+
case APIC_LVTERR:
3737+
case APIC_TMICT:
3738+
case APIC_TDCR:
3739+
ret = true;
3740+
break;
3741+
default:
3742+
break;
3743+
}
3744+
return ret;
3745+
}
3746+
3747+
static int avic_unaccelerated_access_interception(struct vcpu_svm *svm)
3748+
{
3749+
int ret = 0;
3750+
u32 offset = svm->vmcb->control.exit_info_1 &
3751+
AVIC_UNACCEL_ACCESS_OFFSET_MASK;
3752+
u32 vector = svm->vmcb->control.exit_info_2 &
3753+
AVIC_UNACCEL_ACCESS_VECTOR_MASK;
3754+
bool write = (svm->vmcb->control.exit_info_1 >> 32) &
3755+
AVIC_UNACCEL_ACCESS_WRITE_MASK;
3756+
bool trap = is_avic_unaccelerated_access_trap(offset);
3757+
3758+
trace_kvm_avic_unaccelerated_access(svm->vcpu.vcpu_id, offset,
3759+
trap, write, vector);
3760+
if (trap) {
3761+
/* Handling Trap */
3762+
WARN_ONCE(!write, "svm: Handling trap read.\n");
3763+
ret = avic_unaccel_trap_write(svm);
3764+
} else {
3765+
/* Handling Fault */
3766+
ret = (emulate_instruction(&svm->vcpu, 0) == EMULATE_DONE);
3767+
}
3768+
3769+
return ret;
3770+
}
3771+
34953772
static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
34963773
[SVM_EXIT_READ_CR0] = cr_interception,
34973774
[SVM_EXIT_READ_CR3] = cr_interception,
@@ -3555,6 +3832,8 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
35553832
[SVM_EXIT_XSETBV] = xsetbv_interception,
35563833
[SVM_EXIT_NPF] = pf_interception,
35573834
[SVM_EXIT_RSM] = emulate_on_interception,
3835+
[SVM_EXIT_AVIC_INCOMPLETE_IPI] = avic_incomplete_ipi_interception,
3836+
[SVM_EXIT_AVIC_UNACCELERATED_ACCESS] = avic_unaccelerated_access_interception,
35583837
};
35593838

35603839
static void dump_vmcb(struct kvm_vcpu *vcpu)

0 commit comments

Comments
 (0)