From 29608180d0c8cc6e8893cf0e03e161e94b007638 Mon Sep 17 00:00:00 2001 From: Valentine Sinitsyn Date: Thu, 16 Jul 2015 00:34:47 +0500 Subject: [PATCH] x86: Implement amd_iommu event log 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 [Jan: Cleanups, refactored amd_iommu_print_event] Signed-off-by: Jan Kiszka --- hypervisor/arch/x86/amd_iommu.c | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/hypervisor/arch/x86/amd_iommu.c b/hypervisor/arch/x86/amd_iommu.c index c727937..a7cd832 100644 --- a/hypervisor/arch/x86/amd_iommu.c +++ b/hypervisor/arch/x86/amd_iommu.c @@ -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 */ -- 2.39.2