Skip to content

Commit ee13cb2

Browse files
committed
powerpc/64s: Add support for software count cache flush
Some CPU revisions support a mode where the count cache needs to be flushed by software on context switch. Additionally some revisions may have a hardware accelerated flush, in which case the software flush sequence can be shortened. If we detect the appropriate flag from firmware we patch a branch into _switch() which takes us to a count cache flush sequence. That sequence in turn may be patched to return early if we detect that the CPU supports accelerating the flush sequence in hardware. Add debugfs support for reporting the state of the flush, as well as runtime disabling it. And modify the spectre_v2 sysfs file to report the state of the software flush. Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent dc8c6cc commit ee13cb2

File tree

4 files changed

+154
-5
lines changed

4 files changed

+154
-5
lines changed

arch/powerpc/include/asm/asm-prototypes.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,10 @@ struct kvm_vcpu;
143143
void _kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
144144
void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr);
145145

146+
/* Patch sites */
147+
extern s32 patch__call_flush_count_cache;
148+
extern s32 patch__flush_count_cache_return;
149+
150+
extern long flush_count_cache;
151+
146152
#endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */

arch/powerpc/include/asm/security_features.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum stf_barrier_type {
2222

2323
void setup_stf_barrier(void);
2424
void do_stf_barrier_fixups(enum stf_barrier_type types);
25+
void setup_count_cache_flush(void);
2526

2627
static inline void security_ftr_set(unsigned long feature)
2728
{

arch/powerpc/kernel/entry_64.S

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <asm/page.h>
2626
#include <asm/mmu.h>
2727
#include <asm/thread_info.h>
28+
#include <asm/code-patching-asm.h>
2829
#include <asm/ppc_asm.h>
2930
#include <asm/asm-offsets.h>
3031
#include <asm/cputable.h>
@@ -506,6 +507,57 @@ _GLOBAL(ret_from_kernel_thread)
506507
li r3,0
507508
b .Lsyscall_exit
508509

510+
#ifdef CONFIG_PPC_BOOK3S_64
511+
512+
#define FLUSH_COUNT_CACHE \
513+
1: nop; \
514+
patch_site 1b, patch__call_flush_count_cache
515+
516+
517+
#define BCCTR_FLUSH .long 0x4c400420
518+
519+
.macro nops number
520+
.rept \number
521+
nop
522+
.endr
523+
.endm
524+
525+
.balign 32
526+
.global flush_count_cache
527+
flush_count_cache:
528+
/* Save LR into r9 */
529+
mflr r9
530+
531+
.rept 64
532+
bl .+4
533+
.endr
534+
b 1f
535+
nops 6
536+
537+
.balign 32
538+
/* Restore LR */
539+
1: mtlr r9
540+
li r9,0x7fff
541+
mtctr r9
542+
543+
BCCTR_FLUSH
544+
545+
2: nop
546+
patch_site 2b patch__flush_count_cache_return
547+
548+
nops 3
549+
550+
.rept 278
551+
.balign 32
552+
BCCTR_FLUSH
553+
nops 7
554+
.endr
555+
556+
blr
557+
#else
558+
#define FLUSH_COUNT_CACHE
559+
#endif /* CONFIG_PPC_BOOK3S_64 */
560+
509561
/*
510562
* This routine switches between two different tasks. The process
511563
* state of one is saved on its kernel stack. Then the state
@@ -537,6 +589,8 @@ _GLOBAL(_switch)
537589
std r23,_CCR(r1)
538590
std r1,KSP(r3) /* Set old stack pointer */
539591

592+
FLUSH_COUNT_CACHE
593+
540594
/*
541595
* On SMP kernels, care must be taken because a task may be
542596
* scheduled off CPUx and on to CPUy. Memory ordering must be

arch/powerpc/kernel/security.c

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@
88
#include <linux/device.h>
99
#include <linux/seq_buf.h>
1010

11+
#include <asm/asm-prototypes.h>
12+
#include <asm/code-patching.h>
1113
#include <asm/debugfs.h>
1214
#include <asm/security_features.h>
1315
#include <asm/setup.h>
1416

1517

1618
unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT;
1719

20+
enum count_cache_flush_type {
21+
COUNT_CACHE_FLUSH_NONE = 0x1,
22+
COUNT_CACHE_FLUSH_SW = 0x2,
23+
COUNT_CACHE_FLUSH_HW = 0x4,
24+
};
25+
static enum count_cache_flush_type count_cache_flush_type;
26+
1827
bool barrier_nospec_enabled;
1928
static bool no_nospec;
2029

@@ -159,17 +168,29 @@ ssize_t cpu_show_spectre_v2(struct device *dev, struct device_attribute *attr, c
159168
bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED);
160169
ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED);
161170

162-
if (bcs || ccd) {
171+
if (bcs || ccd || count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) {
172+
bool comma = false;
163173
seq_buf_printf(&s, "Mitigation: ");
164174

165-
if (bcs)
175+
if (bcs) {
166176
seq_buf_printf(&s, "Indirect branch serialisation (kernel only)");
177+
comma = true;
178+
}
179+
180+
if (ccd) {
181+
if (comma)
182+
seq_buf_printf(&s, ", ");
183+
seq_buf_printf(&s, "Indirect branch cache disabled");
184+
comma = true;
185+
}
167186

168-
if (bcs && ccd)
187+
if (comma)
169188
seq_buf_printf(&s, ", ");
170189

171-
if (ccd)
172-
seq_buf_printf(&s, "Indirect branch cache disabled");
190+
seq_buf_printf(&s, "Software count cache flush");
191+
192+
if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW)
193+
seq_buf_printf(&s, "(hardware accelerated)");
173194
} else
174195
seq_buf_printf(&s, "Vulnerable");
175196

@@ -326,4 +347,71 @@ static __init int stf_barrier_debugfs_init(void)
326347
}
327348
device_initcall(stf_barrier_debugfs_init);
328349
#endif /* CONFIG_DEBUG_FS */
350+
351+
static void toggle_count_cache_flush(bool enable)
352+
{
353+
if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) {
354+
patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP);
355+
count_cache_flush_type = COUNT_CACHE_FLUSH_NONE;
356+
pr_info("count-cache-flush: software flush disabled.\n");
357+
return;
358+
}
359+
360+
patch_branch_site(&patch__call_flush_count_cache,
361+
(u64)&flush_count_cache, BRANCH_SET_LINK);
362+
363+
if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) {
364+
count_cache_flush_type = COUNT_CACHE_FLUSH_SW;
365+
pr_info("count-cache-flush: full software flush sequence enabled.\n");
366+
return;
367+
}
368+
369+
patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR);
370+
count_cache_flush_type = COUNT_CACHE_FLUSH_HW;
371+
pr_info("count-cache-flush: hardware assisted flush sequence enabled\n");
372+
}
373+
374+
void setup_count_cache_flush(void)
375+
{
376+
toggle_count_cache_flush(true);
377+
}
378+
379+
#ifdef CONFIG_DEBUG_FS
380+
static int count_cache_flush_set(void *data, u64 val)
381+
{
382+
bool enable;
383+
384+
if (val == 1)
385+
enable = true;
386+
else if (val == 0)
387+
enable = false;
388+
else
389+
return -EINVAL;
390+
391+
toggle_count_cache_flush(enable);
392+
393+
return 0;
394+
}
395+
396+
static int count_cache_flush_get(void *data, u64 *val)
397+
{
398+
if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE)
399+
*val = 0;
400+
else
401+
*val = 1;
402+
403+
return 0;
404+
}
405+
406+
DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get,
407+
count_cache_flush_set, "%llu\n");
408+
409+
static __init int count_cache_flush_debugfs_init(void)
410+
{
411+
debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root,
412+
NULL, &fops_count_cache_flush);
413+
return 0;
414+
}
415+
device_initcall(count_cache_flush_debugfs_init);
416+
#endif /* CONFIG_DEBUG_FS */
329417
#endif /* CONFIG_PPC_BOOK3S_64 */

0 commit comments

Comments
 (0)