| From foo@baz Mon Apr 10 18:08:16 CEST 2017 |
| From: alexander.levin@verizon.com |
| Date: Tue, 4 Apr 2017 19:32:32 +0000 |
| Subject: firmware: qcom: scm: Fix interrupted SCM calls |
| To: "gregkh@linuxfoundation.org" <gregkh@linuxfoundation.org> |
| Cc: "stable@vger.kernel.org" <stable@vger.kernel.org> |
| Message-ID: <20170404193158.19041-80-alexander.levin@verizon.com> |
| |
| From: Andy Gross <andy.gross@linaro.org> |
| |
| [ Upstream commit 82bcd087029f6056506ea929f11af02622230901 ] |
| |
| This patch adds a Qualcomm specific quirk to the arm_smccc_smc call. |
| |
| On Qualcomm ARM64 platforms, the SMC call can return before it has |
| completed. If this occurs, the call can be restarted, but it requires |
| using the returned session ID value from the interrupted SMC call. |
| |
| The quirk stores off the session ID from the interrupted call in the |
| quirk structure so that it can be used by the caller. |
| |
| This patch folds in a fix given by Sricharan R: |
| https://lkml.org/lkml/2016/9/28/272 |
| |
| Signed-off-by: Andy Gross <andy.gross@linaro.org> |
| Reviewed-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm64/kernel/smccc-call.S | 9 ++++++++- |
| drivers/firmware/qcom_scm-64.c | 13 ++++++++++--- |
| include/linux/arm-smccc.h | 11 ++++++++--- |
| 3 files changed, 26 insertions(+), 7 deletions(-) |
| |
| --- a/arch/arm64/kernel/smccc-call.S |
| +++ b/arch/arm64/kernel/smccc-call.S |
| @@ -12,6 +12,7 @@ |
| * |
| */ |
| #include <linux/linkage.h> |
| +#include <linux/arm-smccc.h> |
| #include <asm/asm-offsets.h> |
| |
| .macro SMCCC instr |
| @@ -20,7 +21,13 @@ |
| ldr x4, [sp] |
| stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS] |
| stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS] |
| - ret |
| + ldr x4, [sp, #8] |
| + cbz x4, 1f /* no quirk structure */ |
| + ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS] |
| + cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6 |
| + b.ne 1f |
| + str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS] |
| +1: ret |
| .cfi_endproc |
| .endm |
| |
| --- a/drivers/firmware/qcom_scm-64.c |
| +++ b/drivers/firmware/qcom_scm-64.c |
| @@ -91,6 +91,7 @@ static int qcom_scm_call(struct device * |
| dma_addr_t args_phys = 0; |
| void *args_virt = NULL; |
| size_t alloc_len; |
| + struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6}; |
| |
| if (unlikely(arglen > N_REGISTER_ARGS)) { |
| alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); |
| @@ -131,10 +132,16 @@ static int qcom_scm_call(struct device * |
| qcom_smccc_convention, |
| ARM_SMCCC_OWNER_SIP, fn_id); |
| |
| + quirk.state.a6 = 0; |
| + |
| do { |
| - arm_smccc_smc(cmd, desc->arginfo, desc->args[0], |
| - desc->args[1], desc->args[2], x5, 0, 0, |
| - res); |
| + arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0], |
| + desc->args[1], desc->args[2], x5, |
| + quirk.state.a6, 0, res, &quirk); |
| + |
| + if (res->a0 == QCOM_SCM_INTERRUPTED) |
| + cmd = res->a0; |
| + |
| } while (res->a0 == QCOM_SCM_INTERRUPTED); |
| |
| mutex_unlock(&qcom_scm_lock); |
| --- a/include/linux/arm-smccc.h |
| +++ b/include/linux/arm-smccc.h |
| @@ -14,9 +14,6 @@ |
| #ifndef __LINUX_ARM_SMCCC_H |
| #define __LINUX_ARM_SMCCC_H |
| |
| -#include <linux/linkage.h> |
| -#include <linux/types.h> |
| - |
| /* |
| * This file provides common defines for ARM SMC Calling Convention as |
| * specified in |
| @@ -60,6 +57,13 @@ |
| #define ARM_SMCCC_OWNER_TRUSTED_OS 50 |
| #define ARM_SMCCC_OWNER_TRUSTED_OS_END 63 |
| |
| +#define ARM_SMCCC_QUIRK_NONE 0 |
| +#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */ |
| + |
| +#ifndef __ASSEMBLY__ |
| + |
| +#include <linux/linkage.h> |
| +#include <linux/types.h> |
| /** |
| * struct arm_smccc_res - Result from SMC/HVC call |
| * @a0-a3 result values from registers 0 to 3 |
| @@ -125,4 +129,5 @@ asmlinkage void __arm_smccc_hvc(unsigned |
| |
| #define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__) |
| |
| +#endif /*__ASSEMBLY__*/ |
| #endif /*__LINUX_ARM_SMCCC_H*/ |