|
8 | 8 | #include <linux/arm-smccc.h> |
9 | 9 | #include <linux/types.h> |
10 | 10 | #include <linux/cpu.h> |
| 11 | +#include <linux/arm-smccc.h> |
11 | 12 | #include <asm/cpu.h> |
12 | 13 | #include <asm/cputype.h> |
13 | 14 | #include <asm/cpufeature.h> |
14 | 15 | #include <asm/kvm_asm.h> |
15 | 16 | #include <asm/smp_plat.h> |
| 17 | +#include <asm/memory.h> |
| 18 | +#include <asm/barrier.h> |
| 19 | +#include <asm/tlbflush.h> |
| 20 | +#include <asm/sysreg.h> |
16 | 21 |
|
17 | 22 | static u64 target_impl_cpu_num; |
18 | 23 | static struct target_impl_cpu *target_impl_cpus; |
@@ -565,6 +570,97 @@ static const struct midr_range erratum_ac04_cpu_23_list[] = { |
565 | 570 | }; |
566 | 571 | #endif |
567 | 572 |
|
| 573 | +#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE |
| 574 | +/* |
| 575 | + * MAIR_EL1 MT_NORMAL_NC to Device-nGnRE Conversion |
| 576 | + * |
| 577 | + * Boot-time workaround that converts MT_NORMAL_NC attribute to Device-nGnRE |
| 578 | + * (0x04) via kernel parameter for hardware-specific issues. |
| 579 | + * |
| 580 | + */ |
| 581 | + |
| 582 | +/* |
| 583 | + * Flag indicating if MT_NORMAL_NC to nGnRE conversion is enabled |
| 584 | + */ |
| 585 | +static int mair_el1_nc_to_ngnre __read_mostly = -1; |
| 586 | + |
| 587 | +/* |
| 588 | + * Parse kernel command line parameter at boot: |
| 589 | + * mair_el1_nc_to_ngnre=1 |
| 590 | + * Enables MT_NORMAL_NC to Device-nGnRE conversion |
| 591 | + */ |
| 592 | +static int __init mair_el1_nc_setup(char *str) |
| 593 | +{ |
| 594 | + bool enable; |
| 595 | + int ret; |
| 596 | + |
| 597 | + ret = kstrtobool(str, &enable); |
| 598 | + if (ret) |
| 599 | + return ret; |
| 600 | + |
| 601 | + mair_el1_nc_to_ngnre = enable ? 1 : 0; |
| 602 | + |
| 603 | + pr_info("MAIR_EL1: MT_NORMAL_NC to Device-nGnRE conversion %s\n", |
| 604 | + enable ? "enabled" : "disabled"); |
| 605 | + |
| 606 | + return 0; |
| 607 | +} |
| 608 | +early_param("mair_el1_nc_to_ngnre", mair_el1_nc_setup); |
| 609 | + |
| 610 | +/* Cpufeature capability check for MAIR NC to nGnRE workaround */ |
| 611 | +static bool has_nc_ngnre_workaround(const struct arm64_cpu_capabilities *entry, |
| 612 | + int scope) |
| 613 | +{ |
| 614 | + /* Lazy initialization: check only once */ |
| 615 | + if (mair_el1_nc_to_ngnre == -1) { |
| 616 | + if ((arm_smccc_get_soc_id_version() == 0x036b0410) && |
| 617 | + (arm_smccc_get_soc_id_revision() < 5)) { |
| 618 | + mair_el1_nc_to_ngnre = 1; |
| 619 | + return true; |
| 620 | + } |
| 621 | + mair_el1_nc_to_ngnre = 0; |
| 622 | + } |
| 623 | + |
| 624 | + return mair_el1_nc_to_ngnre > 0; |
| 625 | +} |
| 626 | + |
| 627 | +/* |
| 628 | + * Called by cpufeature framework when CPU comes online |
| 629 | + * For boot CPU: alternatives not yet patched, so apply NC to nGnRE here |
| 630 | + * For secondary CPUs: alternatives already patched in __cpu_setup |
| 631 | + */ |
| 632 | +static void enable_nc_to_ngnre(struct arm64_cpu_capabilities const *cap) |
| 633 | +{ |
| 634 | + u64 attr_mask, current_mair, new_mair; |
| 635 | + u8 current_attr; |
| 636 | + |
| 637 | + if (mair_el1_nc_to_ngnre <= 0) |
| 638 | + return; |
| 639 | + |
| 640 | + current_mair = read_sysreg(mair_el1); |
| 641 | + |
| 642 | + /* Check if MT_NORMAL_NC is already Device-nGnRE */ |
| 643 | + attr_mask = GENMASK_ULL((MT_NORMAL_NC * 8) + 7, MT_NORMAL_NC * 8); |
| 644 | + current_attr = FIELD_GET(attr_mask, current_mair); |
| 645 | + |
| 646 | + /* Already set via alternatives (secondary CPU case) */ |
| 647 | + if (current_attr == MAIR_ATTR_DEVICE_nGnRE) |
| 648 | + return; |
| 649 | + |
| 650 | + /* Apply override for boot CPU */ |
| 651 | + new_mair = (current_mair & ~attr_mask) | |
| 652 | + ((u64)MAIR_ATTR_DEVICE_nGnRE << (MT_NORMAL_NC * 8)); |
| 653 | + |
| 654 | + write_sysreg(new_mair, mair_el1); |
| 655 | + isb(); |
| 656 | + local_flush_tlb_all(); |
| 657 | + |
| 658 | + pr_info("CPU%d: MAIR_EL1 updated 0x%016llx -> 0x%016llx\n", |
| 659 | + smp_processor_id(), current_mair, new_mair); |
| 660 | +} |
| 661 | + |
| 662 | +#endif /* CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE */ |
| 663 | + |
568 | 664 | const struct arm64_cpu_capabilities arm64_errata[] = { |
569 | 665 | #ifdef CONFIG_ARM64_WORKAROUND_CLEAN_CACHE |
570 | 666 | { |
@@ -906,6 +1002,15 @@ const struct arm64_cpu_capabilities arm64_errata[] = { |
906 | 1002 | .matches = has_impdef_pmuv3, |
907 | 1003 | .cpu_enable = cpu_enable_impdef_pmuv3_traps, |
908 | 1004 | }, |
| 1005 | +#ifdef CONFIG_ARM64_WORKAROUND_NC_TO_NGNRE |
| 1006 | + { |
| 1007 | + .desc = "MAIR_EL1 NC to nGnRE", |
| 1008 | + .capability = ARM64_WORKAROUND_NC_TO_NGNRE, |
| 1009 | + .type = ARM64_CPUCAP_BOOT_CPU_FEATURE, |
| 1010 | + .matches = has_nc_ngnre_workaround, |
| 1011 | + .cpu_enable = enable_nc_to_ngnre, |
| 1012 | + }, |
| 1013 | +#endif |
909 | 1014 | { |
910 | 1015 | } |
911 | 1016 | }; |
0 commit comments