ANDROID: arm64: Initialise mismatched compat hwcaps from CPU notifier

When CPUs with mismatched support for 32-bit EL0 are detected early,
before the system capabilities are finalised, then initialisation of
the compat hwcaps is left to the boot CPU to configure as it would do
for a system without a mismatch.

Unfortunately, this initialisation is only carried out if
system_supports_32bit_el0(), which isn't initialised until later in the
boot process via a CPU hotplug notifier. Consequently, the compat hwcaps
reported do not necessarily indicate all of the CPU features available.

Move the initialisation of compat hwcaps to the CPU hotplug notifier
itself so that we can ensure that they are configured as soon as we have
detected a mismatch.

Bug: 186482502
Cc: Quentin Perret <qperret@google.com>
Cc: Huang Yiwei <hyiwei@codeaurora.org>
Signed-off-by: Will Deacon <willdeacon@google.com>
Change-Id: I54edcc34f96be9dec15aa44407441c0227c17753
This commit is contained in:
Will Deacon 2021-04-27 12:59:52 +01:00 committed by Quentin Perret
commit ea2c091f13

View file

@ -995,8 +995,6 @@ static void relax_cpu_ftr_reg(u32 sys_id, int field)
WARN_ON(!ftrp->width);
}
static void update_compat_elf_hwcaps(void);
static void update_mismatched_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
struct cpuinfo_arm64 *boot)
{
@ -1010,7 +1008,6 @@ static void update_mismatched_32bit_el0_cpu_features(struct cpuinfo_arm64 *info,
boot->aarch32 = info->aarch32;
init_32bit_cpu_features(&boot->aarch32);
update_compat_elf_hwcaps();
boot_cpu_32bit_regs_overridden = true;
}
@ -1287,51 +1284,6 @@ has_cpuid_feature(const struct arm64_cpu_capabilities *entry, int scope)
return feature_matches(val, entry);
}
static int enable_mismatched_32bit_el0(unsigned int cpu)
{
static int lucky_winner = -1;
struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
if (cpu_32bit) {
cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
}
if (cpumask_test_cpu(0, cpu_32bit_el0_mask) == cpu_32bit)
return 0;
if (lucky_winner >= 0)
return 0;
/*
* We've detected a mismatch. We need to keep one of our CPUs with
* 32-bit EL0 online so that is_cpu_allowed() doesn't end up rejecting
* every CPU in the system for a 32-bit task.
*/
lucky_winner = cpu_32bit ? cpu : cpumask_any_and(cpu_32bit_el0_mask,
cpu_active_mask);
get_cpu_device(lucky_winner)->offline_disabled = true;
pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
cpu, lucky_winner);
return 0;
}
static int __init init_32bit_el0_mask(void)
{
if (!allow_mismatched_32bit_el0)
return 0;
if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL))
return -ENOMEM;
return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"arm64/mismatched_32bit_el0:online",
enable_mismatched_32bit_el0, NULL);
}
subsys_initcall_sync(init_32bit_el0_mask);
const struct cpumask *system_32bit_el0_cpumask(void)
{
if (!system_supports_32bit_el0())
@ -2567,12 +2519,6 @@ static void setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
cap_set_elf_hwcap(hwcaps);
}
static void update_compat_elf_hwcaps(void)
{
if (system_capabilities_finalized())
setup_elf_hwcaps(compat_elf_hwcaps);
}
static void update_cpu_capabilities(u16 scope_mask)
{
int i;
@ -2953,6 +2899,52 @@ void __init setup_cpu_features(void)
ARCH_DMA_MINALIGN);
}
static int enable_mismatched_32bit_el0(unsigned int cpu)
{
static int lucky_winner = -1;
struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
bool cpu_32bit = id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0);
if (cpu_32bit) {
cpumask_set_cpu(cpu, cpu_32bit_el0_mask);
static_branch_enable_cpuslocked(&arm64_mismatched_32bit_el0);
}
if (cpumask_test_cpu(0, cpu_32bit_el0_mask) == cpu_32bit)
return 0;
if (lucky_winner >= 0)
return 0;
/*
* We've detected a mismatch. We need to keep one of our CPUs with
* 32-bit EL0 online so that is_cpu_allowed() doesn't end up rejecting
* every CPU in the system for a 32-bit task.
*/
lucky_winner = cpu_32bit ? cpu : cpumask_any_and(cpu_32bit_el0_mask,
cpu_active_mask);
get_cpu_device(lucky_winner)->offline_disabled = true;
setup_elf_hwcaps(compat_elf_hwcaps);
pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
cpu, lucky_winner);
return 0;
}
static int __init init_32bit_el0_mask(void)
{
if (!allow_mismatched_32bit_el0)
return 0;
if (!zalloc_cpumask_var(&cpu_32bit_el0_mask, GFP_KERNEL))
return -ENOMEM;
return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"arm64/mismatched_32bit_el0:online",
enable_mismatched_32bit_el0, NULL);
}
subsys_initcall_sync(init_32bit_el0_mask);
static bool __maybe_unused
cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
{