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 <t...@linutronix.de> Signed-off-by: Fenghua Yu <fenghua...@intel.com> Reviewed-by: Tony Luck <tony.l...@intel.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 _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu