1
0
mirror of https://git.yoctoproject.org/meta-arm synced 2026-06-08 15:30:08 +00:00

arm-bsp/linux: add asymmetric AArch32 EL0 support for TC0

This adds patches for asymmetric AArch32 EL0 configuration support for
android11-5.4-lts kernel. And enables CONFIG_ASYMMETRIC_AARCH32 and
disables CONFIG_KVM in TC0 defconfig.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: Iab94b5c48b60aca4a71267ba1be1cb1836002d10
Signed-off-by: Jon Mason <jon.mason@arm.com>
This commit is contained in:
Arunachalam Ganapathy
2021-02-08 12:26:51 +00:00
committed by Jon Mason
parent 3be0996065
commit 1f872c3c83
4 changed files with 554 additions and 1 deletions
@@ -81,6 +81,8 @@ SRC_URI_append_tc0 = " \
file://0011-tee-optee-Add-support-for-session-login-client-UUID-.patch \
file://0012-driver-optee-Support-for-ffa-transport.patch \
file://0013-tee-optee-fix-mem-handle-removal-in-ffa_shm_unregist.patch \
file://0014-arm64-Add-support-for-asymmetric-AArch32-EL0-configu.patch \
file://0015-arm64-smp-Prevent-hotplugging-the-last-AArch32-able-.patch \
"
#
@@ -0,0 +1,462 @@
Upstream-Status: Backport [http://www.linux-arm.org/git?p=linux-power.git;a=commit;h=ce7d6e205568b7949ff26f2b3c65dc4db1c15b96]
Signed-off-by: Usama Arif <usama.arif@arm.com>
From 0316669bc2a0e1279427b7a3ed01313b70544756 Mon Sep 17 00:00:00 2001
From: Catalin Marinas <catalin.marinas@arm.com>
Date: Thu, 20 Jun 2019 17:59:25 +0100
Subject: [PATCH 1/2] arm64: Add support for asymmetric AArch32 EL0
configurations
There is a non-negligible chance that we may see asymmetric big.LITTLE
configurations where only the LITTLE CPUs have AArch32 support at EL0.
While Linux currently handles such configurations by not allowing
AArch32 tasks, there is a strong marketing push to investigate the
possibility of allowing compat applications. This patch is aimed to
facilitate internal testing and NOT FOR UPSTREAM.
When the CONFIG_ASYMMETRIC_AARCH32 option is enabled (EXPERT), the type
of the ARM64_HAS_32BIT_EL0 capability becomes WEAK_LOCAL_CPU_FEATURE.
The kernel will now return true for system_supports_32bit_el0() and
32-bit tasks will be migrated to the capable CPUs during
do_notify_resume(). If the last CPU supporting 32-bit is offlined, the
kernel will SIGKILL any scheduled 32-bit tasks (the alternative is to
prevent offlining through a new .cpu_disable feature entry).
In addition to the relaxation of the ARM64_HAS_32BIT_EL0 capability,
this patch factors out the 32-bit cpuinfo and features setting into
separate functions: __cpuinfo_store_cpu_32bit(),
init_cpu_32bit_features(). The cpuinfo of the booting CPU
(boot_cpu_data) is now updated on the first 32-bit capable CPU even if
it is a secondary one. The ID_AA64PFR0_EL0_64BIT_ONLY feature is relaxed
to FTR_NONSTRICT and FTR_HIGHER_SAFE when the asymmetric AArch32 support
is enabled. The compat_elf_hwcaps are only verified for the
AArch32-capable CPUs to still allow hotplugging AArch64-only CPUs.
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Morten Rasmussen <Morten.Rasmussen@arm.com>
Cc: Valentin Schneider <valentin.schneider@arm.com>
Cc: Qais Yousef <qais.yousef@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Qais Yousef <qais.yousef@arm.com>
---
arch/arm64/Kconfig | 13 ++++++
arch/arm64/include/asm/cpu.h | 2 +
arch/arm64/include/asm/cpufeature.h | 3 ++
arch/arm64/include/asm/thread_info.h | 5 ++-
arch/arm64/kernel/cpufeature.c | 64 ++++++++++++++++++----------
arch/arm64/kernel/cpuinfo.c | 61 +++++++++++++++++---------
arch/arm64/kernel/process.c | 17 ++++++++
arch/arm64/kernel/signal.c | 30 ++++++++++++-
8 files changed, 150 insertions(+), 45 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 71d23b5d10d4..068e9e9ffc40 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1680,6 +1680,19 @@ config DMI
endmenu
+config ASYMMETRIC_AARCH32
+ bool "Allow support for asymmetric AArch32 support"
+ depends on COMPAT && EXPERT && !KVM
+ help
+ Enable this option to allow support for asymmetric AArch32 EL0
+ CPU configurations. Once the AArch32 EL0 support is detected
+ on a CPU, the feature is made available to user space to allow
+ the execution of 32-bit (compat) applications by migrating
+ them to the capable CPUs. Offlining such CPUs leads to 32-bit
+ applications being killed.
+
+ If unsure say N.
+
config SYSVIPC_COMPAT
def_bool y
depends on COMPAT && SYSVIPC
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index d72d995b7e25..39dbf5827070 100644
--- a/arch/arm64/include/asm/cpu.h
+++ b/arch/arm64/include/asm/cpu.h
@@ -15,6 +15,7 @@
struct cpuinfo_arm64 {
struct cpu cpu;
struct kobject kobj;
+ bool aarch32_valid;
u32 reg_ctr;
u32 reg_cntfrq;
u32 reg_dczid;
@@ -60,6 +61,7 @@ void cpuinfo_store_cpu(void);
void __init cpuinfo_store_boot_cpu(void);
void __init init_cpu_features(struct cpuinfo_arm64 *info);
+void init_cpu_32bit_features(struct cpuinfo_arm64 *info);
void update_cpu_features(int cpu, struct cpuinfo_arm64 *info,
struct cpuinfo_arm64 *boot);
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 9cde5d2e768f..d00d673d423b 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -17,6 +17,7 @@
#ifndef __ASSEMBLY__
#include <linux/bug.h>
+#include <linux/cpumask.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
@@ -370,6 +371,8 @@ cpucap_multi_entry_cap_matches(const struct arm64_cpu_capabilities *entry,
return false;
}
+extern cpumask_t aarch32_el0_mask;
+
extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
extern struct static_key_false cpu_hwcap_keys[ARM64_NCAPS];
extern struct static_key_false arm64_const_caps_ready;
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 8c73764b9ed2..54a4c912a7ab 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -66,6 +66,7 @@ void arch_release_task_struct(struct task_struct *tsk);
#define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */
#define TIF_UPROBE 4 /* uprobe breakpoint or singlestep */
#define TIF_FSCHECK 5 /* Check FS is USER_DS on return */
+#define TIF_SET_32BIT_AFFINITY 6 /* set thread affinity for asymmetric AArch32 */
#define TIF_NOHZ 7
#define TIF_SYSCALL_TRACE 8 /* syscall trace active */
#define TIF_SYSCALL_AUDIT 9 /* syscall auditing */
@@ -95,11 +96,13 @@ void arch_release_task_struct(struct task_struct *tsk);
#define _TIF_UPROBE (1 << TIF_UPROBE)
#define _TIF_FSCHECK (1 << TIF_FSCHECK)
#define _TIF_32BIT (1 << TIF_32BIT)
+#define _TIF_SET_32BIT_AFFINITY (1 << TIF_SET_32BIT_AFFINITY)
#define _TIF_SVE (1 << TIF_SVE)
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
_TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
- _TIF_UPROBE | _TIF_FSCHECK)
+ _TIF_UPROBE | _TIF_FSCHECK | \
+ _TIF_SET_32BIT_AFFINITY)
#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
_TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index da92693b202c..c0d656cfe5b8 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -8,7 +8,6 @@
#define pr_fmt(fmt) "CPU features: " fmt
#include <linux/bsearch.h>
-#include <linux/cpumask.h>
#include <linux/crash_dump.h>
#include <linux/sort.h>
#include <linux/stop_machine.h>
@@ -164,7 +163,11 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL3_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL2_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
+#ifndef CONFIG_ASYMMETRIC_AARCH32
ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
+#else
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_HIGHER_SAFE, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
+#endif
ARM64_FTR_END,
};
@@ -509,7 +512,7 @@ static void __init sort_ftr_regs(void)
* Any bits that are not covered by an arm64_ftr_bits entry are considered
* RES0 for the system-wide value, and must strictly match.
*/
-static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
+static void init_cpu_ftr_reg(u32 sys_reg, u64 new)
{
u64 val = 0;
u64 strict_mask = ~0x0ULL;
@@ -590,25 +593,6 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1);
init_cpu_ftr_reg(SYS_ID_AA64ZFR0_EL1, info->reg_id_aa64zfr0);
- if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
- init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
- init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
- init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
- init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
- init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
- init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
- init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
- init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
- init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
- init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
- init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
- init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
- init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
- init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
- init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
- init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
- }
-
if (id_aa64pfr0_sve(info->reg_id_aa64pfr0)) {
init_cpu_ftr_reg(SYS_ZCR_EL1, info->reg_zcr);
sve_init_vq_map();
@@ -627,6 +611,26 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
setup_boot_cpu_capabilities();
}
+void init_cpu_32bit_features(struct cpuinfo_arm64 *info)
+{
+ init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
+ init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
+ init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
+ init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
+ init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
+ init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
+ init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
+ init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
+ init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
+ init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
+ init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
+ init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
+ init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
+ init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
+ init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
+ init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
+}
+
static void update_cpu_ftr_reg(struct arm64_ftr_reg *reg, u64 new)
{
const struct arm64_ftr_bits *ftrp;
@@ -1264,6 +1268,16 @@ static bool can_use_gic_priorities(const struct arm64_cpu_capabilities *entry,
}
#endif
+#ifdef CONFIG_ASYMMETRIC_AARCH32
+cpumask_t aarch32_el0_mask;
+
+static void cpu_enable_aarch32_el0(struct arm64_cpu_capabilities const *cap)
+{
+ if (has_cpuid_feature(cap, SCOPE_LOCAL_CPU))
+ cpumask_set_cpu(smp_processor_id(), &aarch32_el0_mask);
+}
+#endif
+
static const struct arm64_cpu_capabilities arm64_features[] = {
{
.desc = "GIC system register CPU interface",
@@ -1340,7 +1354,12 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
{
.desc = "32-bit EL0 Support",
.capability = ARM64_HAS_32BIT_EL0,
+#ifndef CONFIG_ASYMMETRIC_AARCH32
.type = ARM64_CPUCAP_SYSTEM_FEATURE,
+#else
+ .type = ARM64_CPUCAP_WEAK_LOCAL_CPU_FEATURE,
+ .cpu_enable = cpu_enable_aarch32_el0,
+#endif
.matches = has_cpuid_feature,
.sys_reg = SYS_ID_AA64PFR0_EL1,
.sign = FTR_UNSIGNED,
@@ -1983,7 +2002,8 @@ static void verify_local_cpu_capabilities(void)
verify_local_elf_hwcaps(arm64_elf_hwcaps);
- if (system_supports_32bit_el0())
+ if (system_supports_32bit_el0() &&
+ this_cpu_has_cap(ARM64_HAS_32BIT_EL0))
verify_local_elf_hwcaps(compat_elf_hwcaps);
if (system_supports_sve())
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 05933c065732..bda37687bb66 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -351,27 +351,6 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1);
- /* Update the 32bit ID registers only if AArch32 is implemented */
- if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
- info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
- info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
- info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
- info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
- info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
- info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
- info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
- info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
- info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
- info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
- info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
- info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
- info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
-
- info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
- info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
- info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
- }
-
if (IS_ENABLED(CONFIG_ARM64_SVE) &&
id_aa64pfr0_sve(info->reg_id_aa64pfr0))
info->reg_zcr = read_zcr_features();
@@ -379,10 +358,46 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
cpuinfo_detect_icache_policy(info);
}
+static void __cpuinfo_store_cpu_32bit(struct cpuinfo_arm64 *info)
+{
+ info->aarch32_valid = true;
+
+ info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
+ info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
+ info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
+ info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
+ info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
+ info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
+ info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
+ info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
+ info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
+ info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
+ info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
+ info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
+ info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
+
+ info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
+ info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
+ info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
+}
+
void cpuinfo_store_cpu(void)
{
struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
__cpuinfo_store_cpu(info);
+ if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
+ __cpuinfo_store_cpu_32bit(info);
+ /*
+ * With asymmetric AArch32 support, populate the boot CPU information
+ * on the first 32-bit capable secondary CPU if the primary one
+ * skipped this step.
+ */
+ if (IS_ENABLED(CONFIG_ASYMMETRIC_AARCH32) &&
+ !boot_cpu_data.aarch32_valid &&
+ id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
+ __cpuinfo_store_cpu_32bit(&boot_cpu_data);
+ init_cpu_32bit_features(&boot_cpu_data);
+ }
update_cpu_features(smp_processor_id(), info, &boot_cpu_data);
}
@@ -390,9 +405,13 @@ void __init cpuinfo_store_boot_cpu(void)
{
struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0);
__cpuinfo_store_cpu(info);
+ if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
+ __cpuinfo_store_cpu_32bit(info);
boot_cpu_data = *info;
init_cpu_features(&boot_cpu_data);
+ if (id_aa64pfr0_32bit_el0(boot_cpu_data.reg_id_aa64pfr0))
+ init_cpu_32bit_features(&boot_cpu_data);
}
device_initcall(cpuinfo_regs_init);
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index cc1d8b1025b1..35f0c93699ce 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -494,6 +494,15 @@ static void entry_task_switch(struct task_struct *next)
__this_cpu_write(__entry_task, next);
}
+static void aarch32_thread_switch(struct task_struct *next)
+{
+ struct thread_info *ti = task_thread_info(next);
+
+ if (IS_ENABLED(CONFIG_ASYMMETRIC_AARCH32) && is_compat_thread(ti) &&
+ !cpumask_test_cpu(smp_processor_id(), &aarch32_el0_mask))
+ set_ti_thread_flag(ti, TIF_SET_32BIT_AFFINITY);
+}
+
/*
* Thread switching.
*/
@@ -511,6 +520,7 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
ptrauth_thread_switch(next);
ssbs_thread_switch(next);
scs_overflow_check(next);
+ aarch32_thread_switch(next);
/*
* Complete any pending TLB or cache maintenance on this CPU in case
@@ -569,6 +579,13 @@ void arch_setup_new_exec(void)
current->mm->context.flags = is_compat_task() ? MMCF_AARCH32 : 0;
ptrauth_thread_init_user(current);
+
+ /*
+ * If exec'ing a 32-bit task, force the asymmetric 32-bit feature
+ * check as the task may not go through a switch_to() call.
+ */
+ if (IS_ENABLED(CONFIG_ASYMMETRIC_AARCH32) && is_compat_task())
+ set_thread_flag(TIF_SET_32BIT_AFFINITY);
}
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index dd2cdc0d5be2..d8cdd3211d68 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -8,6 +8,7 @@
#include <linux/cache.h>
#include <linux/compat.h>
+#include <linux/cpumask.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/signal.h>
@@ -896,6 +897,29 @@ static void do_signal(struct pt_regs *regs)
restore_saved_sigmask();
}
+static void set_32bit_cpus_allowed(void)
+{
+ int ret;
+
+ /*
+ * Try to honour as best as possible whatever affinity request this
+ * task has. If it spans no compatible CPU, disregard it entirely.
+ */
+ if (cpumask_intersects(current->cpus_ptr, &aarch32_el0_mask)) {
+ cpumask_t cpus_allowed;
+
+ cpumask_and(&cpus_allowed, current->cpus_ptr, &aarch32_el0_mask);
+ ret = set_cpus_allowed_ptr(current, &cpus_allowed);
+ } else {
+ ret = set_cpus_allowed_ptr(current, &aarch32_el0_mask);
+ }
+
+ if (ret) {
+ pr_warn_once("No CPUs capable of running 32-bit tasks\n");
+ force_sig(SIGKILL);
+ }
+}
+
asmlinkage void do_notify_resume(struct pt_regs *regs,
unsigned long thread_flags)
{
@@ -910,7 +934,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
/* Check valid user FS if needed */
addr_limit_user_check();
- if (thread_flags & _TIF_NEED_RESCHED) {
+ if (IS_ENABLED(CONFIG_ASYMMETRIC_AARCH32) &&
+ thread_flags & _TIF_SET_32BIT_AFFINITY) {
+ clear_thread_flag(TIF_SET_32BIT_AFFINITY);
+ set_32bit_cpus_allowed();
+ } else if (thread_flags & _TIF_NEED_RESCHED) {
/* Unmask Debug and SError for the next task */
local_daif_restore(DAIF_PROCCTX_NOIRQ);
--
2.29.2
@@ -0,0 +1,89 @@
Upstream-Status: Backport [http://www.linux-arm.org/git?p=linux-power.git;a=commit;h=e6b567c1cc07dd1690e5d34b6a93ab9819ab2eeb]
Signed-off-by: Usama Arif <usama.arif@arm.com>
From d6acb605de7d40c295ada9b1f4c8336e4db71ae4 Mon Sep 17 00:00:00 2001
From: Valentin Schneider <valentin.schneider@arm.com>
Date: Thu, 5 Sep 2019 17:53:19 +0100
Subject: [PATCH 2/2] arm64: smp: Prevent hotplugging the last AArch32-able CPU
EL0 AArch32 tasks are now sigkilled when they can't run on any
compatible CPU, either because there aren't any left (hotplug) or
because they aren't allowed to run on those left (task affinity).
However, it has been deemed valuable to prevent the loss of
functionality resulting in offlining the last AArch32-compatible CPU.
Add arch-specific hook in _cpu_down() that allows checking whether we
can offline a cpu or not and use that hook to veto offlining the last
AArch32 CPU.
Signed-off-by: Valentin Schneider <valentin.schneider@arm.com>
Signed-off-by: Qais Yousef <qais.yousef@arm.com>
---
arch/arm64/kernel/smp.c | 22 ++++++++++++++++++++++
kernel/cpu.c | 9 +++++++++
2 files changed, 31 insertions(+)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 038ce6263d1c..c8ab4ee29f32 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -85,6 +85,28 @@ static inline int op_cpu_kill(unsigned int cpu)
}
#endif
+bool arch_allows_cpu_disable(int cpu, int tasks_frozen,
+ enum cpuhp_state target)
+{
+ /*
+ * Don't let the last AArch32-compatible CPU go down unless the request
+ * is related to suspend (!tasks_frozen) then allow it to be offlined
+ * or we'll break suspend-to-ram functionality.
+ */
+ if (IS_ENABLED(CONFIG_ASYMMETRIC_AARCH32) &&
+ !cpumask_empty(&aarch32_el0_mask) &&
+ !tasks_frozen) {
+ cpumask_t online;
+
+ cpumask_and(&online, &aarch32_el0_mask, cpu_online_mask);
+
+ if (cpumask_weight(&online) == 1)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Boot a secondary CPU, and assign it the specified idle task.
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 261b5098f81c..4fae9b61f442 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -134,6 +134,12 @@ static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state)
return cpuhp_hp_states + state;
}
+bool __weak arch_allows_cpu_disable(int cpu, int tasks_frozen,
+ enum cpuhp_state target)
+{
+ return true;
+}
+
/**
* cpuhp_invoke_callback _ Invoke the callbacks for a given state
* @cpu: The cpu for which the callback should be invoked
@@ -985,6 +991,9 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen,
if (!cpu_present(cpu))
return -EINVAL;
+ if (!arch_allows_cpu_disable(cpu, tasks_frozen, target))
+ return -EBUSY;
+
cpus_write_lock();
cpuhp_tasks_frozen = tasks_frozen;
--
2.29.2
@@ -2,6 +2,7 @@ CONFIG_POSIX_MQUEUE=y
CONFIG_AUDIT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_ASYMMETRIC_AARCH32=y
CONFIG_PREEMPT=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_BSD_PROCESS_ACCT=y
@@ -116,7 +117,6 @@ CONFIG_ACPI_APEI_PCIEAER=y
CONFIG_ACPI_APEI_MEMORY_FAILURE=y
CONFIG_ACPI_APEI_EINJ=y
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y