diff options
Diffstat (limited to 'arch/x86/kvm/lapic.c')
| -rw-r--r-- | arch/x86/kvm/lapic.c | 116 | 
1 files changed, 72 insertions, 44 deletions
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 5439117d5c4..00691185817 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -71,9 +71,6 @@  #define VEC_POS(v) ((v) & (32 - 1))  #define REG_POS(v) (((v) >> 5) << 4) -static unsigned int min_timer_period_us = 500; -module_param(min_timer_period_us, uint, S_IRUGO | S_IWUSR); -  static inline void apic_set_reg(struct kvm_lapic *apic, int reg_off, u32 val)  {  	*((u32 *) (apic->regs + reg_off)) = val; @@ -143,6 +140,8 @@ static inline int kvm_apic_id(struct kvm_lapic *apic)  	return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;  } +#define KVM_X2APIC_CID_BITS 0 +  static void recalculate_apic_map(struct kvm *kvm)  {  	struct kvm_apic_map *new, *old = NULL; @@ -180,7 +179,8 @@ static void recalculate_apic_map(struct kvm *kvm)  		if (apic_x2apic_mode(apic)) {  			new->ldr_bits = 32;  			new->cid_shift = 16; -			new->cid_mask = new->lid_mask = 0xffff; +			new->cid_mask = (1 << KVM_X2APIC_CID_BITS) - 1; +			new->lid_mask = 0xffff;  		} else if (kvm_apic_sw_enabled(apic) &&  				!new->cid_mask /* flat mode */ &&  				kvm_apic_get_reg(apic, APIC_DFR) == APIC_DFR_CLUSTER) { @@ -360,6 +360,8 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)  static inline void apic_set_isr(int vec, struct kvm_lapic *apic)  { +	/* Note that we never get here with APIC virtualization enabled.  */ +  	if (!__apic_test_and_set_vector(vec, apic->regs + APIC_ISR))  		++apic->isr_count;  	BUG_ON(apic->isr_count > MAX_APIC_VECTOR); @@ -371,12 +373,48 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)  	apic->highest_isr_cache = vec;  } +static inline int apic_find_highest_isr(struct kvm_lapic *apic) +{ +	int result; + +	/* +	 * Note that isr_count is always 1, and highest_isr_cache +	 * is always -1, with APIC virtualization enabled. +	 */ +	if (!apic->isr_count) +		return -1; +	if (likely(apic->highest_isr_cache != -1)) +		return apic->highest_isr_cache; + +	result = find_highest_vector(apic->regs + APIC_ISR); +	ASSERT(result == -1 || result >= 16); + +	return result; +} +  static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)  { -	if (__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR)) +	struct kvm_vcpu *vcpu; +	if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR)) +		return; + +	vcpu = apic->vcpu; + +	/* +	 * We do get here for APIC virtualization enabled if the guest +	 * uses the Hyper-V APIC enlightenment.  In this case we may need +	 * to trigger a new interrupt delivery by writing the SVI field; +	 * on the other hand isr_count and highest_isr_cache are unused +	 * and must be left alone. +	 */ +	if (unlikely(kvm_apic_vid_enabled(vcpu->kvm))) +		kvm_x86_ops->hwapic_isr_update(vcpu->kvm, +					       apic_find_highest_isr(apic)); +	else {  		--apic->isr_count; -	BUG_ON(apic->isr_count < 0); -	apic->highest_isr_cache = -1; +		BUG_ON(apic->isr_count < 0); +		apic->highest_isr_cache = -1; +	}  }  int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu) @@ -432,7 +470,7 @@ static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)  	u8 val;  	if (pv_eoi_get_user(vcpu, &val) < 0)  		apic_debug("Can't read EOI MSR value: 0x%llx\n", -			   (unsigned long long)vcpi->arch.pv_eoi.msr_val); +			   (unsigned long long)vcpu->arch.pv_eoi.msr_val);  	return val & 0x1;  } @@ -440,7 +478,7 @@ static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)  {  	if (pv_eoi_put_user(vcpu, KVM_PV_EOI_ENABLED) < 0) {  		apic_debug("Can't set EOI MSR value: 0x%llx\n", -			   (unsigned long long)vcpi->arch.pv_eoi.msr_val); +			   (unsigned long long)vcpu->arch.pv_eoi.msr_val);  		return;  	}  	__set_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention); @@ -450,28 +488,12 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)  {  	if (pv_eoi_put_user(vcpu, KVM_PV_EOI_DISABLED) < 0) {  		apic_debug("Can't clear EOI MSR value: 0x%llx\n", -			   (unsigned long long)vcpi->arch.pv_eoi.msr_val); +			   (unsigned long long)vcpu->arch.pv_eoi.msr_val);  		return;  	}  	__clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);  } -static inline int apic_find_highest_isr(struct kvm_lapic *apic) -{ -	int result; - -	/* Note that isr_count is always 1 with vid enabled */ -	if (!apic->isr_count) -		return -1; -	if (likely(apic->highest_isr_cache != -1)) -		return apic->highest_isr_cache; - -	result = find_highest_vector(apic->regs + APIC_ISR); -	ASSERT(result == -1 || result >= 16); - -	return result; -} -  void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr)  {  	struct kvm_lapic *apic = vcpu->arch.apic; @@ -841,7 +863,8 @@ static u32 apic_get_tmcct(struct kvm_lapic *apic)  	ASSERT(apic != NULL);  	/* if initial count is 0, current count should also be 0 */ -	if (kvm_apic_get_reg(apic, APIC_TMICT) == 0) +	if (kvm_apic_get_reg(apic, APIC_TMICT) == 0 || +		apic->lapic_timer.period == 0)  		return 0;  	remaining = hrtimer_get_remaining(&apic->lapic_timer.timer); @@ -1346,8 +1369,12 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)  		return;  	} +	if (!kvm_vcpu_is_bsp(apic->vcpu)) +		value &= ~MSR_IA32_APICBASE_BSP; +	vcpu->arch.apic_base = value; +  	/* update jump label if enable bit changes */ -	if ((vcpu->arch.apic_base ^ value) & MSR_IA32_APICBASE_ENABLE) { +	if ((old_value ^ value) & MSR_IA32_APICBASE_ENABLE) {  		if (value & MSR_IA32_APICBASE_ENABLE)  			static_key_slow_dec_deferred(&apic_hw_disabled);  		else @@ -1355,10 +1382,6 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)  		recalculate_apic_map(vcpu->kvm);  	} -	if (!kvm_vcpu_is_bsp(apic->vcpu)) -		value &= ~MSR_IA32_APICBASE_BSP; - -	vcpu->arch.apic_base = value;  	if ((old_value ^ value) & X2APIC_ENABLE) {  		if (value & X2APIC_ENABLE) {  			u32 id = kvm_apic_id(apic); @@ -1604,6 +1627,8 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)  	int vector = kvm_apic_has_interrupt(vcpu);  	struct kvm_lapic *apic = vcpu->arch.apic; +	/* Note that we never get here with APIC virtualization enabled.  */ +  	if (vector == -1)  		return -1; @@ -1691,7 +1716,6 @@ static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,  void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)  {  	u32 data; -	void *vapic;  	if (test_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention))  		apic_sync_pv_eoi_from_guest(vcpu, vcpu->arch.apic); @@ -1699,9 +1723,8 @@ void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)  	if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))  		return; -	vapic = kmap_atomic(vcpu->arch.apic->vapic_page); -	data = *(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr)); -	kunmap_atomic(vapic); +	kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data, +				sizeof(u32));  	apic_set_tpr(vcpu->arch.apic, data & 0xff);  } @@ -1737,7 +1760,6 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)  	u32 data, tpr;  	int max_irr, max_isr;  	struct kvm_lapic *apic = vcpu->arch.apic; -	void *vapic;  	apic_sync_pv_eoi_to_guest(vcpu, apic); @@ -1753,18 +1775,24 @@ void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)  		max_isr = 0;  	data = (tpr & 0xff) | ((max_isr & 0xf0) << 8) | (max_irr << 24); -	vapic = kmap_atomic(vcpu->arch.apic->vapic_page); -	*(u32 *)(vapic + offset_in_page(vcpu->arch.apic->vapic_addr)) = data; -	kunmap_atomic(vapic); +	kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data, +				sizeof(u32));  } -void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr) +int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)  { -	vcpu->arch.apic->vapic_addr = vapic_addr; -	if (vapic_addr) +	if (vapic_addr) { +		if (kvm_gfn_to_hva_cache_init(vcpu->kvm, +					&vcpu->arch.apic->vapic_cache, +					vapic_addr, sizeof(u32))) +			return -EINVAL;  		__set_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention); -	else +	} else {  		__clear_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention); +	} + +	vcpu->arch.apic->vapic_addr = vapic_addr; +	return 0;  }  int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data)  | 
