]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
x86: Implement amd_iommu event log
authorValentine Sinitsyn <valentine.sinitsyn@gmail.com>
Wed, 15 Jul 2015 19:34:47 +0000 (00:34 +0500)
committerJan Kiszka <jan.kiszka@siemens.com>
Wed, 16 Mar 2016 08:23:53 +0000 (09:23 +0100)
Add functions to read event logs AMD IOMMU provides and print their
contents. The latter is rather basic, but decoding all possible log
entries is hairy, so we'd better wait and collect stats which
problems occur most often.

Signed-off-by: Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
[Jan: Cleanups, refactored amd_iommu_print_event]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/x86/amd_iommu.c

index c72793784a139a2a93fd3140d65cbaff541ceaea..a7cd8321a3a3cc9117c13d17368544878a5809a0 100644 (file)
@@ -788,6 +788,79 @@ void iommu_shutdown(void)
        }
 }
 
+static void amd_iommu_print_event(struct amd_iommu *iommu,
+                                 union buf_entry *entry)
+{
+       printk("AMD IOMMU %d reported event\n", iommu->idx);
+       printk(" EventCode: %lx, Operand 1: %lx, Operand 2: %lx\n",
+              entry->type, entry->raw64[0], entry->raw64[1]);
+       switch (entry->type) {
+               case EVENT_TYPE_ILL_DEV_TAB_ENTRY...EVENT_TYPE_PAGE_TAB_HW_ERR:
+               case EVENT_TYPE_IOTLB_INV_TIMEOUT...EVENT_TYPE_INV_PPR_REQ:
+                       printk(" DeviceId (bus:dev.func): %02x:%02x.%x\n",
+                              PCI_BDF_PARAMS(entry->raw32[0] & 0xffff));
+                       break;
+               case EVENT_TYPE_ILL_CMD_ERR:
+               case EVENT_TYPE_CMD_HW_ERR:
+                       panic_printk("FATAL: IOMMU %d command error\n");
+                       panic_stop();
+       }
+}
+
+static void amd_iommu_restart_event_log(struct amd_iommu *iommu)
+{
+       void *base = iommu->mmio_base;
+
+       wait_for_zero(base + AMD_STATUS_REG, AMD_STATUS_EVT_LOG_RUN);
+
+       mmio_write64_field(base + AMD_CONTROL_REG, AMD_CONTROL_EVT_LOG_EN, 0);
+
+       /* Simply start from the scratch */
+       mmio_write64(base + AMD_EVT_LOG_HEAD_REG, 0);
+       mmio_write64(base + AMD_EVT_LOG_TAIL_REG, 0);
+
+       /* Clear EventOverflow (RW1C) */
+       mmio_write64_field(base + AMD_STATUS_REG, AMD_STATUS_EVT_OVERFLOW, 1);
+
+       /* Bring logging back */
+       mmio_write64_field(base + AMD_CONTROL_REG, AMD_CONTROL_EVT_LOG_EN, 1);
+}
+
+static void amd_iommu_poll_events(struct amd_iommu *iommu)
+{
+       union buf_entry *evt;
+       u32 head, tail;
+       u64 status;
+
+       status = mmio_read64(iommu->mmio_base + AMD_STATUS_REG);
+
+       if (status & AMD_STATUS_EVT_OVERFLOW) {
+               printk("IOMMU %d: Event Log overflow occurred, "
+                      "some events were lost!\n", iommu->idx);
+               amd_iommu_restart_event_log(iommu);
+       }
+
+       while (status & AMD_STATUS_EVT_LOG_INT) {
+               /* Clear EventLogInt (RW1C) */
+               mmio_write64_field(iommu->mmio_base + AMD_STATUS_REG,
+                                  AMD_STATUS_EVT_LOG_INT, 1);
+
+               head = mmio_read32(iommu->mmio_base + AMD_EVT_LOG_HEAD_REG);
+               tail = mmio_read32(iommu->mmio_base + AMD_EVT_LOG_TAIL_REG);
+
+               while (head != tail) {
+                       evt = (union buf_entry *)(iommu->evt_log_base + head);
+                       amd_iommu_print_event(iommu, evt);
+                       head = (head + sizeof(*evt)) % EVT_LOG_SIZE;
+               }
+
+               mmio_write32(iommu->evt_log_base + AMD_EVT_LOG_HEAD_REG, head);
+
+               /* Re-read status to catch new events, as Linux does */
+               status = mmio_read64(iommu->mmio_base + AMD_STATUS_REG);
+       }
+}
+
 void iommu_check_pending_faults(void)
 {
        /* TODO: Implement */