Hi Jean,

On 1/11/19 6:46 PM, Jean-Philippe Brucker wrote:
> On 08/01/2019 10:26, Eric Auger wrote:
>> When a stage 1 related fault event is read from the event queue,
>> let's propagate it to potential external fault listeners, ie. users
>> who registered a fault handler.
>>
>> Signed-off-by: Eric Auger <eric.au...@redhat.com>
>> ---
>>  drivers/iommu/arm-smmu-v3.c | 124 ++++++++++++++++++++++++++++++++----
>>  1 file changed, 113 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
>> index 999ee470a2ae..6a711cbbb228 100644
>> --- a/drivers/iommu/arm-smmu-v3.c
>> +++ b/drivers/iommu/arm-smmu-v3.c
>> @@ -168,6 +168,26 @@
>>  #define ARM_SMMU_PRIQ_IRQ_CFG1              0xd8
>>  #define ARM_SMMU_PRIQ_IRQ_CFG2              0xdc
>>  
>> +/* Events */
>> +#define ARM_SMMU_EVT_F_UUT          0x01
>> +#define ARM_SMMU_EVT_C_BAD_STREAMID 0x02
>> +#define ARM_SMMU_EVT_F_STE_FETCH    0x03
>> +#define ARM_SMMU_EVT_C_BAD_STE              0x04
>> +#define ARM_SMMU_EVT_F_BAD_ATS_TREQ 0x05
>> +#define ARM_SMMU_EVT_F_STREAM_DISABLED      0x06
>> +#define ARM_SMMU_EVT_F_TRANSL_FORBIDDEN     0x07
>> +#define ARM_SMMU_EVT_C_BAD_SUBSTREAMID      0x08
>> +#define ARM_SMMU_EVT_F_CD_FETCH             0x09
>> +#define ARM_SMMU_EVT_C_BAD_CD               0x0a
>> +#define ARM_SMMU_EVT_F_WALK_EABT    0x0b
>> +#define ARM_SMMU_EVT_F_TRANSLATION  0x10
>> +#define ARM_SMMU_EVT_F_ADDR_SIZE    0x11
>> +#define ARM_SMMU_EVT_F_ACCESS               0x12
>> +#define ARM_SMMU_EVT_F_PERMISSION   0x13
>> +#define ARM_SMMU_EVT_F_TLB_CONFLICT 0x20
>> +#define ARM_SMMU_EVT_F_CFG_CONFLICT 0x21
>> +#define ARM_SMMU_EVT_E_PAGE_REQUEST 0x24
>> +
>>  /* Common MSI config fields */
>>  #define MSI_CFG0_ADDR_MASK          GENMASK_ULL(51, 2)
>>  #define MSI_CFG2_SH                 GENMASK(5, 4)
>> @@ -333,6 +353,11 @@
>>  #define EVTQ_MAX_SZ_SHIFT           7
>>  
>>  #define EVTQ_0_ID                   GENMASK_ULL(7, 0)
>> +#define EVTQ_0_SUBSTREAMID          GENMASK_ULL(31, 12)
>> +#define EVTQ_0_STREAMID                     GENMASK_ULL(63, 32)
>> +#define EVTQ_1_S2                   GENMASK_ULL(39, 39)
>> +#define EVTQ_1_CLASS                        GENMASK_ULL(40, 41)
>> +#define EVTQ_3_FETCH_ADDR           GENMASK_ULL(51, 3)
>>  
>>  /* PRI queue */
>>  #define PRIQ_ENT_DWORDS                     2
>> @@ -1270,7 +1295,6 @@ static int arm_smmu_init_l2_strtab(struct 
>> arm_smmu_device *smmu, u32 sid)
>>      return 0;
>>  }
>>  
>> -__maybe_unused
>>  static struct arm_smmu_master_data *
>>  arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
>>  {
>> @@ -1296,24 +1320,102 @@ arm_smmu_find_master(struct arm_smmu_device *smmu, 
>> u32 sid)
>>      return master;
>>  }
>>  
>> +static void arm_smmu_report_event(struct arm_smmu_device *smmu, u64 *evt)
>> +{
>> +    u64 fetch_addr = FIELD_GET(EVTQ_3_FETCH_ADDR, evt[3]);
>> +    u32 sid = FIELD_GET(EVTQ_0_STREAMID, evt[0]);
>> +    bool s1 = !FIELD_GET(EVTQ_1_S2, evt[1]);
>> +    u8 type = FIELD_GET(EVTQ_0_ID, evt[0]);
>> +    struct arm_smmu_master_data *master;
>> +    struct iommu_fault_event event;
>> +    bool propagate = true;
>> +    u64 addr = evt[2];
>> +    int i;
>> +
>> +    master = arm_smmu_find_master(smmu, sid);
>> +    if (WARN_ON(!master))
>> +            return;
>> +
>> +    event.fault.type = IOMMU_FAULT_DMA_UNRECOV;
>> +
>> +    switch (type) {
>> +    case ARM_SMMU_EVT_C_BAD_STREAMID:
>> +            event.fault.reason = IOMMU_FAULT_REASON_SOURCEID_INVALID;
>> +            break;
>> +    case ARM_SMMU_EVT_F_STREAM_DISABLED:
>> +    case ARM_SMMU_EVT_C_BAD_SUBSTREAMID:
>> +            event.fault.reason = IOMMU_FAULT_REASON_PASID_INVALID;
>> +            break;
>> +    case ARM_SMMU_EVT_F_CD_FETCH:
>> +            event.fault.reason = IOMMU_FAULT_REASON_PASID_FETCH;
>> +            break;
>> +    case ARM_SMMU_EVT_F_WALK_EABT:
>> +            event.fault.reason = IOMMU_FAULT_REASON_WALK_EABT;
>> +            event.fault.addr = addr;
>> +            event.fault.fetch_addr = fetch_addr;
>> +            propagate = s1;
>> +            break;
>> +    case ARM_SMMU_EVT_F_TRANSLATION:
>> +            event.fault.reason = IOMMU_FAULT_REASON_PTE_FETCH;
>> +            event.fault.addr = addr;
>> +            event.fault.fetch_addr = fetch_addr;
>> +            propagate = s1;
>> +            break;
>> +    case ARM_SMMU_EVT_F_PERMISSION:
>> +            event.fault.reason = IOMMU_FAULT_REASON_PERMISSION;
>> +            event.fault.addr = addr;
>> +            propagate = s1;
>> +            break;
>> +    case ARM_SMMU_EVT_F_ACCESS:
>> +            event.fault.reason = IOMMU_FAULT_REASON_ACCESS;
>> +            event.fault.addr = addr;
>> +            propagate = s1;
>> +            break;
>> +    case ARM_SMMU_EVT_C_BAD_STE:
>> +            event.fault.reason = 
>> IOMMU_FAULT_REASON_BAD_DEVICE_CONTEXT_ENTRY;
>> +            break;
>> +    case ARM_SMMU_EVT_C_BAD_CD:
>> +            event.fault.reason = IOMMU_FAULT_REASON_BAD_PASID_ENTRY;
>> +            break;
>> +    case ARM_SMMU_EVT_F_ADDR_SIZE:
>> +            event.fault.reason = IOMMU_FAULT_REASON_OOR_ADDRESS;
>> +            propagate = s1;
>> +            break;
>> +    case ARM_SMMU_EVT_F_STE_FETCH:
>> +            event.fault.reason = IOMMU_FAULT_REASON_DEVICE_CONTEXT_FETCH;
>> +            event.fault.fetch_addr = fetch_addr;
>> +            break;
>> +    /* End of addition */
>> +    case ARM_SMMU_EVT_E_PAGE_REQUEST:
>> +    case ARM_SMMU_EVT_F_TLB_CONFLICT:
>> +    case ARM_SMMU_EVT_F_CFG_CONFLICT:
>> +    case ARM_SMMU_EVT_F_BAD_ATS_TREQ:
>> +    case ARM_SMMU_EVT_F_TRANSL_FORBIDDEN:
>> +    case ARM_SMMU_EVT_F_UUT:
>> +    default:
>> +            event.fault.reason = IOMMU_FAULT_REASON_UNKNOWN;
>> +    }
>> +    /* only propagate the error if it relates to stage 1 */
>> +    if (s1)
> 
> if (propagate)
> 
> But I don't quite understand how we're deciding what to propagate: a
> C_BAD_STE is most likely a bug in the SMMU driver, but is reported to
> userspace. On the other hand a stage-2 F_TRANSLATION is likely an error
> from the VMM (didn't setup stage-2 mappings properly), but we're not
> reporting it. Maybe we should add a bit to event.fault that tells
> whether the fault was stage 1 or 2, and let the VMM deal with it?
Yes I mixed this up. propagate should be false by default. In case the
event has an S2 field and S2 == 0 we can safely propagate to the guest.
Otherwise it is case by case and I need to do another review. If it
relates to structures owned by the guest propagate.
> 
>> +            iommu_report_device_fault(master->dev, &event);
> 
> We should return here if the fault is successfully injected

Even if the fault gets injected in the guest can't it be still useful to
get the message below on host side?

Thanks

Eric
> 
> Thanks,
> Jean
> 
>> +
>> +    dev_info(smmu->dev, "event 0x%02x received:\n", type);
>> +    for (i = 0; i < EVTQ_ENT_DWORDS; ++i) {
>> +            dev_info(smmu->dev, "\t0x%016llx\n",
>> +                     (unsigned long long)evt[i]);
>> +    }
>> +}
>> +
>>  /* IRQ and event handlers */
>>  static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
>>  {
>> -    int i;
>>      struct arm_smmu_device *smmu = dev;
>>      struct arm_smmu_queue *q = &smmu->evtq.q;
>>      u64 evt[EVTQ_ENT_DWORDS];
>>  
>>      do {
>> -            while (!queue_remove_raw(q, evt)) {
>> -                    u8 id = FIELD_GET(EVTQ_0_ID, evt[0]);
>> -
>> -                    dev_info(smmu->dev, "event 0x%02x received:\n", id);
>> -                    for (i = 0; i < ARRAY_SIZE(evt); ++i)
>> -                            dev_info(smmu->dev, "\t0x%016llx\n",
>> -                                     (unsigned long long)evt[i]);
>> -
>> -            }
>> +            while (!queue_remove_raw(q, evt))
>> +                    arm_smmu_report_event(smmu, evt);
>>  
>>              /*
>>               * Not much we can do on overflow, so scream and pretend we're
>>
> 
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to