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

arm64: Allow mismatched 32-bit EL0 support

When confronted with a mixture of CPUs, some of which support 32-bit
applications and others which don't, we quite sensibly treat the system
as 64-bit only for userspace and prevent execve() of 32-bit binaries.

Unfortunately, some crazy folks have decided to build systems like this
with the intention of running 32-bit applications, so relax our
sanitisation logic to continue to advertise 32-bit support to userspace
on these systems and track the real 32-bit capable cores in a cpumask
instead. For now, the default behaviour remains but will be tied to
a command-line option in a later patch.

Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20210608180313.11502-3-will@kernel.org
Signed-off-by: Will Deacon <will@kernel.org>

+110 -15
+7 -1
arch/arm64/include/asm/cpufeature.h
··· 637 637 return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1)); 638 638 } 639 639 640 + const struct cpumask *system_32bit_el0_cpumask(void); 641 + DECLARE_STATIC_KEY_FALSE(arm64_mismatched_32bit_el0); 642 + 640 643 static inline bool system_supports_32bit_el0(void) 641 644 { 642 - return cpus_have_const_cap(ARM64_HAS_32BIT_EL0); 645 + u64 pfr0 = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1); 646 + 647 + return static_branch_unlikely(&arm64_mismatched_32bit_el0) || 648 + id_aa64pfr0_32bit_el0(pfr0); 643 649 } 644 650 645 651 static inline bool system_supports_4kb_granule(void)
+101 -13
arch/arm64/kernel/cpufeature.c
··· 108 108 EXPORT_SYMBOL(arm64_use_ng_mappings); 109 109 110 110 /* 111 + * Permit PER_LINUX32 and execve() of 32-bit binaries even if not all CPUs 112 + * support it? 113 + */ 114 + static bool __read_mostly allow_mismatched_32bit_el0; 115 + 116 + /* 117 + * Static branch enabled only if allow_mismatched_32bit_el0 is set and we have 118 + * seen at least one CPU capable of 32-bit EL0. 119 + */ 120 + DEFINE_STATIC_KEY_FALSE(arm64_mismatched_32bit_el0); 121 + 122 + /* 123 + * Mask of CPUs supporting 32-bit EL0. 124 + * Only valid if arm64_mismatched_32bit_el0 is enabled. 125 + */ 126 + static cpumask_var_t cpu_32bit_el0_mask __cpumask_var_read_mostly; 127 + 128 + /* 111 129 * Flag to indicate if we have computed the system wide 112 130 * capabilities based on the boot time active CPUs. This 113 131 * will be used to determine if a new booting CPU should ··· 793 775 * Any bits that are not covered by an arm64_ftr_bits entry are considered 794 776 * RES0 for the system-wide value, and must strictly match. 795 777 */ 796 - static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new) 778 + static void init_cpu_ftr_reg(u32 sys_reg, u64 new) 797 779 { 798 780 u64 val = 0; 799 781 u64 strict_mask = ~0x0ULL; ··· 889 871 890 872 static void __init setup_boot_cpu_capabilities(void); 891 873 892 - static void __init init_32bit_cpu_features(struct cpuinfo_32bit *info) 874 + static void init_32bit_cpu_features(struct cpuinfo_32bit *info) 893 875 { 894 876 init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0); 895 877 init_cpu_ftr_reg(SYS_ID_DFR1_EL1, info->reg_id_dfr1); ··· 1006 988 1007 989 /* Bogus field? */ 1008 990 WARN_ON(!ftrp->width); 991 + } 992 + 993 + static void lazy_init_32bit_cpu_features(struct cpuinfo_arm64 *info, 994 + struct cpuinfo_arm64 *boot) 995 + { 996 + static bool boot_cpu_32bit_regs_overridden = false; 997 + 998 + if (!allow_mismatched_32bit_el0 || boot_cpu_32bit_regs_overridden) 999 + return; 1000 + 1001 + if (id_aa64pfr0_32bit_el0(boot->reg_id_aa64pfr0)) 1002 + return; 1003 + 1004 + boot->aarch32 = info->aarch32; 1005 + init_32bit_cpu_features(&boot->aarch32); 1006 + boot_cpu_32bit_regs_overridden = true; 1009 1007 } 1010 1008 1011 1009 static int update_32bit_cpu_features(int cpu, struct cpuinfo_32bit *info, ··· 1195 1161 * (e.g. SYS_ID_AA64PFR0_EL1), so we call it last. 1196 1162 */ 1197 1163 if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) { 1164 + lazy_init_32bit_cpu_features(info, boot); 1198 1165 taint |= update_32bit_cpu_features(cpu, &info->aarch32, 1199 1166 &boot->aarch32); 1200 1167 } ··· 1306 1271 val = __read_sysreg_by_encoding(entry->sys_reg); 1307 1272 1308 1273 return feature_matches(val, entry); 1274 + } 1275 + 1276 + const struct cpumask *system_32bit_el0_cpumask(void) 1277 + { 1278 + if (!system_supports_32bit_el0()) 1279 + return cpu_none_mask; 1280 + 1281 + if (static_branch_unlikely(&arm64_mismatched_32bit_el0)) 1282 + return cpu_32bit_el0_mask; 1283 + 1284 + return cpu_possible_mask; 1285 + } 1286 + 1287 + static bool has_32bit_el0(const struct arm64_cpu_capabilities *entry, int scope) 1288 + { 1289 + if (!has_cpuid_feature(entry, scope)) 1290 + return allow_mismatched_32bit_el0; 1291 + 1292 + if (scope == SCOPE_SYSTEM) 1293 + pr_info("detected: 32-bit EL0 Support\n"); 1294 + 1295 + return true; 1309 1296 } 1310 1297 1311 1298 static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry, int scope) ··· 1948 1891 .cpu_enable = cpu_copy_el2regs, 1949 1892 }, 1950 1893 { 1951 - .desc = "32-bit EL0 Support", 1952 - .capability = ARM64_HAS_32BIT_EL0, 1894 + .capability = ARM64_HAS_32BIT_EL0_DO_NOT_USE, 1953 1895 .type = ARM64_CPUCAP_SYSTEM_FEATURE, 1954 - .matches = has_cpuid_feature, 1896 + .matches = has_32bit_el0, 1955 1897 .sys_reg = SYS_ID_AA64PFR0_EL1, 1956 1898 .sign = FTR_UNSIGNED, 1957 1899 .field_pos = ID_AA64PFR0_EL0_SHIFT, ··· 2459 2403 {}, 2460 2404 }; 2461 2405 2462 - static void __init cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap) 2406 + static void cap_set_elf_hwcap(const struct arm64_cpu_capabilities *cap) 2463 2407 { 2464 2408 switch (cap->hwcap_type) { 2465 2409 case CAP_HWCAP: ··· 2504 2448 return rc; 2505 2449 } 2506 2450 2507 - static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps) 2451 + static void setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps) 2508 2452 { 2509 2453 /* We support emulation of accesses to CPU ID feature registers */ 2510 2454 cpu_set_named_feature(CPUID); ··· 2679 2623 } 2680 2624 2681 2625 static void 2682 - verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps) 2626 + __verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps) 2683 2627 { 2684 2628 2685 2629 for (; caps->matches; caps++) ··· 2688 2632 smp_processor_id(), caps->desc); 2689 2633 cpu_die_early(); 2690 2634 } 2635 + } 2636 + 2637 + static void verify_local_elf_hwcaps(void) 2638 + { 2639 + __verify_local_elf_hwcaps(arm64_elf_hwcaps); 2640 + 2641 + if (id_aa64pfr0_32bit_el0(read_cpuid(ID_AA64PFR0_EL1))) 2642 + __verify_local_elf_hwcaps(compat_elf_hwcaps); 2691 2643 } 2692 2644 2693 2645 static void verify_sve_features(void) ··· 2762 2698 * on all secondary CPUs. 2763 2699 */ 2764 2700 verify_local_cpu_caps(SCOPE_ALL & ~SCOPE_BOOT_CPU); 2765 - 2766 - verify_local_elf_hwcaps(arm64_elf_hwcaps); 2767 - 2768 - if (system_supports_32bit_el0()) 2769 - verify_local_elf_hwcaps(compat_elf_hwcaps); 2701 + verify_local_elf_hwcaps(); 2770 2702 2771 2703 if (system_supports_sve()) 2772 2704 verify_sve_features(); ··· 2896 2836 pr_warn("No Cache Writeback Granule information, assuming %d\n", 2897 2837 ARCH_DMA_MINALIGN); 2898 2838 } 2839 + 2840 + static int enable_mismatched_32bit_el0(unsigned int cpu) 2841 + { 2842 + struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu); 2843 + bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0); 2844 + 2845 + if (cpu_32bit) { 2846 + cpumask_set_cpu(cpu, cpu_32bit_el0_mask); 2847 + static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0); 2848 + setup_elf_hwcaps(compat_elf_hwcaps); 2849 + } 2850 + 2851 + return 0; 2852 + } 2853 + 2854 + static int __init init_32bit_el0_mask(void) 2855 + { 2856 + if (!allow_mismatched_32bit_el0) 2857 + return 0; 2858 + 2859 + if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL)) 2860 + return -ENOMEM; 2861 + 2862 + return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, 2863 + "arm64/mismatched_32bit_el0:online", 2864 + enable_mismatched_32bit_el0, NULL); 2865 + } 2866 + subsys_initcall_sync(init_32bit_el0_mask); 2899 2867 2900 2868 static void __maybe_unused cpu_enable_cnp(struct arm64_cpu_capabilities const *cap) 2901 2869 {
+2 -1
arch/arm64/tools/cpucaps
··· 3 3 # Internal CPU capabilities constants, keep this list sorted 4 4 5 5 BTI 6 - HAS_32BIT_EL0 6 + # Unreliable: use system_supports_32bit_el0() instead. 7 + HAS_32BIT_EL0_DO_NOT_USE 7 8 HAS_32BIT_EL1 8 9 HAS_ADDRESS_AUTH 9 10 HAS_ADDRESS_AUTH_ARCH