Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+154 -5
+6
arch/powerpc/include/asm/asm-prototypes.h
··· 143 143 void _kvmppc_restore_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr); 144 144 void _kvmppc_save_tm_pr(struct kvm_vcpu *vcpu, u64 guest_msr); 145 145 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 + 146 152 #endif /* _ASM_POWERPC_ASM_PROTOTYPES_H */
+1
arch/powerpc/include/asm/security_features.h
··· 22 22 23 23 void setup_stf_barrier(void); 24 24 void do_stf_barrier_fixups(enum stf_barrier_type types); 25 + void setup_count_cache_flush(void); 25 26 26 27 static inline void security_ftr_set(unsigned long feature) 27 28 {
+54
arch/powerpc/kernel/entry_64.S
··· 25 25 #include <asm/page.h> 26 26 #include <asm/mmu.h> 27 27 #include <asm/thread_info.h> 28 + #include <asm/code-patching-asm.h> 28 29 #include <asm/ppc_asm.h> 29 30 #include <asm/asm-offsets.h> 30 31 #include <asm/cputable.h> ··· 507 506 li r3,0 508 507 b .Lsyscall_exit 509 508 509 + #ifdef CONFIG_PPC_BOOK3S_64 510 + 511 + #define FLUSH_COUNT_CACHE \ 512 + 1: nop; \ 513 + patch_site 1b, patch__call_flush_count_cache 514 + 515 + 516 + #define BCCTR_FLUSH .long 0x4c400420 517 + 518 + .macro nops number 519 + .rept \number 520 + nop 521 + .endr 522 + .endm 523 + 524 + .balign 32 525 + .global flush_count_cache 526 + flush_count_cache: 527 + /* Save LR into r9 */ 528 + mflr r9 529 + 530 + .rept 64 531 + bl .+4 532 + .endr 533 + b 1f 534 + nops 6 535 + 536 + .balign 32 537 + /* Restore LR */ 538 + 1: mtlr r9 539 + li r9,0x7fff 540 + mtctr r9 541 + 542 + BCCTR_FLUSH 543 + 544 + 2: nop 545 + patch_site 2b patch__flush_count_cache_return 546 + 547 + nops 3 548 + 549 + .rept 278 550 + .balign 32 551 + BCCTR_FLUSH 552 + nops 7 553 + .endr 554 + 555 + blr 556 + #else 557 + #define FLUSH_COUNT_CACHE 558 + #endif /* CONFIG_PPC_BOOK3S_64 */ 559 + 510 560 /* 511 561 * This routine switches between two different tasks. The process 512 562 * state of one is saved on its kernel stack. Then the state ··· 588 536 mfcr r23 589 537 std r23,_CCR(r1) 590 538 std r1,KSP(r3) /* Set old stack pointer */ 539 + 540 + FLUSH_COUNT_CACHE 591 541 592 542 /* 593 543 * On SMP kernels, care must be taken because a task may be
+93 -5
arch/powerpc/kernel/security.c
··· 8 8 #include <linux/device.h> 9 9 #include <linux/seq_buf.h> 10 10 11 + #include <asm/asm-prototypes.h> 12 + #include <asm/code-patching.h> 11 13 #include <asm/debugfs.h> 12 14 #include <asm/security_features.h> 13 15 #include <asm/setup.h> 14 16 15 17 16 18 unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; 19 + 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; 17 26 18 27 bool barrier_nospec_enabled; 19 28 static bool no_nospec; ··· 168 159 bcs = security_ftr_enabled(SEC_FTR_BCCTRL_SERIALISED); 169 160 ccd = security_ftr_enabled(SEC_FTR_COUNT_CACHE_DISABLED); 170 161 171 - if (bcs || ccd) { 162 + if (bcs || ccd || count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) { 163 + bool comma = false; 172 164 seq_buf_printf(&s, "Mitigation: "); 173 165 174 - if (bcs) 166 + if (bcs) { 175 167 seq_buf_printf(&s, "Indirect branch serialisation (kernel only)"); 168 + comma = true; 169 + } 176 170 177 - if (bcs && ccd) 171 + if (ccd) { 172 + if (comma) 173 + seq_buf_printf(&s, ", "); 174 + seq_buf_printf(&s, "Indirect branch cache disabled"); 175 + comma = true; 176 + } 177 + 178 + if (comma) 178 179 seq_buf_printf(&s, ", "); 179 180 180 - if (ccd) 181 - seq_buf_printf(&s, "Indirect branch cache disabled"); 181 + seq_buf_printf(&s, "Software count cache flush"); 182 + 183 + if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW) 184 + seq_buf_printf(&s, "(hardware accelerated)"); 182 185 } else 183 186 seq_buf_printf(&s, "Vulnerable"); 184 187 ··· 346 325 return 0; 347 326 } 348 327 device_initcall(stf_barrier_debugfs_init); 328 + #endif /* CONFIG_DEBUG_FS */ 329 + 330 + static void toggle_count_cache_flush(bool enable) 331 + { 332 + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { 333 + patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP); 334 + count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; 335 + pr_info("count-cache-flush: software flush disabled.\n"); 336 + return; 337 + } 338 + 339 + patch_branch_site(&patch__call_flush_count_cache, 340 + (u64)&flush_count_cache, BRANCH_SET_LINK); 341 + 342 + if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { 343 + count_cache_flush_type = COUNT_CACHE_FLUSH_SW; 344 + pr_info("count-cache-flush: full software flush sequence enabled.\n"); 345 + return; 346 + } 347 + 348 + patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR); 349 + count_cache_flush_type = COUNT_CACHE_FLUSH_HW; 350 + pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); 351 + } 352 + 353 + void setup_count_cache_flush(void) 354 + { 355 + toggle_count_cache_flush(true); 356 + } 357 + 358 + #ifdef CONFIG_DEBUG_FS 359 + static int count_cache_flush_set(void *data, u64 val) 360 + { 361 + bool enable; 362 + 363 + if (val == 1) 364 + enable = true; 365 + else if (val == 0) 366 + enable = false; 367 + else 368 + return -EINVAL; 369 + 370 + toggle_count_cache_flush(enable); 371 + 372 + return 0; 373 + } 374 + 375 + static int count_cache_flush_get(void *data, u64 *val) 376 + { 377 + if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE) 378 + *val = 0; 379 + else 380 + *val = 1; 381 + 382 + return 0; 383 + } 384 + 385 + DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get, 386 + count_cache_flush_set, "%llu\n"); 387 + 388 + static __init int count_cache_flush_debugfs_init(void) 389 + { 390 + debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root, 391 + NULL, &fops_count_cache_flush); 392 + return 0; 393 + } 394 + device_initcall(count_cache_flush_debugfs_init); 349 395 #endif /* CONFIG_DEBUG_FS */ 350 396 #endif /* CONFIG_PPC_BOOK3S_64 */