.. | .. |
---|
7 | 7 | #include <linux/cpu.h> |
---|
8 | 8 | #include <linux/kernel.h> |
---|
9 | 9 | #include <linux/device.h> |
---|
| 10 | +#include <linux/nospec.h> |
---|
| 11 | +#include <linux/prctl.h> |
---|
10 | 12 | #include <linux/seq_buf.h> |
---|
11 | 13 | |
---|
12 | 14 | #include <asm/asm-prototypes.h> |
---|
.. | .. |
---|
14 | 16 | #include <asm/debugfs.h> |
---|
15 | 17 | #include <asm/security_features.h> |
---|
16 | 18 | #include <asm/setup.h> |
---|
| 19 | +#include <asm/inst.h> |
---|
17 | 20 | |
---|
18 | 21 | |
---|
19 | | -unsigned long powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; |
---|
| 22 | +u64 powerpc_security_features __read_mostly = SEC_FTR_DEFAULT; |
---|
20 | 23 | |
---|
21 | | -enum count_cache_flush_type { |
---|
22 | | - COUNT_CACHE_FLUSH_NONE = 0x1, |
---|
23 | | - COUNT_CACHE_FLUSH_SW = 0x2, |
---|
24 | | - COUNT_CACHE_FLUSH_HW = 0x4, |
---|
| 24 | +enum branch_cache_flush_type { |
---|
| 25 | + BRANCH_CACHE_FLUSH_NONE = 0x1, |
---|
| 26 | + BRANCH_CACHE_FLUSH_SW = 0x2, |
---|
| 27 | + BRANCH_CACHE_FLUSH_HW = 0x4, |
---|
25 | 28 | }; |
---|
26 | | -static enum count_cache_flush_type count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; |
---|
27 | | -static bool link_stack_flush_enabled; |
---|
| 29 | +static enum branch_cache_flush_type count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; |
---|
| 30 | +static enum branch_cache_flush_type link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; |
---|
28 | 31 | |
---|
29 | 32 | bool barrier_nospec_enabled; |
---|
30 | 33 | static bool no_nospec; |
---|
.. | .. |
---|
95 | 98 | return 0; |
---|
96 | 99 | } |
---|
97 | 100 | |
---|
98 | | -DEFINE_SIMPLE_ATTRIBUTE(fops_barrier_nospec, |
---|
99 | | - barrier_nospec_get, barrier_nospec_set, "%llu\n"); |
---|
| 101 | +DEFINE_DEBUGFS_ATTRIBUTE(fops_barrier_nospec, barrier_nospec_get, |
---|
| 102 | + barrier_nospec_set, "%llu\n"); |
---|
100 | 103 | |
---|
101 | 104 | static __init int barrier_nospec_debugfs_init(void) |
---|
102 | 105 | { |
---|
103 | | - debugfs_create_file("barrier_nospec", 0600, powerpc_debugfs_root, NULL, |
---|
104 | | - &fops_barrier_nospec); |
---|
| 106 | + debugfs_create_file_unsafe("barrier_nospec", 0600, |
---|
| 107 | + powerpc_debugfs_root, NULL, |
---|
| 108 | + &fops_barrier_nospec); |
---|
105 | 109 | return 0; |
---|
106 | 110 | } |
---|
107 | 111 | device_initcall(barrier_nospec_debugfs_init); |
---|
| 112 | + |
---|
| 113 | +static __init int security_feature_debugfs_init(void) |
---|
| 114 | +{ |
---|
| 115 | + debugfs_create_x64("security_features", 0400, powerpc_debugfs_root, |
---|
| 116 | + &powerpc_security_features); |
---|
| 117 | + return 0; |
---|
| 118 | +} |
---|
| 119 | +device_initcall(security_feature_debugfs_init); |
---|
108 | 120 | #endif /* CONFIG_DEBUG_FS */ |
---|
109 | 121 | |
---|
110 | 122 | #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3S_64) |
---|
.. | .. |
---|
207 | 219 | if (ccd) |
---|
208 | 220 | seq_buf_printf(&s, "Indirect branch cache disabled"); |
---|
209 | 221 | |
---|
210 | | - if (link_stack_flush_enabled) |
---|
211 | | - seq_buf_printf(&s, ", Software link stack flush"); |
---|
212 | | - |
---|
213 | | - } else if (count_cache_flush_type != COUNT_CACHE_FLUSH_NONE) { |
---|
| 222 | + } else if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { |
---|
214 | 223 | seq_buf_printf(&s, "Mitigation: Software count cache flush"); |
---|
215 | 224 | |
---|
216 | | - if (count_cache_flush_type == COUNT_CACHE_FLUSH_HW) |
---|
| 225 | + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) |
---|
217 | 226 | seq_buf_printf(&s, " (hardware accelerated)"); |
---|
218 | | - |
---|
219 | | - if (link_stack_flush_enabled) |
---|
220 | | - seq_buf_printf(&s, ", Software link stack flush"); |
---|
221 | 227 | |
---|
222 | 228 | } else if (btb_flush_enabled) { |
---|
223 | 229 | seq_buf_printf(&s, "Mitigation: Branch predictor state flush"); |
---|
224 | 230 | } else { |
---|
225 | 231 | seq_buf_printf(&s, "Vulnerable"); |
---|
| 232 | + } |
---|
| 233 | + |
---|
| 234 | + if (bcs || ccd || count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) { |
---|
| 235 | + if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE) |
---|
| 236 | + seq_buf_printf(&s, ", Software link stack flush"); |
---|
| 237 | + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) |
---|
| 238 | + seq_buf_printf(&s, " (hardware accelerated)"); |
---|
226 | 239 | } |
---|
227 | 240 | |
---|
228 | 241 | seq_buf_printf(&s, "\n"); |
---|
.. | .. |
---|
349 | 362 | return sprintf(buf, "Vulnerable\n"); |
---|
350 | 363 | } |
---|
351 | 364 | |
---|
| 365 | +static int ssb_prctl_get(struct task_struct *task) |
---|
| 366 | +{ |
---|
| 367 | + if (stf_enabled_flush_types == STF_BARRIER_NONE) |
---|
| 368 | + /* |
---|
| 369 | + * We don't have an explicit signal from firmware that we're |
---|
| 370 | + * vulnerable or not, we only have certain CPU revisions that |
---|
| 371 | + * are known to be vulnerable. |
---|
| 372 | + * |
---|
| 373 | + * We assume that if we're on another CPU, where the barrier is |
---|
| 374 | + * NONE, then we are not vulnerable. |
---|
| 375 | + */ |
---|
| 376 | + return PR_SPEC_NOT_AFFECTED; |
---|
| 377 | + else |
---|
| 378 | + /* |
---|
| 379 | + * If we do have a barrier type then we are vulnerable. The |
---|
| 380 | + * barrier is not a global or per-process mitigation, so the |
---|
| 381 | + * only value we can report here is PR_SPEC_ENABLE, which |
---|
| 382 | + * appears as "vulnerable" in /proc. |
---|
| 383 | + */ |
---|
| 384 | + return PR_SPEC_ENABLE; |
---|
| 385 | + |
---|
| 386 | + return -EINVAL; |
---|
| 387 | +} |
---|
| 388 | + |
---|
| 389 | +int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) |
---|
| 390 | +{ |
---|
| 391 | + switch (which) { |
---|
| 392 | + case PR_SPEC_STORE_BYPASS: |
---|
| 393 | + return ssb_prctl_get(task); |
---|
| 394 | + default: |
---|
| 395 | + return -ENODEV; |
---|
| 396 | + } |
---|
| 397 | +} |
---|
| 398 | + |
---|
352 | 399 | #ifdef CONFIG_DEBUG_FS |
---|
353 | 400 | static int stf_barrier_set(void *data, u64 val) |
---|
354 | 401 | { |
---|
.. | .. |
---|
374 | 421 | return 0; |
---|
375 | 422 | } |
---|
376 | 423 | |
---|
377 | | -DEFINE_SIMPLE_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, "%llu\n"); |
---|
| 424 | +DEFINE_DEBUGFS_ATTRIBUTE(fops_stf_barrier, stf_barrier_get, stf_barrier_set, |
---|
| 425 | + "%llu\n"); |
---|
378 | 426 | |
---|
379 | 427 | static __init int stf_barrier_debugfs_init(void) |
---|
380 | 428 | { |
---|
381 | | - debugfs_create_file("stf_barrier", 0600, powerpc_debugfs_root, NULL, &fops_stf_barrier); |
---|
| 429 | + debugfs_create_file_unsafe("stf_barrier", 0600, powerpc_debugfs_root, |
---|
| 430 | + NULL, &fops_stf_barrier); |
---|
382 | 431 | return 0; |
---|
383 | 432 | } |
---|
384 | 433 | device_initcall(stf_barrier_debugfs_init); |
---|
385 | 434 | #endif /* CONFIG_DEBUG_FS */ |
---|
386 | 435 | |
---|
387 | | -static void no_count_cache_flush(void) |
---|
| 436 | +static void update_branch_cache_flush(void) |
---|
388 | 437 | { |
---|
389 | | - count_cache_flush_type = COUNT_CACHE_FLUSH_NONE; |
---|
390 | | - pr_info("count-cache-flush: software flush disabled.\n"); |
---|
| 438 | + u32 *site; |
---|
| 439 | + |
---|
| 440 | +#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE |
---|
| 441 | + site = &patch__call_kvm_flush_link_stack; |
---|
| 442 | + // This controls the branch from guest_exit_cont to kvm_flush_link_stack |
---|
| 443 | + if (link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) { |
---|
| 444 | + patch_instruction_site(site, ppc_inst(PPC_INST_NOP)); |
---|
| 445 | + } else { |
---|
| 446 | + // Could use HW flush, but that could also flush count cache |
---|
| 447 | + patch_branch_site(site, (u64)&kvm_flush_link_stack, BRANCH_SET_LINK); |
---|
| 448 | + } |
---|
| 449 | +#endif |
---|
| 450 | + |
---|
| 451 | + // Patch out the bcctr first, then nop the rest |
---|
| 452 | + site = &patch__call_flush_branch_caches3; |
---|
| 453 | + patch_instruction_site(site, ppc_inst(PPC_INST_NOP)); |
---|
| 454 | + site = &patch__call_flush_branch_caches2; |
---|
| 455 | + patch_instruction_site(site, ppc_inst(PPC_INST_NOP)); |
---|
| 456 | + site = &patch__call_flush_branch_caches1; |
---|
| 457 | + patch_instruction_site(site, ppc_inst(PPC_INST_NOP)); |
---|
| 458 | + |
---|
| 459 | + // This controls the branch from _switch to flush_branch_caches |
---|
| 460 | + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE && |
---|
| 461 | + link_stack_flush_type == BRANCH_CACHE_FLUSH_NONE) { |
---|
| 462 | + // Nothing to be done |
---|
| 463 | + |
---|
| 464 | + } else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW && |
---|
| 465 | + link_stack_flush_type == BRANCH_CACHE_FLUSH_HW) { |
---|
| 466 | + // Patch in the bcctr last |
---|
| 467 | + site = &patch__call_flush_branch_caches1; |
---|
| 468 | + patch_instruction_site(site, ppc_inst(0x39207fff)); // li r9,0x7fff |
---|
| 469 | + site = &patch__call_flush_branch_caches2; |
---|
| 470 | + patch_instruction_site(site, ppc_inst(0x7d2903a6)); // mtctr r9 |
---|
| 471 | + site = &patch__call_flush_branch_caches3; |
---|
| 472 | + patch_instruction_site(site, ppc_inst(PPC_INST_BCCTR_FLUSH)); |
---|
| 473 | + |
---|
| 474 | + } else { |
---|
| 475 | + patch_branch_site(site, (u64)&flush_branch_caches, BRANCH_SET_LINK); |
---|
| 476 | + |
---|
| 477 | + // If we just need to flush the link stack, early return |
---|
| 478 | + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) { |
---|
| 479 | + patch_instruction_site(&patch__flush_link_stack_return, |
---|
| 480 | + ppc_inst(PPC_INST_BLR)); |
---|
| 481 | + |
---|
| 482 | + // If we have flush instruction, early return |
---|
| 483 | + } else if (count_cache_flush_type == BRANCH_CACHE_FLUSH_HW) { |
---|
| 484 | + patch_instruction_site(&patch__flush_count_cache_return, |
---|
| 485 | + ppc_inst(PPC_INST_BLR)); |
---|
| 486 | + } |
---|
| 487 | + } |
---|
391 | 488 | } |
---|
392 | 489 | |
---|
393 | | -static void toggle_count_cache_flush(bool enable) |
---|
| 490 | +static void toggle_branch_cache_flush(bool enable) |
---|
394 | 491 | { |
---|
395 | | - if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE) && |
---|
396 | | - !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) |
---|
397 | | - enable = false; |
---|
| 492 | + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { |
---|
| 493 | + if (count_cache_flush_type != BRANCH_CACHE_FLUSH_NONE) |
---|
| 494 | + count_cache_flush_type = BRANCH_CACHE_FLUSH_NONE; |
---|
398 | 495 | |
---|
399 | | - if (!enable) { |
---|
400 | | - patch_instruction_site(&patch__call_flush_count_cache, PPC_INST_NOP); |
---|
401 | | -#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE |
---|
402 | | - patch_instruction_site(&patch__call_kvm_flush_link_stack, PPC_INST_NOP); |
---|
403 | | -#endif |
---|
404 | | - pr_info("link-stack-flush: software flush disabled.\n"); |
---|
405 | | - link_stack_flush_enabled = false; |
---|
406 | | - no_count_cache_flush(); |
---|
407 | | - return; |
---|
| 496 | + pr_info("count-cache-flush: flush disabled.\n"); |
---|
| 497 | + } else { |
---|
| 498 | + if (security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { |
---|
| 499 | + count_cache_flush_type = BRANCH_CACHE_FLUSH_HW; |
---|
| 500 | + pr_info("count-cache-flush: hardware flush enabled.\n"); |
---|
| 501 | + } else { |
---|
| 502 | + count_cache_flush_type = BRANCH_CACHE_FLUSH_SW; |
---|
| 503 | + pr_info("count-cache-flush: software flush enabled.\n"); |
---|
| 504 | + } |
---|
408 | 505 | } |
---|
409 | 506 | |
---|
410 | | - // This enables the branch from _switch to flush_count_cache |
---|
411 | | - patch_branch_site(&patch__call_flush_count_cache, |
---|
412 | | - (u64)&flush_count_cache, BRANCH_SET_LINK); |
---|
| 507 | + if (!enable || !security_ftr_enabled(SEC_FTR_FLUSH_LINK_STACK)) { |
---|
| 508 | + if (link_stack_flush_type != BRANCH_CACHE_FLUSH_NONE) |
---|
| 509 | + link_stack_flush_type = BRANCH_CACHE_FLUSH_NONE; |
---|
413 | 510 | |
---|
414 | | -#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE |
---|
415 | | - // This enables the branch from guest_exit_cont to kvm_flush_link_stack |
---|
416 | | - patch_branch_site(&patch__call_kvm_flush_link_stack, |
---|
417 | | - (u64)&kvm_flush_link_stack, BRANCH_SET_LINK); |
---|
418 | | -#endif |
---|
419 | | - |
---|
420 | | - pr_info("link-stack-flush: software flush enabled.\n"); |
---|
421 | | - link_stack_flush_enabled = true; |
---|
422 | | - |
---|
423 | | - // If we just need to flush the link stack, patch an early return |
---|
424 | | - if (!security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) { |
---|
425 | | - patch_instruction_site(&patch__flush_link_stack_return, PPC_INST_BLR); |
---|
426 | | - no_count_cache_flush(); |
---|
427 | | - return; |
---|
| 511 | + pr_info("link-stack-flush: flush disabled.\n"); |
---|
| 512 | + } else { |
---|
| 513 | + if (security_ftr_enabled(SEC_FTR_BCCTR_LINK_FLUSH_ASSIST)) { |
---|
| 514 | + link_stack_flush_type = BRANCH_CACHE_FLUSH_HW; |
---|
| 515 | + pr_info("link-stack-flush: hardware flush enabled.\n"); |
---|
| 516 | + } else { |
---|
| 517 | + link_stack_flush_type = BRANCH_CACHE_FLUSH_SW; |
---|
| 518 | + pr_info("link-stack-flush: software flush enabled.\n"); |
---|
| 519 | + } |
---|
428 | 520 | } |
---|
429 | 521 | |
---|
430 | | - if (!security_ftr_enabled(SEC_FTR_BCCTR_FLUSH_ASSIST)) { |
---|
431 | | - count_cache_flush_type = COUNT_CACHE_FLUSH_SW; |
---|
432 | | - pr_info("count-cache-flush: full software flush sequence enabled.\n"); |
---|
433 | | - return; |
---|
434 | | - } |
---|
435 | | - |
---|
436 | | - patch_instruction_site(&patch__flush_count_cache_return, PPC_INST_BLR); |
---|
437 | | - count_cache_flush_type = COUNT_CACHE_FLUSH_HW; |
---|
438 | | - pr_info("count-cache-flush: hardware assisted flush sequence enabled\n"); |
---|
| 522 | + update_branch_cache_flush(); |
---|
439 | 523 | } |
---|
440 | 524 | |
---|
441 | 525 | void setup_count_cache_flush(void) |
---|
.. | .. |
---|
459 | 543 | security_ftr_enabled(SEC_FTR_FLUSH_COUNT_CACHE)) |
---|
460 | 544 | security_ftr_set(SEC_FTR_FLUSH_LINK_STACK); |
---|
461 | 545 | |
---|
462 | | - toggle_count_cache_flush(enable); |
---|
| 546 | + toggle_branch_cache_flush(enable); |
---|
463 | 547 | } |
---|
464 | 548 | |
---|
465 | 549 | #ifdef CONFIG_DEBUG_FS |
---|
.. | .. |
---|
474 | 558 | else |
---|
475 | 559 | return -EINVAL; |
---|
476 | 560 | |
---|
477 | | - toggle_count_cache_flush(enable); |
---|
| 561 | + toggle_branch_cache_flush(enable); |
---|
478 | 562 | |
---|
479 | 563 | return 0; |
---|
480 | 564 | } |
---|
481 | 565 | |
---|
482 | 566 | static int count_cache_flush_get(void *data, u64 *val) |
---|
483 | 567 | { |
---|
484 | | - if (count_cache_flush_type == COUNT_CACHE_FLUSH_NONE) |
---|
| 568 | + if (count_cache_flush_type == BRANCH_CACHE_FLUSH_NONE) |
---|
485 | 569 | *val = 0; |
---|
486 | 570 | else |
---|
487 | 571 | *val = 1; |
---|
.. | .. |
---|
489 | 573 | return 0; |
---|
490 | 574 | } |
---|
491 | 575 | |
---|
492 | | -DEFINE_SIMPLE_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get, |
---|
493 | | - count_cache_flush_set, "%llu\n"); |
---|
| 576 | +DEFINE_DEBUGFS_ATTRIBUTE(fops_count_cache_flush, count_cache_flush_get, |
---|
| 577 | + count_cache_flush_set, "%llu\n"); |
---|
494 | 578 | |
---|
495 | 579 | static __init int count_cache_flush_debugfs_init(void) |
---|
496 | 580 | { |
---|
497 | | - debugfs_create_file("count_cache_flush", 0600, powerpc_debugfs_root, |
---|
498 | | - NULL, &fops_count_cache_flush); |
---|
| 581 | + debugfs_create_file_unsafe("count_cache_flush", 0600, |
---|
| 582 | + powerpc_debugfs_root, NULL, |
---|
| 583 | + &fops_count_cache_flush); |
---|
499 | 584 | return 0; |
---|
500 | 585 | } |
---|
501 | 586 | device_initcall(count_cache_flush_debugfs_init); |
---|