[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1585596788-193989-7-git-send-email-fenghua.yu@intel.com>
Date: Mon, 30 Mar 2020 12:33:07 -0700
From: Fenghua Yu <fenghua.yu@...el.com>
To: "Thomas Gleixner" <tglx@...utronix.de>,
"Ingo Molnar" <mingo@...hat.com>, "Borislav Petkov" <bp@...en8.de>,
"H Peter Anvin" <hpa@...or.com>,
"David Woodhouse" <dwmw2@...radead.org>,
"Lu Baolu" <baolu.lu@...ux.intel.com>,
"Dave Hansen" <dave.hansen@...el.com>,
"Tony Luck" <tony.luck@...el.com>,
"Ashok Raj" <ashok.raj@...el.com>,
"Jacob Jun Pan" <jacob.jun.pan@...el.com>,
"Dave Jiang" <dave.jiang@...el.com>,
"Sohil Mehta" <sohil.mehta@...el.com>,
"Ravi V Shankar" <ravi.v.shankar@...el.com>
Cc: "linux-kernel" <linux-kernel@...r.kernel.org>,
"x86" <x86@...nel.org>, iommu@...ts.linux-foundation.org,
Fenghua Yu <fenghua.yu@...el.com>
Subject: [PATCH 6/7] x86/traps: Fix up invalid PASID
A #GP fault is generated when ENQCMD instruction is executed without
a valid PASID value programmed in. The #GP fault handler will initialize
the current thread's PASID MSR.
The following heuristic is used to avoid decoding the user instructions
to determine the precise reason for the #GP fault:
1) If the mm for the process has not been allocated a PASID, this #GP
cannot be fixed.
2) If the PASID MSR is already initialized, then the #GP was for some
other reason
3) Try initializing the PASID MSR and returning. If the #GP was from
an ENQCMD this will fix it. If not, the #GP fault will be repeated
and we will hit case "2".
Suggested-by: Thomas Gleixner <tglx@...utronix.de>
Signed-off-by: Fenghua Yu <fenghua.yu@...el.com>
Reviewed-by: Tony Luck <tony.luck@...el.com>
---
arch/x86/include/asm/iommu.h | 1 +
arch/x86/kernel/traps.c | 17 +++++++++++++++++
drivers/iommu/intel-svm.c | 37 ++++++++++++++++++++++++++++++++++++
3 files changed, 55 insertions(+)
diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h
index ed41259fe7ac..e9365a5d6f7d 100644
--- a/arch/x86/include/asm/iommu.h
+++ b/arch/x86/include/asm/iommu.h
@@ -27,5 +27,6 @@ arch_rmrr_sanity_check(struct acpi_dmar_reserved_memory *rmrr)
}
void __free_pasid(struct mm_struct *mm);
+bool __fixup_pasid_exception(void);
#endif /* _ASM_X86_IOMMU_H */
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 6ef00eb6fbb9..369b5ba94635 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -56,6 +56,7 @@
#include <asm/umip.h>
#include <asm/insn.h>
#include <asm/insn-eval.h>
+#include <asm/iommu.h>
#ifdef CONFIG_X86_64
#include <asm/x86_init.h>
@@ -488,6 +489,16 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
return GP_CANONICAL;
}
+static bool fixup_pasid_exception(void)
+{
+ if (!IS_ENABLED(CONFIG_INTEL_IOMMU_SVM))
+ return false;
+ if (!static_cpu_has(X86_FEATURE_ENQCMD))
+ return false;
+
+ return __fixup_pasid_exception();
+}
+
#define GPFSTR "general protection fault"
dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
@@ -499,6 +510,12 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
int ret;
RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU");
+
+ if (user_mode(regs) && fixup_pasid_exception()) {
+ cond_local_irq_enable(regs);
+ return;
+ }
+
cond_local_irq_enable(regs);
if (static_cpu_has(X86_FEATURE_UMIP)) {
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index da718a49e91e..5ed39a022adb 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -759,3 +759,40 @@ void __free_pasid(struct mm_struct *mm)
*/
ioasid_free(pasid);
}
+
+/*
+ * Fix up the PASID MSR if possible.
+ *
+ * But if the #GP was due to another reason, a second #GP might be triggered
+ * to force proper behavior.
+ */
+bool __fixup_pasid_exception(void)
+{
+ struct mm_struct *mm;
+ bool ret = true;
+ u64 pasid_msr;
+ int pasid;
+
+ mm = get_task_mm(current);
+ /* This #GP was triggered from user mode. So mm cannot be NULL. */
+ pasid = mm->context.pasid;
+ /* Ensure this process has been bound to a PASID. */
+ if (!pasid) {
+ ret = false;
+ goto out;
+ }
+
+ /* Check to see if the PASID MSR has already been set for this task. */
+ rdmsrl(MSR_IA32_PASID, pasid_msr);
+ if (pasid_msr & MSR_IA32_PASID_VALID) {
+ ret = false;
+ goto out;
+ }
+
+ /* Fix up the MSR. */
+ wrmsrl(MSR_IA32_PASID, pasid | MSR_IA32_PASID_VALID);
+out:
+ mmput(mm);
+
+ return ret;
+}
--
2.19.1
Powered by blists - more mailing lists