mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	KVM: arm/arm64: vgic: Prevent userspace injection of a mapped interrupt
Virtual interrupts mapped to a HW interrupt should only be triggered from inside the kernel. Otherwise, you could end up confusing the kernel (and the GIC's) state machine. Rearrange the injection path so that kvm_vgic_inject_irq is used for non-mapped interrupts, and kvm_vgic_inject_mapped_irq is used for mapped interrupts. The latter should only be called from inside the kernel (timer, irqfd). Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
		
							parent
							
								
									6e84e0e067
								
							
						
					
					
						commit
						773299a570
					
				
					 2 changed files with 73 additions and 34 deletions
				
			
		|  | @ -350,6 +350,8 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); | |||
| void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); | ||||
| int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, | ||||
| 			bool level); | ||||
| int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, | ||||
| 			       struct irq_phys_map *map, bool level); | ||||
| void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg); | ||||
| int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); | ||||
| int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu); | ||||
|  |  | |||
|  | @ -1555,7 +1555,8 @@ static int vgic_validate_injection(struct kvm_vcpu *vcpu, int irq, int level) | |||
| } | ||||
| 
 | ||||
| static int vgic_update_irq_pending(struct kvm *kvm, int cpuid, | ||||
| 				  unsigned int irq_num, bool level) | ||||
| 				   struct irq_phys_map *map, | ||||
| 				   unsigned int irq_num, bool level) | ||||
| { | ||||
| 	struct vgic_dist *dist = &kvm->arch.vgic; | ||||
| 	struct kvm_vcpu *vcpu; | ||||
|  | @ -1563,6 +1564,9 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid, | |||
| 	int enabled; | ||||
| 	bool ret = true, can_inject = true; | ||||
| 
 | ||||
| 	if (irq_num >= min(kvm->arch.vgic.nr_irqs, 1020)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	spin_lock(&dist->lock); | ||||
| 
 | ||||
| 	vcpu = kvm_get_vcpu(kvm, cpuid); | ||||
|  | @ -1625,28 +1629,17 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid, | |||
| out: | ||||
| 	spin_unlock(&dist->lock); | ||||
| 
 | ||||
| 	return ret ? cpuid : -EINVAL; | ||||
| 	if (ret) { | ||||
| 		/* kick the specified vcpu */ | ||||
| 		kvm_vcpu_kick(kvm_get_vcpu(kvm, cpuid)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic | ||||
|  * @kvm:     The VM structure pointer | ||||
|  * @cpuid:   The CPU for PPIs | ||||
|  * @irq_num: The IRQ number that is assigned to the device | ||||
|  * @level:   Edge-triggered:  true:  to trigger the interrupt | ||||
|  *			      false: to ignore the call | ||||
|  *	     Level-sensitive  true:  activates an interrupt | ||||
|  *			      false: deactivates an interrupt | ||||
|  * | ||||
|  * The GIC is not concerned with devices being active-LOW or active-HIGH for | ||||
|  * level-sensitive interrupts.  You can think of the level parameter as 1 | ||||
|  * being HIGH and 0 being LOW and all devices being active-HIGH. | ||||
|  */ | ||||
| int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, | ||||
| 			bool level) | ||||
| static int vgic_lazy_init(struct kvm *kvm) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int vcpu_id; | ||||
| 
 | ||||
| 	if (unlikely(!vgic_initialized(kvm))) { | ||||
| 		/*
 | ||||
|  | @ -1655,29 +1648,73 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, | |||
| 		 * be explicitly initialized once setup with the respective | ||||
| 		 * KVM device call. | ||||
| 		 */ | ||||
| 		if (kvm->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V2) { | ||||
| 			ret = -EBUSY; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		if (kvm->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V2) | ||||
| 			return -EBUSY; | ||||
| 
 | ||||
| 		mutex_lock(&kvm->lock); | ||||
| 		ret = vgic_init(kvm); | ||||
| 		mutex_unlock(&kvm->lock); | ||||
| 
 | ||||
| 		if (ret) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (irq_num >= min(kvm->arch.vgic.nr_irqs, 1020)) | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic | ||||
|  * @kvm:     The VM structure pointer | ||||
|  * @cpuid:   The CPU for PPIs | ||||
|  * @irq_num: The IRQ number that is assigned to the device. This IRQ | ||||
|  *           must not be mapped to a HW interrupt. | ||||
|  * @level:   Edge-triggered:  true:  to trigger the interrupt | ||||
|  *			      false: to ignore the call | ||||
|  *	     Level-sensitive  true:  raise the input signal | ||||
|  *			      false: lower the input signal | ||||
|  * | ||||
|  * The GIC is not concerned with devices being active-LOW or active-HIGH for | ||||
|  * level-sensitive interrupts.  You can think of the level parameter as 1 | ||||
|  * being HIGH and 0 being LOW and all devices being active-HIGH. | ||||
|  */ | ||||
| int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, | ||||
| 			bool level) | ||||
| { | ||||
| 	struct irq_phys_map *map; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = vgic_lazy_init(kvm); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	map = vgic_irq_map_search(kvm_get_vcpu(kvm, cpuid), irq_num); | ||||
| 	if (map) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	vcpu_id = vgic_update_irq_pending(kvm, cpuid, irq_num, level); | ||||
| 	if (vcpu_id >= 0) { | ||||
| 		/* kick the specified vcpu */ | ||||
| 		kvm_vcpu_kick(kvm_get_vcpu(kvm, vcpu_id)); | ||||
| 	} | ||||
| 	return vgic_update_irq_pending(kvm, cpuid, NULL, irq_num, level); | ||||
| } | ||||
| 
 | ||||
| out: | ||||
| 	return ret; | ||||
| /**
 | ||||
|  * kvm_vgic_inject_mapped_irq - Inject a physically mapped IRQ to the vgic | ||||
|  * @kvm:     The VM structure pointer | ||||
|  * @cpuid:   The CPU for PPIs | ||||
|  * @map:     Pointer to a irq_phys_map structure describing the mapping | ||||
|  * @level:   Edge-triggered:  true:  to trigger the interrupt | ||||
|  *			      false: to ignore the call | ||||
|  *	     Level-sensitive  true:  raise the input signal | ||||
|  *			      false: lower the input signal | ||||
|  * | ||||
|  * The GIC is not concerned with devices being active-LOW or active-HIGH for | ||||
|  * level-sensitive interrupts.  You can think of the level parameter as 1 | ||||
|  * being HIGH and 0 being LOW and all devices being active-HIGH. | ||||
|  */ | ||||
| int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, | ||||
| 			       struct irq_phys_map *map, bool level) | ||||
| { | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = vgic_lazy_init(kvm); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return vgic_update_irq_pending(kvm, cpuid, map, map->virt_irq, level); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t vgic_maintenance_handler(int irq, void *data) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Marc Zyngier
						Marc Zyngier