All tasks start with PASID state disabled. This means that the first
time they execute an ENQCMD instruction they will take a #GP fault.

Modify the #GP fault handler to check if the "mm" for the task has
already been allocated a PASID. If so, try to fix the #GP fault by
loading the IA32_PASID MSR.

Signed-off-by: Fenghua Yu <fenghua...@intel.com>
Reviewed-by: Tony Luck <tony.l...@intel.com>
---
v2:
- Directly write IA32_PASID MSR in fixup while local IRQ is still disabled
  (Thomas)
- Move #ifdef over to CONFIG_IOMMU_SVA since it is what
  defines mm->pasid and ->pasid_activated (Dave Hansen).
- Rename try_fixup_pasid() -> try_fixup_enqcmd_gp(). This
  code really is highly specific to ENQCMD, not PASIDs (Dave Hansen).
- Add lockdep assert and comment about context (Dave Hansen).
- Re-flow the if() mess (Dave Hansen).

 arch/x86/kernel/traps.c | 55 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index c9d566dcf89a..7ef00dee35be 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -39,6 +39,7 @@
 #include <linux/io.h>
 #include <linux/hardirq.h>
 #include <linux/atomic.h>
+#include <linux/ioasid.h>
 
 #include <asm/stacktrace.h>
 #include <asm/processor.h>
@@ -559,6 +560,57 @@ static bool fixup_iopl_exception(struct pt_regs *regs)
        return true;
 }
 
+/*
+ * The unprivileged ENQCMD instruction generates #GPs if the
+ * IA32_PASID MSR has not been populated.  If possible, populate
+ * the MSR from a PASID previously allocated to the mm.
+ */
+static bool try_fixup_enqcmd_gp(void)
+{
+#ifdef CONFIG_IOMMU_SVA
+       u32 pasid;
+
+       /*
+        * MSR_IA32_PASID is managed using XSAVE.  Directly
+        * writing to the MSR is only possible when fpregs
+        * are valid and the fpstate is not.  This is
+        * guaranteed when handling a userspace exception
+        * in *before* interrupts are re-enabled.
+        */
+       lockdep_assert_irqs_disabled();
+
+       /*
+        * Hardware without ENQCMD will not generate
+        * #GPs that can be fixed up here.
+        */
+       if (!cpu_feature_enabled(X86_FEATURE_ENQCMD))
+               return false;
+
+       pasid = current->mm->pasid;
+
+       /*
+        * If the mm has not been allocated a
+        * PASID, the #GP can not be fixed up.
+        */
+       if (!pasid_valid(pasid))
+               return false;
+
+       /*
+        * Did this thread already have its PASID activated?
+        * If so, the #GP must be from something else.
+        */
+       if (current->pasid_activated)
+               return false;
+
+       wrmsrl(MSR_IA32_PASID, pasid | MSR_IA32_PASID_VALID);
+       current->pasid_activated = 1;
+
+       return true;
+#else
+       return false;
+#endif
+}
+
 DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
 {
        char desc[sizeof(GPFSTR) + 50 + 2*sizeof(unsigned long) + 1] = GPFSTR;
@@ -567,6 +619,9 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
        unsigned long gp_addr;
        int ret;
 
+       if (user_mode(regs) && try_fixup_enqcmd_gp())
+               return;
+
        cond_local_irq_enable(regs);
 
        if (static_cpu_has(X86_FEATURE_UMIP)) {
-- 
2.35.0

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to