diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index af24982911d8..ef5685c9ba51 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1793,6 +1793,14 @@ config BUILD_ARM64_DT_OVERLAY required flags to add DT overlay in the compilation. +config TLB_CONF_HANDLER + bool "enable handler for TLB conflict" + help + This option enables graceful handling of TLB + conflict in EL2. Say yes here to enable TLB + conflict handler. + If unsure, Say n. + config SYSVIPC_COMPAT def_bool y depends on COMPAT && SYSVIPC diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 52905e09899c..d76f4c24e16a 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -24,6 +24,10 @@ #include #include +#ifdef CONFIG_TLB_CONF_HANDLER +#include +#endif + #include #include #include @@ -691,6 +695,15 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs) return 0; } +#ifdef CONFIG_TLB_CONF_HANDLER +static int do_tlb_conf_fault(unsigned long addr, unsigned int esr, struct pt_regs *regs) +{ + if (qcom_scm_tlb_conf_handler(addr)) + return 1; + return 0; +} +#endif + static const struct fault_info fault_info[] = { { do_bad, SIGKILL, SI_KERNEL, "ttbr address size fault" }, { do_bad, SIGKILL, SI_KERNEL, "level 1 address size fault" }, @@ -740,7 +753,11 @@ static const struct fault_info fault_info[] = { { do_bad, SIGKILL, SI_KERNEL, "unknown 45" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 46" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 47" }, +#ifdef CONFIG_TLB_CONF_HANDLER + { do_tlb_conf_fault, SIGKILL, SI_KERNEL, "TLB conflict abort" }, +#else { do_bad, SIGKILL, SI_KERNEL, "TLB conflict abort" }, +#endif { do_bad, SIGKILL, SI_KERNEL, "Unsupported atomic hardware update fault" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 50" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 51" }, diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index a731a5f504f0..66af8140ef2d 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -1020,6 +1020,27 @@ int __qcom_scm_sec_wdog_trigger(struct device *dev) return ret ? : desc.res[0]; } +#ifdef CONFIG_TLB_CONF_HANDLER +int __qcom_scm_tlb_conf_handler(struct device *dev, unsigned long addr) +{ + int ret; + +#define SCM_TLB_CONFLICT_CMD 0x1F + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = SCM_TLB_CONFLICT_CMD, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + desc.args[0] = addr; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call_atomic(dev, &desc); + + return ret ? : desc.res[0]; +} +#endif + void __qcom_scm_disable_sdi(struct device *dev) { int ret; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 276b5861061e..04120e4a2caa 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -160,6 +160,14 @@ int qcom_scm_sec_wdog_trigger(void) } EXPORT_SYMBOL(qcom_scm_sec_wdog_trigger); +#ifdef CONFIG_TLB_CONF_HANDLER +int qcom_scm_tlb_conf_handler(unsigned long addr) +{ + return __qcom_scm_tlb_conf_handler(__scm->dev, addr); +} +EXPORT_SYMBOL(qcom_scm_tlb_conf_handler); +#endif + /** * qcom_scm_disable_sdi() - Disable SDI */ diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index df6449d05c47..445790592780 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -28,6 +28,9 @@ extern void __qcom_scm_cpu_power_down(struct device *dev, u32 flags); extern void __qcom_scm_cpu_hp(struct device *dev, u32 flags); extern int __qcom_scm_sec_wdog_deactivate(struct device *dev); extern int __qcom_scm_sec_wdog_trigger(struct device *dev); +#ifdef CONFIG_TLB_CONF_HANDLER +extern int __qcom_scm_tlb_conf_handler(struct device *dev, unsigned long addr); +#endif extern void __qcom_scm_disable_sdi(struct device *dev); extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id); extern int __qcom_scm_spin_cpu(struct device *dev); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index d924c8f818df..f4d883f158fd 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -86,6 +86,9 @@ extern void qcom_scm_cpu_power_down(u32 flags); extern void qcom_scm_cpu_hp(u32 flags); extern int qcom_scm_sec_wdog_deactivate(void); extern int qcom_scm_sec_wdog_trigger(void); +#ifdef CONFIG_TLB_CONF_HANDLER +extern int qcom_scm_tlb_conf_handler(unsigned long addr); +#endif extern void qcom_scm_disable_sdi(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); extern int qcom_scm_spin_cpu(void); @@ -231,6 +234,9 @@ static inline void qcom_scm_cpu_power_down(u32 flags) {} static inline void qcom_scm_cpu_hp(u32 flags) {} static inline int qcom_scm_sec_wdog_deactivate(void) { return -ENODEV; } static inline int qcom_scm_sec_wdog_trigger(void) { return -ENODEV; } +#ifdef CONFIG_TLB_CONF_HANDLER +static inline int qcom_scm_tlb_conf_handler(unsigned long addr) { return -ENODEV; } +#endif static inline void qcom_scm_disable_sdi(void) {} static inline u32 qcom_scm_set_remote_state(u32 state, u32 id) { return -ENODEV; }