From f056b8a329528d4ddcbf881bb788dcdedbda7a0a Mon Sep 17 00:00:00 2001 From: Naina Mehta Date: Mon, 26 Jul 2021 16:34:55 +0530 Subject: [PATCH] arm64: Add TLB conflict fault handler Add TLB conflict fault handler and try to handle gracefully. Check whether TLB fault can be handled by EL2 and cause panic if EL2 is not able to handle. Change-Id: I276ec5413411932bd8a67ed4c85ebbf66f4affcf Signed-off-by: Runmin Wang Signed-off-by: Prasad Sodagudi Signed-off-by: Naina Mehta --- arch/arm64/Kconfig | 8 ++++++++ arch/arm64/mm/fault.c | 17 +++++++++++++++++ drivers/firmware/qcom_scm-smc.c | 21 +++++++++++++++++++++ drivers/firmware/qcom_scm.c | 8 ++++++++ drivers/firmware/qcom_scm.h | 3 +++ include/linux/qcom_scm.h | 6 ++++++ 6 files changed, 63 insertions(+) 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; }