Received: by 2002:a05:6a10:1287:0:0:0:0 with SMTP id d7csp235599pxv; Wed, 21 Jul 2021 21:33:59 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxWFNS1en9zzk9isw/h2jW32E8WZzRN/+UvD9vbDWfK78rJ508HTbr1mkQmzdjh/1MJj5MZ X-Received: by 2002:a17:906:af91:: with SMTP id mj17mr41974574ejb.76.1626928439413; Wed, 21 Jul 2021 21:33:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1626928439; cv=none; d=google.com; s=arc-20160816; b=taQwFOftMIAgcIwkEvvJro+U6xAG08DQwMDvIHAiNBg3eoiLnGz9ZbWYZmxe14psf4 /9gVF5IxHpaSy0RFvnHEEPehHfU1u/QSGG7bWLjEzF/gMOggOE7+ABs1ixPd7EvEiQd9 ZdsRzTdjcGe56Imi3XxkyT/HdXQpV9JT69P11g77ZuBvrlBUyO5lvyk4fL2ALMt/Na1d m/T3tHKOb2a4oVpiEI/uPWtmlALise4tTY4snUTTaMJS19LrL6p1Zc8hedYi7O9AM9Aq kMTCqy08HFXYLk9SYFCzezDeqvnRa6rLoScNdFsIveC/FJcon0PEHZAo1bz2OQ5uj0ja Wupg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:message-id:date:subject:cc:to:from; bh=gxn/y9qtqvoSnMEr7aD8TvPjuI6YM+m+8g9jvaQ45Ng=; b=qpdFXNJqHYmEO4dZDn5LlKCOqN7bvKiLcjEE1I26cGt5f2lGQE580cLMjgXv38ngpL ROIftHc3aIdv+ZooNq0YzB7JmMGKIsa898v38BYWWbEy8C2el8Ua24sMva0MDMhrFIYT ry+EBc+f3u0hKqEP44NW9Et8kC7T5r5W1uHB2IwrMFr3kmf1d0M8Wrvd2LqHVbNx3T1J SnXxTpwdb4oii2cyiM7nw1rHxhyomveZF/hURY2/tfge+LR6IVpbr3nAYu+n74nqVThV uAG3TfWXnfjqTf4Aktkm3ycU5ofFRYMWSAhMkplheMJdulGNjv5kRRLNiun0NMavZaju gUWg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id y24si28886341ejr.702.2021.07.21.21.33.21; Wed, 21 Jul 2021 21:33:59 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229934AbhGVDuY (ORCPT + 99 others); Wed, 21 Jul 2021 23:50:24 -0400 Received: from mga17.intel.com ([192.55.52.151]:9384 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229905AbhGVDuX (ORCPT ); Wed, 21 Jul 2021 23:50:23 -0400 X-IronPort-AV: E=McAfee;i="6200,9189,10052"; a="191838730" X-IronPort-AV: E=Sophos;i="5.84,259,1620716400"; d="scan'208";a="191838730" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Jul 2021 21:30:57 -0700 X-IronPort-AV: E=Sophos;i="5.84,259,1620716400"; d="scan'208";a="454612687" Received: from km-skylake-client-platform.sc.intel.com ([172.25.103.115]) by orsmga007-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Jul 2021 21:30:57 -0700 From: Kyung Min Park To: iommu@lists.linux-foundation.org, linux-kernel@vger.kernel.org Cc: baolu.lu@linux.intel.com, dwmw2@infradead.org, joro@8bytes.org, will@kernel.org, yian.chen@intel.com, sohil.mehta@intel.com, ravi.v.shankar@intel.com, ashok.raj@intel.com, Kyung Min Park Subject: [PATCH v2] iommu/vt-d: Dump DMAR translation structure Date: Wed, 21 Jul 2021 21:24:53 -0700 Message-Id: <20210722042453.10579-1-kyung.min.park@intel.com> X-Mailer: git-send-email 2.17.1 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When the dmar translation fault happens, the kernel prints a single line fault reason with corresponding hexadecimal code defined in the Intel VT-d specification. Currently, when user wants to debug the translation fault in detail, debugfs is used for dumping the dmar_translation_struct, which is not available when the kernel failed to boot. Dump the DMAR translation structure, pagewalk the IO page table and print the page table entry when the fault happens. Signed-off-by: Kyung Min Park --- ChangeLog: - Change from v1 to v2: 1. fix compilation issue with different IOMMU config option. --- drivers/iommu/intel/dmar.c | 8 ++- drivers/iommu/intel/iommu.c | 107 ++++++++++++++++++++++++++++++++++++ include/linux/dmar.h | 4 ++ 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c index d66f79acd14d..c74d97c5ec21 100644 --- a/drivers/iommu/intel/dmar.c +++ b/drivers/iommu/intel/dmar.c @@ -1948,19 +1948,21 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, source_id >> 8, PCI_SLOT(source_id & 0xFF), PCI_FUNC(source_id & 0xFF), addr >> 48, fault_reason, reason); - else if (pasid == INVALID_IOASID) + else if (pasid == INVALID_IOASID) { pr_err("[%s NO_PASID] Request device [0x%02x:0x%02x.%d] fault addr 0x%llx [fault reason 0x%02x] %s\n", type ? "DMA Read" : "DMA Write", source_id >> 8, PCI_SLOT(source_id & 0xFF), PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); - else + dmar_fault_dump_ptes(iommu, source_id, addr, pasid); + } else { pr_err("[%s PASID 0x%x] Request device [0x%02x:0x%02x.%d] fault addr 0x%llx [fault reason 0x%02x] %s\n", type ? "DMA Read" : "DMA Write", pasid, source_id >> 8, PCI_SLOT(source_id & 0xFF), PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); - + dmar_fault_dump_ptes(iommu, source_id, addr, pasid); + } return 0; } diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index a6a07d985709..859ec0832429 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -173,6 +173,8 @@ static struct intel_iommu **g_iommus; static void __init check_tylersburg_isoch(void); static int rwbf_quirk; +static inline struct device_domain_info * +dmar_search_domain_by_dev_info(int segment, int bus, int devfn); /* * set to 1 to panic kernel if can't successfully enable VT-d @@ -998,6 +1000,111 @@ static void free_context_table(struct intel_iommu *iommu) spin_unlock_irqrestore(&iommu->lock, flags); } +static void pgtable_walk(struct intel_iommu *iommu, unsigned long pfn, u8 bus, u8 devfn) +{ + struct dma_pte *parent, *pte = NULL; + struct device_domain_info *info; + struct dmar_domain *domain = NULL; + int offset, level; + + info = dmar_search_domain_by_dev_info(iommu->segment, bus, devfn); + if (!info) { + pr_info("no iommu info for this device.\n"); + return; + } + + domain = info->domain; + if (!domain) { + pr_info("iommu domain does not exist for this device.\n"); + return; + } + level = agaw_to_level(domain->agaw); + parent = domain->pgd; + if (!parent) { + pr_info("NULL pointer of page table entry.\n"); + return; + } + + while (1) { + offset = pfn_level_offset(pfn, level); + pte = &parent[offset]; + if (!pte || (dma_pte_superpage(pte) || !dma_pte_present(pte))) { + pr_info("pte not present at level %d", level); + break; + } + pr_info("pte level: %d, pte value: %llx\n", level, pte->val); + + if (level == 1) + break; + parent = phys_to_virt(dma_pte_addr(pte)); + level--; + } +} + +void dmar_fault_dump_ptes(struct intel_iommu *iommu, u16 source_id, unsigned long long addr, + u32 pasid) +{ + struct pasid_dir_entry *dir, *pde; + struct pasid_entry *entries, *pte; + struct context_entry *ctx_entry; + struct root_entry *rt_entry; + u8 devfn = source_id & 0xff; + u8 bus = source_id >> 8; + int i, dir_index, index; + + /* root entry dump */ + rt_entry = &iommu->root_entry[bus]; + if (!rt_entry) { + pr_info("root table entry is not present\n"); + return; + } + if (!sm_supported(iommu)) + pr_info("%s, root_entry[63:0]: %llx", iommu->name, rt_entry->lo); + else + pr_info("%s, root_entry[127:0]: %llx, %llx\n", iommu->name, + rt_entry->hi, rt_entry->lo); + + /* context entry dump */ + ctx_entry = iommu_context_addr(iommu, bus, devfn, 0); + if (!ctx_entry) { + pr_info("context table entry is not present\n"); + return; + } + pr_info("%s, ctx_entry[127:0]: %llx, %llx\n", iommu->name, ctx_entry->hi, ctx_entry->lo); + + /* legacy mode does not require PASID entries */ + if (!sm_supported(iommu)) + goto pgtable_walk; + + /* get the pointer to pasid directory entry */ + dir = phys_to_virt(ctx_entry->lo & VTD_PAGE_MASK); + if (!dir) { + pr_info("pasid directory entry is not present\n"); + return; + } + /* For request-without-pasid, get the pasid from context entry */ + if (intel_iommu_sm && pasid == INVALID_IOASID) + pasid = PASID_RID2PASID; + + dir_index = pasid >> PASID_PDE_SHIFT; + pde = &dir[dir_index]; + pr_info("%s, pasid_dir_entry: %llx\n", iommu->name, pde->val); + + /* get the pointer to the pasid table entry */ + entries = get_pasid_table_from_pde(pde); + if (!entries) { + pr_info("pasid table entry is not present\n"); + return; + } + index = pasid & PASID_PTE_MASK; + pte = &entries[index]; + for (i = 0; i < ARRAY_SIZE(pte->val); i++) + pr_info("%s, pasid_table_entry[%d]: %llx\n", iommu->name, i, pte->val[i]); + +pgtable_walk: + pgtable_walk(iommu, addr >> VTD_PAGE_SHIFT, bus, devfn); +} + static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, unsigned long pfn, int *target_level) { diff --git a/include/linux/dmar.h b/include/linux/dmar.h index e04436a7ff27..cdcb1b6d85b9 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -142,9 +142,13 @@ extern int dmar_parse_one_satc(struct acpi_dmar_header *hdr, void *arg); extern int dmar_release_one_atsr(struct acpi_dmar_header *hdr, void *arg); extern int dmar_iommu_hotplug(struct dmar_drhd_unit *dmaru, bool insert); extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); +void dmar_fault_dump_ptes(struct intel_iommu *iommu, u16 source_id, + unsigned long long addr, u32 pasid); #else /* !CONFIG_INTEL_IOMMU: */ static inline int intel_iommu_init(void) { return -ENODEV; } static inline void intel_iommu_shutdown(void) { } +static inline void dmar_fault_dump_ptes(struct intel_iommu *iommu, u16 source_id, + unsigned long long addr, u32 pasid) {} #define dmar_parse_one_rmrr dmar_res_noop #define dmar_parse_one_atsr dmar_res_noop -- 2.17.1