Skip to content

Commit cfa7ff9

Browse files
brooniewilldeacon
authored andcommitted
arm64: smccc: Support SMCCC v1.3 SVE register saving hint
SMCCC v1.2 requires that all SVE state be preserved over SMC calls which introduces substantial overhead in the common case where there is no SVE state in the registers. To avoid this SMCCC v1.3 introduces a flag which allows the caller to say that there is no state that needs to be preserved in the registers. Make use of this flag, setting it if the SMCCC version indicates support for it and the TIF_ flags indicate that there is no live SVE state in the registers, this avoids placing any constraints on when SMCCC calls can be done or triggering extra saving and reloading of SVE register state in the kernel. This would be straightforward enough except for the rather entertaining inline assembly we use to do SMCCC v1.1 calls to allow us to take advantage of the limited number of registers it clobbers. Deal with this by having a function which we call immediately before issuing the SMCCC call to make our checks and set the flag. Using alternatives the overhead if SVE is supported but not detected at runtime can be reduced to a single NOP. Signed-off-by: Mark Brown <broonie@kernel.org> Reviewed-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210603184118.15090-1-broonie@kernel.org Signed-off-by: Will Deacon <will@kernel.org>
1 parent 57ad4fe commit cfa7ff9

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

arch/arm64/kernel/smccc-call.S

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,34 @@
77

88
#include <asm/asm-offsets.h>
99
#include <asm/assembler.h>
10+
#include <asm/thread_info.h>
11+
12+
/*
13+
* If we have SMCCC v1.3 and (as is likely) no SVE state in
14+
* the registers then set the SMCCC hint bit to say there's no
15+
* need to preserve it. Do this by directly adjusting the SMCCC
16+
* function value which is already stored in x0 ready to be called.
17+
*/
18+
SYM_FUNC_START(__arm_smccc_sve_check)
19+
20+
ldr_l x16, smccc_has_sve_hint
21+
cbz x16, 2f
22+
23+
get_current_task x16
24+
ldr x16, [x16, #TSK_TI_FLAGS]
25+
tbnz x16, #TIF_FOREIGN_FPSTATE, 1f // Any live FP state?
26+
tbnz x16, #TIF_SVE, 2f // Does that state include SVE?
27+
28+
1: orr x0, x0, ARM_SMCCC_1_3_SVE_HINT
29+
30+
2: ret
31+
SYM_FUNC_END(__arm_smccc_sve_check)
32+
EXPORT_SYMBOL(__arm_smccc_sve_check)
1033

1134
.macro SMCCC instr
35+
alternative_if ARM64_SVE
36+
bl __arm_smccc_sve_check
37+
alternative_else_nop_endif
1238
\instr #0
1339
ldr x4, [sp]
1440
stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]

drivers/firmware/smccc/smccc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
1515
static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
1616

1717
bool __ro_after_init smccc_trng_available = false;
18+
u64 __ro_after_init smccc_has_sve_hint = false;
1819

1920
void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
2021
{
2122
smccc_version = version;
2223
smccc_conduit = conduit;
2324

2425
smccc_trng_available = smccc_probe_trng();
26+
if (IS_ENABLED(CONFIG_ARM64_SVE) &&
27+
smccc_version >= ARM_SMCCC_VERSION_1_3)
28+
smccc_has_sve_hint = true;
2529
}
2630

2731
enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)

include/linux/arm-smccc.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
#define ARM_SMCCC_VERSION_1_0 0x10000
6464
#define ARM_SMCCC_VERSION_1_1 0x10001
6565
#define ARM_SMCCC_VERSION_1_2 0x10002
66+
#define ARM_SMCCC_VERSION_1_3 0x10003
67+
68+
#define ARM_SMCCC_1_3_SVE_HINT 0x10000
6669

6770
#define ARM_SMCCC_VERSION_FUNC_ID \
6871
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
@@ -216,6 +219,8 @@ u32 arm_smccc_get_version(void);
216219

217220
void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit);
218221

222+
extern u64 smccc_has_sve_hint;
223+
219224
/**
220225
* struct arm_smccc_res - Result from SMC/HVC call
221226
* @a0-a3 result values from registers 0 to 3
@@ -295,6 +300,15 @@ struct arm_smccc_quirk {
295300
} state;
296301
};
297302

303+
/**
304+
* __arm_smccc_sve_check() - Set the SVE hint bit when doing SMC calls
305+
*
306+
* Sets the SMCCC hint bit to indicate if there is live state in the SVE
307+
* registers, this modifies x0 in place and should never be called from C
308+
* code.
309+
*/
310+
asmlinkage unsigned long __arm_smccc_sve_check(unsigned long x0);
311+
298312
/**
299313
* __arm_smccc_smc() - make SMC calls
300314
* @a0-a7: arguments passed in registers 0 to 7
@@ -352,6 +366,20 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
352366

353367
#endif
354368

369+
/* nVHE hypervisor doesn't have a current thread so needs separate checks */
370+
#if defined(CONFIG_ARM64_SVE) && !defined(__KVM_NVHE_HYPERVISOR__)
371+
372+
#define SMCCC_SVE_CHECK ALTERNATIVE("nop \n", "bl __arm_smccc_sve_check \n", \
373+
ARM64_SVE)
374+
#define smccc_sve_clobbers "x16", "x30", "cc",
375+
376+
#else
377+
378+
#define SMCCC_SVE_CHECK
379+
#define smccc_sve_clobbers
380+
381+
#endif
382+
355383
#define ___count_args(_0, _1, _2, _3, _4, _5, _6, _7, _8, x, ...) x
356384

357385
#define __count_args(...) \
@@ -419,7 +447,7 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
419447

420448
#define ___constraints(count) \
421449
: __constraint_read_ ## count \
422-
: "memory"
450+
: smccc_sve_clobbers "memory"
423451
#define __constraints(count) ___constraints(count)
424452

425453
/*
@@ -434,7 +462,8 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
434462
register unsigned long r2 asm("r2"); \
435463
register unsigned long r3 asm("r3"); \
436464
__declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
437-
asm volatile(inst "\n" : \
465+
asm volatile(SMCCC_SVE_CHECK \
466+
inst "\n" : \
438467
"=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) \
439468
__constraints(__count_args(__VA_ARGS__))); \
440469
if (___res) \

0 commit comments

Comments
 (0)