| .. | .. |
|---|
| 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); |
|---|