intc/arm_gic: Implement virtualization extensions in gic_(deactivate|complete_irq)
Implement virtualization extensions in the gic_deactivate_irq() and
gic_complete_irq() functions.
When the guest writes an invalid vIRQ to V_EOIR or V_DIR, since the
GICv2 specification is not entirely clear here, we adopt the behaviour
observed on real hardware:
  * When V_CTRL.EOIMode is false (EOI split is disabled):
    - In case of an invalid vIRQ write to V_EOIR:
      -> If some bits are set in H_APR, an invalid vIRQ write to V_EOIR
         triggers a priority drop, and increments V_HCR.EOICount.
      -> If V_APR is already cleared, nothing happen
    - An invalid vIRQ write to V_DIR is ignored.
  * When V_CTRL.EOIMode is true:
    - In case of an invalid vIRQ write to V_EOIR:
      -> If some bits are set in H_APR, an invalid vIRQ write to V_EOIR
         triggers a priority drop.
      -> If V_APR is already cleared, nothing happen
    - An invalid vIRQ write to V_DIR increments V_HCR.EOICount.
Signed-off-by: Luc Michel <luc.michel@greensocs.com>
Message-id: 20180727095421.386-13-luc.michel@greensocs.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
			
			
This commit is contained in:
		
							parent
							
								
									439badd66d
								
							
						
					
					
						commit
						02f2e22d7c
					
				| @ -583,20 +583,19 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) | ||||
| { | ||||
|     int group; | ||||
| 
 | ||||
|     if (irq >= s->num_irq) { | ||||
|     if (irq >= GIC_MAXIRQ || (!gic_is_vcpu(cpu) && irq >= s->num_irq)) { | ||||
|         /*
 | ||||
|          * This handles two cases: | ||||
|          * 1. If software writes the ID of a spurious interrupt [ie 1023] | ||||
|          * to the GICC_DIR, the GIC ignores that write. | ||||
|          * 2. If software writes the number of a non-existent interrupt | ||||
|          * this must be a subcase of "value written is not an active interrupt" | ||||
|          * and so this is UNPREDICTABLE. We choose to ignore it. | ||||
|          * and so this is UNPREDICTABLE. We choose to ignore it. For vCPUs, | ||||
|          * all IRQs potentially exist, so this limit does not apply. | ||||
|          */ | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     group = gic_has_groups(s) && gic_test_group(s, irq, cpu); | ||||
| 
 | ||||
|     if (!gic_eoi_split(s, cpu, attrs)) { | ||||
|         /* This is UNPREDICTABLE; we choose to ignore it */ | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, | ||||
| @ -604,6 +603,17 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (gic_is_vcpu(cpu) && !gic_virq_is_valid(s, irq, cpu)) { | ||||
|         /* This vIRQ does not have an LR entry which is either active or
 | ||||
|          * pending and active. Increment EOICount and ignore the write. | ||||
|          */ | ||||
|         int rcpu = gic_get_vcpu_real_id(cpu); | ||||
|         s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     group = gic_has_groups(s) && gic_test_group(s, irq, cpu); | ||||
| 
 | ||||
|     if (gic_cpu_ns_access(s, cpu, attrs) && !group) { | ||||
|         DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq); | ||||
|         return; | ||||
| @ -618,6 +628,39 @@ static void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) | ||||
|     int group; | ||||
| 
 | ||||
|     DPRINTF("EOI %d\n", irq); | ||||
|     if (gic_is_vcpu(cpu)) { | ||||
|         /* The call to gic_prio_drop() will clear a bit in GICH_APR iff the
 | ||||
|          * running prio is < 0x100. | ||||
|          */ | ||||
|         bool prio_drop = s->running_priority[cpu] < 0x100; | ||||
| 
 | ||||
|         if (irq >= GIC_MAXIRQ) { | ||||
|             /* Ignore spurious interrupt */ | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         gic_drop_prio(s, cpu, 0); | ||||
| 
 | ||||
|         if (!gic_eoi_split(s, cpu, attrs)) { | ||||
|             bool valid = gic_virq_is_valid(s, irq, cpu); | ||||
|             if (prio_drop && !valid) { | ||||
|                 /* We are in a situation where:
 | ||||
|                  *   - V_CTRL.EOIMode is false (no EOI split), | ||||
|                  *   - The call to gic_drop_prio() cleared a bit in GICH_APR, | ||||
|                  *   - This vIRQ does not have an LR entry which is either | ||||
|                  *     active or pending and active. | ||||
|                  * In that case, we must increment EOICount. | ||||
|                  */ | ||||
|                 int rcpu = gic_get_vcpu_real_id(cpu); | ||||
|                 s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT; | ||||
|             } else if (valid) { | ||||
|                 gic_clear_active(s, irq, cpu); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (irq >= s->num_irq) { | ||||
|         /* This handles two cases:
 | ||||
|          * 1. If software writes the ID of a spurious interrupt [ie 1023] | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Luc Michel
						Luc Michel