2022-02-05 18:36:18

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 00/11] KVM: x86: Prep work for VMX IPI virtualization

Prepare for VMX's IPI virtualization, in which hardware treats ICR as a
single 64-bit register in x2APIC mode. The SDM wasn't clear on how ICR
should be modeled, KVM just took the easier path and guessed wrong.

Hardware's implementation of ICR as a 64-bit register requires explicit
handling to maintain backwards compatibility in KVM_{G,S}ET_REG, as
migrating a VM between hosts with different IPI virtualization support
would lead to ICR "corruption" for writes that aren't intercepted by
KVM (hardware doesn't fill ICR2 in vAPIC page).

This series includes AVIC cleanups for things I encountered along the way.
AVIC still has multiple issues, this only fixes the easy bugs.

Sean Christopherson (11):
Revert "svm: Add warning message for AVIC IPI invalid target"
KVM: VMX: Handle APIC-write offset wrangling in VMX code
KVM: x86: Use "raw" APIC register read for handling APIC-write VM-Exit
KVM: SVM: Use common kvm_apic_write_nodecode() for AVIC write traps
KVM: SVM: Don't rewrite guest ICR on AVIC IPI virtualization failure
KVM: x86: WARN if KVM emulates an IPI without clearing the BUSY flag
KVM: x86: Make kvm_lapic_reg_{read,write}() static
KVM: x86: Add helpers to handle 64-bit APIC MSR read/writes
KVM: x86: Treat x2APIC's ICR as a 64-bit register, not two 32-bit regs
KVM: x86: Make kvm_lapic_set_reg() a "private" xAPIC helper
KVM: selftests: Add test to verify KVM handles x2APIC ICR=>ICR2 dance

arch/x86/kvm/lapic.c | 193 ++++++++++++------
arch/x86/kvm/lapic.h | 21 +-
arch/x86/kvm/svm/avic.c | 38 ++--
arch/x86/kvm/trace.h | 6 +-
arch/x86/kvm/vmx/vmx.c | 11 +-
arch/x86/kvm/x86.c | 15 +-
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/x86_64/apic.h | 1 +
.../selftests/kvm/x86_64/xapic_state_test.c | 150 ++++++++++++++
10 files changed, 325 insertions(+), 112 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86_64/xapic_state_test.c


base-commit: 17179d0068b20413de2355f84c75a93740257e20
--
2.35.0.263.gb82422642f-goog



2022-02-05 18:57:42

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 09/11] KVM: x86: Treat x2APIC's ICR as a 64-bit register, not two 32-bit regs

Emulate the x2APIC ICR as a single 64-bit register, as opposed to forking
it across ICR and ICR2 as two 32-bit registers. This mirrors hardware
behavior for Intel's upcoming IPI virtualization support, which does not
split the access.

Previous versions of Intel's SDM and AMD's APM don't explicitly state
exactly how ICR is reflected in the vAPIC page for x2APIC, KVM just
happened to speculate incorrectly.

Handling the upcoming behavior is necessary in order to maintain
backwards compatibility with KVM_{G,S}ET_LAPIC, e.g. failure to shuffle
the 64-bit ICR to ICR+ICR2 and vice versa would break live migration if
IPI virtualization support isn't symmetrical across the source and dest.

Cc: Zeng Guang <[email protected]>
Cc: Chao Gao <[email protected]>
Cc: Maxim Levitsky <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/lapic.c | 114 +++++++++++++++++++++++++++++++++----------
arch/x86/kvm/lapic.h | 8 ++-
arch/x86/kvm/trace.h | 6 +--
arch/x86/kvm/x86.c | 10 +---
4 files changed, 99 insertions(+), 39 deletions(-)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index f72f3043134e..dd185367a62c 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -68,6 +68,29 @@ static bool lapic_timer_advance_dynamic __read_mostly;
/* step-by-step approximation to mitigate fluctuation */
#define LAPIC_TIMER_ADVANCE_ADJUST_STEP 8

+static __always_inline u64 __kvm_lapic_get_reg64(char *regs, int reg)
+{
+ BUILD_BUG_ON(reg != APIC_ICR);
+ return *((u64 *) (regs + reg));
+}
+
+static __always_inline u64 kvm_lapic_get_reg64(struct kvm_lapic *apic, int reg)
+{
+ return __kvm_lapic_get_reg64(apic->regs, reg);
+}
+
+static __always_inline void __kvm_lapic_set_reg64(char *regs, int reg, u64 val)
+{
+ BUILD_BUG_ON(reg != APIC_ICR);
+ *((u64 *) (regs + reg)) = val;
+}
+
+static __always_inline void kvm_lapic_set_reg64(struct kvm_lapic *apic,
+ int reg, u64 val)
+{
+ __kvm_lapic_set_reg64(apic->regs, reg, val);
+}
+
static inline int apic_test_vector(int vec, void *bitmap)
{
return test_bit(VEC_POS(vec), (bitmap) + REG_POS(vec));
@@ -1404,7 +1427,6 @@ static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
APIC_REGS_MASK(APIC_IRR, APIC_ISR_NR) |
APIC_REG_MASK(APIC_ESR) |
APIC_REG_MASK(APIC_ICR) |
- APIC_REG_MASK(APIC_ICR2) |
APIC_REG_MASK(APIC_LVTT) |
APIC_REG_MASK(APIC_LVTTHMR) |
APIC_REG_MASK(APIC_LVTPC) |
@@ -1415,9 +1437,16 @@ static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
APIC_REG_MASK(APIC_TMCCT) |
APIC_REG_MASK(APIC_TDCR);

- /* ARBPRI is not valid on x2APIC */
+ /*
+ * ARBPRI and ICR2 are not valid in x2APIC mode. WARN if KVM reads ICR
+ * in x2APIC mode as it's an 8-byte register in x2APIC and needs to be
+ * manually handled by the caller.
+ */
if (!apic_x2apic_mode(apic))
- valid_reg_mask |= APIC_REG_MASK(APIC_ARBPRI);
+ valid_reg_mask |= APIC_REG_MASK(APIC_ARBPRI) |
+ APIC_REG_MASK(APIC_ICR2);
+ else
+ WARN_ON_ONCE(offset == APIC_ICR);

if (alignment + len > 4)
return 1;
@@ -2061,16 +2090,18 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
break;
}
case APIC_ICR:
+ WARN_ON_ONCE(apic_x2apic_mode(apic));
+
/* No delay here, so we always clear the pending bit */
val &= ~APIC_ICR_BUSY;
kvm_apic_send_ipi(apic, val, kvm_lapic_get_reg(apic, APIC_ICR2));
kvm_lapic_set_reg(apic, APIC_ICR, val);
break;
-
case APIC_ICR2:
- if (!apic_x2apic_mode(apic))
- val &= 0xff000000;
- kvm_lapic_set_reg(apic, APIC_ICR2, val);
+ if (apic_x2apic_mode(apic))
+ ret = 1;
+ else
+ kvm_lapic_set_reg(apic, APIC_ICR2, val & 0xff000000);
break;

case APIC_LVT0:
@@ -2130,10 +2161,9 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
break;

case APIC_SELF_IPI:
- if (apic_x2apic_mode(apic)) {
- kvm_lapic_reg_write(apic, APIC_ICR,
- APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
- } else
+ if (apic_x2apic_mode(apic))
+ kvm_x2apic_icr_write(apic, APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
+ else
ret = 1;
break;
default:
@@ -2359,8 +2389,12 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
if (!apic_x2apic_mode(apic))
kvm_apic_set_ldr(apic, 0);
kvm_lapic_set_reg(apic, APIC_ESR, 0);
- kvm_lapic_set_reg(apic, APIC_ICR, 0);
- kvm_lapic_set_reg(apic, APIC_ICR2, 0);
+ if (!apic_x2apic_mode(apic)) {
+ kvm_lapic_set_reg(apic, APIC_ICR, 0);
+ kvm_lapic_set_reg(apic, APIC_ICR2, 0);
+ } else {
+ kvm_lapic_set_reg64(apic, APIC_ICR, 0);
+ }
kvm_lapic_set_reg(apic, APIC_TDCR, 0);
kvm_lapic_set_reg(apic, APIC_TMICT, 0);
for (i = 0; i < 8; i++) {
@@ -2577,6 +2611,7 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
if (apic_x2apic_mode(vcpu->arch.apic)) {
u32 *id = (u32 *)(s->regs + APIC_ID);
u32 *ldr = (u32 *)(s->regs + APIC_LDR);
+ u64 icr;

if (vcpu->kvm->arch.x2apic_format) {
if (*id != vcpu->vcpu_id)
@@ -2588,9 +2623,21 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
*id <<= 24;
}

- /* In x2APIC mode, the LDR is fixed and based on the id */
- if (set)
+ /*
+ * In x2APIC mode, the LDR is fixed and based on the id. And
+ * ICR is internally a single 64-bit register, but needs to be
+ * split to ICR+ICR2 in userspace for backwards compatibility.
+ */
+ if (set) {
*ldr = kvm_apic_calc_x2apic_ldr(*id);
+
+ icr = __kvm_lapic_get_reg(s->regs, APIC_ICR) |
+ (u64)__kvm_lapic_get_reg(s->regs, APIC_ICR2) << 32;
+ __kvm_lapic_set_reg64(s->regs, APIC_ICR, icr);
+ } else {
+ icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
+ __kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
+ }
}

return 0;
@@ -2782,27 +2829,43 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
return 0;
}

+int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
+{
+ data &= ~APIC_ICR_BUSY;
+
+ kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
+ kvm_lapic_set_reg64(apic, APIC_ICR, data);
+ trace_kvm_apic_write(APIC_ICR, data);
+ return 0;
+}
+
static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data)
{
- u32 low, high = 0;
+ u32 low;
+
+ if (reg == APIC_ICR) {
+ *data = kvm_lapic_get_reg64(apic, APIC_ICR);
+ return 0;
+ }

if (kvm_lapic_reg_read(apic, reg, 4, &low))
return 1;

- if (reg == APIC_ICR &&
- WARN_ON_ONCE(kvm_lapic_reg_read(apic, APIC_ICR2, 4, &high)))
- return 1;
-
- *data = (((u64)high) << 32) | low;
+ *data = low;

return 0;
}

static int kvm_lapic_msr_write(struct kvm_lapic *apic, u32 reg, u64 data)
{
- /* For 64-bit ICR writes, set ICR2 (dest) before ICR (command). */
+ /*
+ * ICR is a 64-bit register in x2APIC mode (and Hyper'v PV vAPIC) and
+ * can be written as such, all other registers remain accessible only
+ * through 32-bit reads/writes.
+ */
if (reg == APIC_ICR)
- kvm_lapic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
+ return kvm_x2apic_icr_write(apic, data);
+
return kvm_lapic_reg_write(apic, reg, (u32)data);
}

@@ -2814,9 +2877,6 @@ int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data)
if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(apic))
return 1;

- if (reg == APIC_ICR2)
- return 1;
-
return kvm_lapic_msr_write(apic, reg, data);
}

@@ -2828,7 +2888,7 @@ int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(apic))
return 1;

- if (reg == APIC_DFR || reg == APIC_ICR2)
+ if (reg == APIC_DFR)
return 1;

return kvm_lapic_msr_read(apic, reg, data);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index ab76896a8c3f..e39e7ec5c2b4 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -118,6 +118,7 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);

+int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data);
int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);

@@ -150,9 +151,14 @@ static inline void kvm_lapic_set_irr(int vec, struct kvm_lapic *apic)
apic->irr_pending = true;
}

+static inline u32 __kvm_lapic_get_reg(char *regs, int reg_off)
+{
+ return *((u32 *) (regs + reg_off));
+}
+
static inline u32 kvm_lapic_get_reg(struct kvm_lapic *apic, int reg_off)
{
- return *((u32 *) (apic->regs + reg_off));
+ return __kvm_lapic_get_reg(apic->regs, reg_off);
}

static inline void __kvm_lapic_set_reg(char *regs, int reg_off, u32 val)
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index 92e6f6702f00..340394a8ce7a 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -251,13 +251,13 @@ TRACE_EVENT(kvm_cpuid,
* Tracepoint for apic access.
*/
TRACE_EVENT(kvm_apic,
- TP_PROTO(unsigned int rw, unsigned int reg, unsigned int val),
+ TP_PROTO(unsigned int rw, unsigned int reg, u64 val),
TP_ARGS(rw, reg, val),

TP_STRUCT__entry(
__field( unsigned int, rw )
__field( unsigned int, reg )
- __field( unsigned int, val )
+ __field( u64, val )
),

TP_fast_assign(
@@ -266,7 +266,7 @@ TRACE_EVENT(kvm_apic,
__entry->val = val;
),

- TP_printk("apic_%s %s = 0x%x",
+ TP_printk("apic_%s %s = 0x%llx",
__entry->rw ? "write" : "read",
__print_symbolic(__entry->reg, kvm_trace_symbol_apic),
__entry->val)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 9024e33c2add..eaad2f485b64 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2014,14 +2014,8 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data
if (((data & APIC_SHORT_MASK) == APIC_DEST_NOSHORT) &&
((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) &&
((data & APIC_MODE_MASK) == APIC_DM_FIXED) &&
- ((u32)(data >> 32) != X2APIC_BROADCAST)) {
- data &= ~APIC_ICR_BUSY;
- kvm_apic_send_ipi(vcpu->arch.apic, (u32)data, (u32)(data >> 32));
- kvm_lapic_set_reg(vcpu->arch.apic, APIC_ICR2, (u32)(data >> 32));
- kvm_lapic_set_reg(vcpu->arch.apic, APIC_ICR, (u32)data);
- trace_kvm_apic_write(APIC_ICR, (u32)data);
- return 0;
- }
+ ((u32)(data >> 32) != X2APIC_BROADCAST))
+ return kvm_x2apic_icr_write(vcpu->arch.apic, data);

return 1;
}
--
2.35.0.263.gb82422642f-goog


2022-02-07 10:01:49

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 06/11] KVM: x86: WARN if KVM emulates an IPI without clearing the BUSY flag

WARN if KVM emulates an IPI without clearing the BUSY flag, failure to do
so could hang the guest if it waits for the IPI be sent.

Opportunistically use APIC_ICR_BUSY macro instead of open coding the
magic number, and add a comment to clarify why kvm_recalculate_apic_map()
is unconditionally invoked (it's really, really confusing for IPIs due to
the existence of fast paths that don't trigger a potential recalc).

Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/lapic.c | 10 +++++++++-
arch/x86/kvm/x86.c | 9 ++++-----
2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 6e1f9e83eb68..4f57b6f5ebd4 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1282,6 +1282,9 @@ void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high)
{
struct kvm_lapic_irq irq;

+ /* KVM has no delay and should always clear the BUSY/PENDING flag. */
+ WARN_ON_ONCE(icr_low & APIC_ICR_BUSY);
+
irq.vector = icr_low & APIC_VECTOR_MASK;
irq.delivery_mode = icr_low & APIC_MODE_MASK;
irq.dest_mode = icr_low & APIC_DEST_MASK;
@@ -2060,7 +2063,7 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
}
case APIC_ICR:
/* No delay here, so we always clear the pending bit */
- val &= ~(1 << 12);
+ val &= ~APIC_ICR_BUSY;
kvm_apic_send_ipi(apic, val, kvm_lapic_get_reg(apic, APIC_ICR2));
kvm_lapic_set_reg(apic, APIC_ICR, val);
break;
@@ -2139,6 +2142,11 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
break;
}

+ /*
+ * Recalculate APIC maps if necessary, e.g. if the software enable bit
+ * was toggled, the APIC ID changed, etc... The maps are marked dirty
+ * on relevant changes, i.e. this is a nop for most writes.
+ */
kvm_recalculate_apic_map(apic->vcpu->kvm);

return ret;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index c25a6ef0ff06..9024e33c2add 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2012,11 +2012,10 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data
return 1;

if (((data & APIC_SHORT_MASK) == APIC_DEST_NOSHORT) &&
- ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) &&
- ((data & APIC_MODE_MASK) == APIC_DM_FIXED) &&
- ((u32)(data >> 32) != X2APIC_BROADCAST)) {
-
- data &= ~(1 << 12);
+ ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) &&
+ ((data & APIC_MODE_MASK) == APIC_DM_FIXED) &&
+ ((u32)(data >> 32) != X2APIC_BROADCAST)) {
+ data &= ~APIC_ICR_BUSY;
kvm_apic_send_ipi(vcpu->arch.apic, (u32)data, (u32)(data >> 32));
kvm_lapic_set_reg(vcpu->arch.apic, APIC_ICR2, (u32)(data >> 32));
kvm_lapic_set_reg(vcpu->arch.apic, APIC_ICR, (u32)data);
--
2.35.0.263.gb82422642f-goog


2022-02-07 11:35:54

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 07/11] KVM: x86: Make kvm_lapic_reg_{read,write}() static

Make the low level read/write lapic helpers static, any accesses to the
local APIC from vendor code or non-APIC code should be routed through
proper helpers.

No functional change intended.

Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/lapic.c | 8 +++-----
arch/x86/kvm/lapic.h | 3 ---
2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 4f57b6f5ebd4..deac73ce2de5 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1385,8 +1385,8 @@ static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev)
#define APIC_REGS_MASK(first, count) \
(APIC_REG_MASK(first) * ((1ull << (count)) - 1))

-int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
- void *data)
+static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
+ void *data)
{
unsigned char alignment = offset & 0xf;
u32 result;
@@ -1442,7 +1442,6 @@ int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
}
return 0;
}
-EXPORT_SYMBOL_GPL(kvm_lapic_reg_read);

static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr)
{
@@ -2003,7 +2002,7 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
}
}

-int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
+static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
{
int ret = 0;

@@ -2151,7 +2150,6 @@ int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)

return ret;
}
-EXPORT_SYMBOL_GPL(kvm_lapic_reg_write);

static int apic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this,
gpa_t address, int len, const void *data)
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 2b44e533fc8d..ab76896a8c3f 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -85,9 +85,6 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
void kvm_recalculate_apic_map(struct kvm *kvm);
void kvm_apic_set_version(struct kvm_vcpu *vcpu);
-int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val);
-int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
- void *data);
bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source,
int shorthand, unsigned int dest, int dest_mode);
int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2);
--
2.35.0.263.gb82422642f-goog


2022-02-07 11:56:22

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 01/11] Revert "svm: Add warning message for AVIC IPI invalid target"

Remove a WARN on an "AVIC IPI invalid target" exit, the WARN is trivial
to trigger from guest as it will fail on any destination APIC ID that
doesn't exist from the guest's perspective.

Don't bother recording anything in the kernel log, the common tracepoint
for kvm_avic_incomplete_ipi() is sufficient for debugging.

This reverts commit 37ef0c4414c9743ba7f1af4392f0a27a99649f2a.

Cc: [email protected]
Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/svm/avic.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 90364d02f22a..ecc81c48c0ca 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -345,8 +345,6 @@ int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu)
avic_kick_target_vcpus(vcpu->kvm, apic, icrl, icrh);
break;
case AVIC_IPI_FAILURE_INVALID_TARGET:
- WARN_ONCE(1, "Invalid IPI target: index=%u, vcpu=%d, icr=%#0x:%#0x\n",
- index, vcpu->vcpu_id, icrh, icrl);
break;
case AVIC_IPI_FAILURE_INVALID_BACKING_PAGE:
WARN_ONCE(1, "Invalid backing page\n");
--
2.35.0.263.gb82422642f-goog


2022-02-07 18:55:09

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 04/11] KVM: SVM: Use common kvm_apic_write_nodecode() for AVIC write traps

Use the common kvm_apic_write_nodecode() to handle AVIC/APIC-write traps
instead of open coding the same exact code. This will allow making the
low level lapic helpers inaccessible outside of lapic.c code.

Opportunistically clean up the params to eliminate a bunch of svm=>vcpu
reflection.

No functional change intended.

Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/svm/avic.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index ecc81c48c0ca..462ab073db38 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -476,10 +476,9 @@ static void avic_handle_dfr_update(struct kvm_vcpu *vcpu)
svm->dfr_reg = dfr;
}

-static int avic_unaccel_trap_write(struct vcpu_svm *svm)
+static int avic_unaccel_trap_write(struct kvm_vcpu *vcpu)
{
- struct kvm_lapic *apic = svm->vcpu.arch.apic;
- u32 offset = svm->vmcb->control.exit_info_1 &
+ u32 offset = to_svm(vcpu)->vmcb->control.exit_info_1 &
AVIC_UNACCEL_ACCESS_OFFSET_MASK;

switch (offset) {
@@ -488,18 +487,17 @@ static int avic_unaccel_trap_write(struct vcpu_svm *svm)
return 0;
break;
case APIC_LDR:
- if (avic_handle_ldr_update(&svm->vcpu))
+ if (avic_handle_ldr_update(vcpu))
return 0;
break;
case APIC_DFR:
- avic_handle_dfr_update(&svm->vcpu);
+ avic_handle_dfr_update(vcpu);
break;
default:
break;
}

- kvm_lapic_reg_write(apic, offset, kvm_lapic_get_reg(apic, offset));
-
+ kvm_apic_write_nodecode(vcpu, offset);
return 1;
}

@@ -549,7 +547,7 @@ int avic_unaccelerated_access_interception(struct kvm_vcpu *vcpu)
if (trap) {
/* Handling Trap */
WARN_ONCE(!write, "svm: Handling trap read.\n");
- ret = avic_unaccel_trap_write(svm);
+ ret = avic_unaccel_trap_write(vcpu);
} else {
/* Handling Fault */
ret = kvm_emulate_instruction(vcpu, 0);
--
2.35.0.263.gb82422642f-goog


2022-02-07 19:25:13

by Sean Christopherson

[permalink] [raw]
Subject: [PATCH 10/11] KVM: x86: Make kvm_lapic_set_reg() a "private" xAPIC helper

Hide the lapic's "raw" write helper inside lapic.c to force non-APIC code
to go through proper helpers when modification the vAPIC state. Keep the
read helper visible to outsiders for now, refactoring KVM to hide it too
is possible, it will just take more work to do so.

No functional change intended.

Signed-off-by: Sean Christopherson <[email protected]>
---
arch/x86/kvm/lapic.c | 10 ++++++++++
arch/x86/kvm/lapic.h | 10 ----------
2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index dd185367a62c..d60eb6251bed 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -68,6 +68,16 @@ static bool lapic_timer_advance_dynamic __read_mostly;
/* step-by-step approximation to mitigate fluctuation */
#define LAPIC_TIMER_ADVANCE_ADJUST_STEP 8

+static inline void __kvm_lapic_set_reg(char *regs, int reg_off, u32 val)
+{
+ *((u32 *) (regs + reg_off)) = val;
+}
+
+static inline void kvm_lapic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val)
+{
+ __kvm_lapic_set_reg(apic->regs, reg_off, val);
+}
+
static __always_inline u64 __kvm_lapic_get_reg64(char *regs, int reg)
{
BUILD_BUG_ON(reg != APIC_ICR);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index e39e7ec5c2b4..4e4f8a22754f 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -161,16 +161,6 @@ static inline u32 kvm_lapic_get_reg(struct kvm_lapic *apic, int reg_off)
return __kvm_lapic_get_reg(apic->regs, reg_off);
}

-static inline void __kvm_lapic_set_reg(char *regs, int reg_off, u32 val)
-{
- *((u32 *) (regs + reg_off)) = val;
-}
-
-static inline void kvm_lapic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val)
-{
- __kvm_lapic_set_reg(apic->regs, reg_off, val);
-}
-
DECLARE_STATIC_KEY_FALSE(kvm_has_noapic_vcpu);

static inline bool lapic_in_kernel(struct kvm_vcpu *vcpu)
--
2.35.0.263.gb82422642f-goog


2022-02-08 13:22:48

by Paolo Bonzini

[permalink] [raw]
Subject: Re: [PATCH 01/11] Revert "svm: Add warning message for AVIC IPI invalid target"

On 2/4/22 22:41, Sean Christopherson wrote:
> Remove a WARN on an "AVIC IPI invalid target" exit, the WARN is trivial
> to trigger from guest as it will fail on any destination APIC ID that
> doesn't exist from the guest's perspective.
>
> Don't bother recording anything in the kernel log, the common tracepoint
> for kvm_avic_incomplete_ipi() is sufficient for debugging.
>
> This reverts commit 37ef0c4414c9743ba7f1af4392f0a27a99649f2a.
>
> Cc: [email protected]
> Signed-off-by: Sean Christopherson <[email protected]>
> ---
> arch/x86/kvm/svm/avic.c | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
> index 90364d02f22a..ecc81c48c0ca 100644
> --- a/arch/x86/kvm/svm/avic.c
> +++ b/arch/x86/kvm/svm/avic.c
> @@ -345,8 +345,6 @@ int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu)
> avic_kick_target_vcpus(vcpu->kvm, apic, icrl, icrh);
> break;
> case AVIC_IPI_FAILURE_INVALID_TARGET:
> - WARN_ONCE(1, "Invalid IPI target: index=%u, vcpu=%d, icr=%#0x:%#0x\n",
> - index, vcpu->vcpu_id, icrh, icrl);
> break;
> case AVIC_IPI_FAILURE_INVALID_BACKING_PAGE:
> WARN_ONCE(1, "Invalid backing page\n");

Queued for 5.17, thanks.

Paolo


2022-02-15 14:49:08

by Chao Gao

[permalink] [raw]
Subject: Re: [PATCH 09/11] KVM: x86: Treat x2APIC's ICR as a 64-bit register, not two 32-bit regs

> case APIC_SELF_IPI:
>- if (apic_x2apic_mode(apic)) {
>- kvm_lapic_reg_write(apic, APIC_ICR,
>- APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
>- } else
>+ if (apic_x2apic_mode(apic))
>+ kvm_x2apic_icr_write(apic, APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
>+ else

The original code looks incorrect. Emulating writes to SELF_IPI by writes to
ICR has an unwanted side-effect: the value of ICR in vAPIC page gets changed.

It is better to use kvm_apic_send_ipi() directly.

2022-02-15 17:56:26

by Sean Christopherson

[permalink] [raw]
Subject: Re: [PATCH 09/11] KVM: x86: Treat x2APIC's ICR as a 64-bit register, not two 32-bit regs

On Tue, Feb 15, 2022, Chao Gao wrote:
> > case APIC_SELF_IPI:
> >- if (apic_x2apic_mode(apic)) {
> >- kvm_lapic_reg_write(apic, APIC_ICR,
> >- APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
> >- } else
> >+ if (apic_x2apic_mode(apic))
> >+ kvm_x2apic_icr_write(apic, APIC_DEST_SELF | (val & APIC_VECTOR_MASK));
> >+ else
>
> The original code looks incorrect. Emulating writes to SELF_IPI by writes to
> ICR has an unwanted side-effect: the value of ICR in vAPIC page gets changed.
>
> It is better to use kvm_apic_send_ipi() directly.

Agreed, the SDM lists SELF_IPI as write-only, with no associated MMIO offset, so
it should have no visible side effect in the vAPIC. I'll add a patch to fix this.

Thanks!

2022-02-24 15:15:24

by Paolo Bonzini

[permalink] [raw]
Subject: Re: [PATCH 00/11] KVM: x86: Prep work for VMX IPI virtualization

On 2/4/22 22:41, Sean Christopherson wrote:
> Prepare for VMX's IPI virtualization, in which hardware treats ICR as a
> single 64-bit register in x2APIC mode. The SDM wasn't clear on how ICR
> should be modeled, KVM just took the easier path and guessed wrong.
>
> Hardware's implementation of ICR as a 64-bit register requires explicit
> handling to maintain backwards compatibility in KVM_{G,S}ET_REG, as
> migrating a VM between hosts with different IPI virtualization support
> would lead to ICR "corruption" for writes that aren't intercepted by
> KVM (hardware doesn't fill ICR2 in vAPIC page).
>
> This series includes AVIC cleanups for things I encountered along the way.
> AVIC still has multiple issues, this only fixes the easy bugs.
>
> Sean Christopherson (11):
> Revert "svm: Add warning message for AVIC IPI invalid target"
> KVM: VMX: Handle APIC-write offset wrangling in VMX code
> KVM: x86: Use "raw" APIC register read for handling APIC-write VM-Exit
> KVM: SVM: Use common kvm_apic_write_nodecode() for AVIC write traps
> KVM: SVM: Don't rewrite guest ICR on AVIC IPI virtualization failure
> KVM: x86: WARN if KVM emulates an IPI without clearing the BUSY flag
> KVM: x86: Make kvm_lapic_reg_{read,write}() static
> KVM: x86: Add helpers to handle 64-bit APIC MSR read/writes
> KVM: x86: Treat x2APIC's ICR as a 64-bit register, not two 32-bit regs
> KVM: x86: Make kvm_lapic_set_reg() a "private" xAPIC helper
> KVM: selftests: Add test to verify KVM handles x2APIC ICR=>ICR2 dance
>
> arch/x86/kvm/lapic.c | 193 ++++++++++++------
> arch/x86/kvm/lapic.h | 21 +-
> arch/x86/kvm/svm/avic.c | 38 ++--
> arch/x86/kvm/trace.h | 6 +-
> arch/x86/kvm/vmx/vmx.c | 11 +-
> arch/x86/kvm/x86.c | 15 +-
> tools/testing/selftests/kvm/.gitignore | 1 +
> tools/testing/selftests/kvm/Makefile | 1 +
> .../selftests/kvm/include/x86_64/apic.h | 1 +
> .../selftests/kvm/x86_64/xapic_state_test.c | 150 ++++++++++++++
> 10 files changed, 325 insertions(+), 112 deletions(-)
> create mode 100644 tools/testing/selftests/kvm/x86_64/xapic_state_test.c
>
>
> base-commit: 17179d0068b20413de2355f84c75a93740257e20

Queued, with patch 4 adjusted. Thanks,

Paolo