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
Reviewed-by: Tony Luck
---
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
#include
#include
+#include
#include
#include
@@ -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