Skip to contentSkip to conversation

arm64: Make Aarch32 compatibility enablement optional at boot

PATCH Superseded Andrea della Porta 6 participants 22 messages Oct 18, 2023 – Oct 22, 2023

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover arm64: Make Aarch32 compatibility enablement optional at boot

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

*** Notes about syscall management ***
VBAR_EL1 register, which holds the exception table address, is setup very early in the boot process, before parse_early_param(). This means that it's not possible to access boot parameter before setting the register. Also, setting the aforementioned register for secondary cpus is done later in the boot flow. Several ways to work around this has been considered, among which:

* resetting VBAR_EL1 to point to one of two vector tables (the former with 32-bit exceptions handler enabled and the latter pointing to unhandled stub, just as if CONFIG_COMPAT is enabled) depending on the proposed boot parameter. This has the disadvantage to produce a somewhat messy patchset involving several lines, has higher cognitive load since there are at least three places where the register is getting changed (not near to each other), and have implications on other code segments (namely kpti, kvm and vdso), requiring special care.

* patching the vector table contents once the early param is available. This has most of the implications of the previous option (except maybe not impacting other code segments), plus it sounds a little 'hackish'.

The chosen approach involves conditional executing 32-bit syscalls depending on the parameter value. This of course results in a little performance loss, but has the following advantages:

* all the cons from previously explained alternatives are solved
* users of 32-bit apps on 64-bit kernel are already suffering from performance losses due to 32-bit apps not fully leveraging the 64-bit processor, so they are already aware of this
* users of 32-bit apps on 64-bit kernel are believed
to be a minority and most of the time there are sources available to be recompiled for 64-bit as a workaround for better performance

It worth mentioning that users of 64-bit apps are, of course, unaffected.

Based on the work from Nikolay Borisov, see:

Andrea della Porta (4):
arm64: Introduce aarch32_enabled() arm64/process: Make loading of 32bit processes depend on

    aarch32_enabled()
  arm64/entry-common: Make Aarch32 syscalls' availability depend on
    aarch32_enabled()

arm64: Make Aarch32 emulation boot time configurable

 .../admin-guide/kernel-parameters.txt         |  7 ++++
 arch/arm64/Kconfig                            |  9 +++++
 arch/arm64/include/asm/compat.h               | 12 +++++++
 arch/arm64/kernel/entry-common.c              | 33 +++++++++++++++++--
 arch/arm64/kernel/process.c                   |  2 +-
 5 files changed, 59 insertions(+), 4 deletions(-)
Signature · 1 line
2.35.3
#1 lore raw

PATCH 1/4 arm64: Introduce aarch32_enabled()

Aarch32 bit support on 64bit kernels depends on whether CONFIG_COMPAT is selected or not. As it is a compile time option it doesn't provide the flexibility to have distributions set their own policy for Aarch32 support and give the user the flexibility to override it.

As a first step introduce aarch32_enabled() which abstracts whether 32 bit compat is turned on or off. Upcoming patches will implement the ability to set Aarch32 compat state at boot time.

---
 arch/arm64/include/asm/compat.h  | 12 ++++++++++++
 arch/arm64/kernel/entry-common.c |  2 ++
 2 files changed, 14 insertions(+)
arch/arm64/include/asm/compat.h +12 -0
index ae904a1ad529..f5ef71bd0024 100644
@@ -98,6 +98,13 @@ static inline int is_compat_thread(struct thread_info *thread)
 
 long compat_arm_syscall(struct pt_regs *regs, int scno);
 
+extern bool __aarch32_enabled;
+
+static inline bool aarch32_enabled(void)
+{
+	return __aarch32_enabled;
+}
+
 #else /* !CONFIG_COMPAT */
 
 static inline int is_compat_thread(struct thread_info *thread)
@@ -105,5 +112,10 @@ static inline int is_compat_thread(struct thread_info *thread)
 	return 0;
 }
 
+static inline bool aarch32_enabled(void)
+{
+	return false;
+}
+
 #endif /* CONFIG_COMPAT */
 #endif /* __ASM_COMPAT_H */
arch/arm64/kernel/entry-common.c +2 -0
index 0fc94207e69a..69ff9b8c0bde 100644
@@ -877,6 +877,8 @@ asmlinkage void noinstr el0t_32_error_handler(struct pt_regs *regs)
 {
 	__el0_error_handler_common(regs);
 }
+
+bool __aarch32_enabled __ro_after_init = true;
 #else /* CONFIG_COMPAT */
 UNHANDLED(el0t, 32, sync)
 UNHANDLED(el0t, 32, irq)
Signature · 1 line
2.35.3
#2 lore raw

PATCH 2/4 arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

In reply to Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

---
 arch/arm64/kernel/process.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/kernel/process.c +1 -1
index 657ea273c0f9..96832f1ec3ee 100644
@@ -601,7 +601,7 @@ unsigned long arch_align_stack(unsigned long sp)
 #ifdef CONFIG_COMPAT
 int compat_elf_check_arch(const struct elf32_hdr *hdr)
 {
-	if (!system_supports_32bit_el0())
+	if (!system_supports_32bit_el0() || !aarch32_enabled())
 		return false;
 
 	if ((hdr)->e_machine != EM_ARM)
Signature · 1 line
2.35.3
reviewer
#3 lore raw

Re: [PATCH 2/4] arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

Quoted 8 lines · Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

Why can't you make system_supports_32bit_el0() take the option into account instead?

Mark.

Quoted 20 lines
---
 arch/arm64/kernel/process.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/kernel/process.c +1 -1
index 657ea273c0f9..96832f1ec3ee 100644
@@ -601,7 +601,7 @@ unsigned long arch_align_stack(unsigned long sp)
 #ifdef CONFIG_COMPAT
 int compat_elf_check_arch(const struct elf32_hdr *hdr)
 {
-	if (!system_supports_32bit_el0())
+	if (!system_supports_32bit_el0() || !aarch32_enabled())
 		return false;
 
 	if ((hdr)->e_machine != EM_ARM)
Signature · 1 line
2.35.3
#4 lore raw

Re: [PATCH 2/4] arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

Quoted 5 lines · Mark Rutland
Quoted 4 lines · Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Quoted 8 lines
Quoted 4 lines

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

Why can't you make system_supports_32bit_el0() take the option into account instead?

I may be wrong here, but it seems to me that system_supports_32bit_el0() answers teh question "can this system supports compat execution?" rather than "do I want this system to run any compat execution?". That's the point of aarch32_enabled(), to state whether we want teh system to run A32 code or not, regardless of the system supporting it (of course, if the system does not support A32 in EL0, this is a no-no, but that's another story).

Andrea

reviewer
#5 lore raw

Re: [PATCH 2/4] arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

Quoted 15 lines · Andrea della Porta
Quoted 5 lines · Mark Rutland
Quoted 4 lines · Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Quoted 8 lines
Quoted 4 lines

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

Why can't you make system_supports_32bit_el0() take the option into account instead?

Quoted 6 lines

I may be wrong here, but it seems to me that system_supports_32bit_el0() answers teh question "can this system supports compat execution?" rather than "do I want this system to run any compat execution?". That's the point of aarch32_enabled(), to state whether we want teh system to run A32 code or not, regardless of the system supporting it (of course, if the system does not support A32 in EL0, this is a no-no, but that's another story).

That's what the implementation does today, but we're really using it as a "do we intend for 32-bit EL0 to work?" predicate, and generally the system_supports_${FEATURE}() helpers are affected by the combination of actual HW support, kernel config options, *and* kernel command line options. For example, system_supports_sve() is affected by both CONFIG_ARM64_SVE and the "arm64.nosve" command line option.

Thanks,
Mark.

#6 lore raw

Re: [PATCH 2/4] arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

Quoted 30 lines · Mark Rutland
Quoted 15 lines · Andrea della Porta
Quoted 5 lines · Mark Rutland
Quoted 4 lines · Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Quoted 8 lines
Quoted 4 lines

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

Why can't you make system_supports_32bit_el0() take the option into account instead?

Quoted 6 lines

I may be wrong here, but it seems to me that system_supports_32bit_el0() answers teh question "can this system supports compat execution?" rather than "do I want this system to run any compat execution?". That's the point of aarch32_enabled(), to state whether we want teh system to run A32 code or not, regardless of the system supporting it (of course, if the system does not support A32 in EL0, this is a no-no, but that's another story).

That's what the implementation does today, but we're really using it as a "do we intend for 32-bit EL0 to work?" predicate, and generally the system_supports_${FEATURE}() helpers are affected by the combination of actual HW support, kernel config options, *and* kernel command line options. For example, system_supports_sve() is affected by both CONFIG_ARM64_SVE and the "arm64.nosve" command line option.

Quoted 2 lines

Thanks,
Mark.

Many thanks for the explanation, then inserting aach32_enabled() in system_supports_32bit_el0() is the way to go.

Andrea

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

reviewer
#7 lore raw

Re: [PATCH 2/4] arm64/process: Make loading of 32bit processes depend on aarch32_enabled()

Quoted 31 lines · Andrea della Porta
Quoted 30 lines · Mark Rutland
Quoted 15 lines · Andrea della Porta
Quoted 5 lines · Mark Rutland
Quoted 4 lines · Andrea della Porta

Major aspect of Aarch32 emulation is the ability to load 32bit processes.
That's currently decided (among others) by compat_elf_check_arch().

Quoted 8 lines
Quoted 4 lines

Make the macro use aarch32_enabled() to decide if Aarch32 compat is enabled before loading a 32bit process.

Why can't you make system_supports_32bit_el0() take the option into account instead?

Quoted 6 lines

I may be wrong here, but it seems to me that system_supports_32bit_el0() answers teh question "can this system supports compat execution?" rather than "do I want this system to run any compat execution?". That's the point of aarch32_enabled(), to state whether we want teh system to run A32 code or not, regardless of the system supporting it (of course, if the system does not support A32 in EL0, this is a no-no, but that's another story).

That's what the implementation does today, but we're really using it as a "do we intend for 32-bit EL0 to work?" predicate, and generally the system_supports_${FEATURE}() helpers are affected by the combination of actual HW support, kernel config options, *and* kernel command line options. For example, system_supports_sve() is affected by both CONFIG_ARM64_SVE and the "arm64.nosve" command line option.

Quoted 5 lines
Quoted 2 lines

Thanks,
Mark.

Many thanks for the explanation, then inserting aach32_enabled() in system_supports_32bit_el0() is the way to go.

I think what we should do here is clean up the way we implement system_supports_32bit_el0() such that it can be a cpucap, and have the conditions that would affect aarch32_enabled() feed into that. That way, system_supports_32bit_el0() will compile down to a single branch/nop (or elided entirely when known to be false at compile-time), and with that I think can reasonably fold the existing UNHANDLED() logic into the entry-common.c exception handlers as a simplification.

The only obviously painful part is that enable_mismatched_32bit_el0() allows (mismatched) AArch32 support to be enabled after we finalize system cpucaps, as part of a late hotplug. I suspect that was implemented that way for expedience rather than because we wanted to enable mismatched AArch32 after finalizing cpucaps.

Will, do you remember why we used a cpuhp callback for enabling mismatched 32-bit support? I couldn't see anything explicit in the commit message for:

2122a833316f2f3f ("arm64: Allow mismatched 32-bit EL0 support")

... and I suspect it was just easier to write that way, rather than adding more code around setup_system_capabilities() ?

Mark.

#8 lore raw

PATCH 3/4 arm64/entry-common: Make Aarch32 syscalls' availability depend on aarch32_enabled()

In reply to Andrea della Porta

Another major aspect of supporting running of 32bit processes is the ability to access 32bit syscalls. Such syscalls can be invoked by using the svc instruction.

If Aarch32 emulation is disabled ensure that calling svc results in the same behavior as if CONFIG_COMPAT has not been enabled (i.e. a kernel panic).

---
 arch/arm64/kernel/entry-common.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
arch/arm64/kernel/entry-common.c +22 -3
index 69ff9b8c0bde..32761760d9dd 100644
@@ -802,6 +802,11 @@ asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_COMPAT
+UNHANDLED(el0t, 32, sync_ni)
+UNHANDLED(el0t, 32, irq_ni)
+UNHANDLED(el0t, 32, fiq_ni)
+UNHANDLED(el0t, 32, error_ni)
+
 static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
 {
 	enter_from_user_mode(regs);
@@ -821,6 +826,11 @@ static void noinstr el0_svc_compat(struct pt_regs *regs)
 
 asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 {
+	if (!aarch32_enabled()) {
+		el0t_32_sync_ni_handler(regs);
+		return;
+	}
+
 	unsigned long esr = read_sysreg(esr_el1);
 
 	switch (ESR_ELx_EC(esr)) {
@@ -865,17 +875,26 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 
 asmlinkage void noinstr el0t_32_irq_handler(struct pt_regs *regs)
 {
-	__el0_irq_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_irq_ni_handler(regs);
+	else
+		__el0_irq_handler_common(regs);
 }
 
 asmlinkage void noinstr el0t_32_fiq_handler(struct pt_regs *regs)
 {
-	__el0_fiq_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_fiq_ni_handler(regs);
+	else
+		__el0_fiq_handler_common(regs);
 }
 
 asmlinkage void noinstr el0t_32_error_handler(struct pt_regs *regs)
 {
-	__el0_error_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_error_ni_handler(regs);
+	else
+		__el0_error_handler_common(regs);
 }
 
 bool __aarch32_enabled __ro_after_init = true;
Signature · 1 line
2.35.3
reviewer
#9 lore raw

Re: [PATCH 3/4] arm64/entry-common: Make Aarch32 syscalls' availability depend on aarch32_enabled()

Quoted 7 lines · Andrea della Porta

Another major aspect of supporting running of 32bit processes is the ability to access 32bit syscalls. Such syscalls can be invoked by using the svc instruction.

If Aarch32 emulation is disabled ensure that calling svc results in the same behavior as if CONFIG_COMPAT has not been enabled (i.e. a kernel panic).

It's not "emulation" it's directly supported by the hardware.

Quoted 11 lines
---
 arch/arm64/kernel/entry-common.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
arch/arm64/kernel/entry-common.c +0 -0
index 69ff9b8c0bde..32761760d9dd 100644

arch/arm64/kernel/entry-common.c asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs) · line 808

Quoted 7 lines
arch/arm64/kernel/entry-common.c +4 -0
@@ -802,6 +802,11 @@ asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_COMPAT
+UNHANDLED(el0t, 32, sync_ni)
+UNHANDLED(el0t, 32, irq_ni)
+UNHANDLED(el0t, 32, fiq_ni)
+UNHANDLED(el0t, 32, error_ni)

IRQ, FIQ, and SError are not syscalls, so the commit title is bad.

arch/arm64/kernel/entry-common.c static void noinstr el0_svc_compat(struct pt_regs *regs) · line 832

Quoted 12 lines
arch/arm64/kernel/entry-common.c +5 -0
+
 static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
 {
 	enter_from_user_mode(regs);
@@ -821,6 +826,11 @@ static void noinstr el0_svc_compat(struct pt_regs *regs)
 
 asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 {
+	if (!aarch32_enabled()) {
+		el0t_32_sync_ni_handler(regs);
+		return;
+	}

Why do we have to do this at all?

If we don't have AArch32 tasks, these paths are unreachable. Why do we need to check that they aren't called?

Mark.

Quoted 37 lines
arch/arm64/kernel/entry-common.c +13 -3
+
 	unsigned long esr = read_sysreg(esr_el1);
 
 	switch (ESR_ELx_EC(esr)) {
@@ -865,17 +875,26 @@ asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 
 asmlinkage void noinstr el0t_32_irq_handler(struct pt_regs *regs)
 {
-	__el0_irq_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_irq_ni_handler(regs);
+	else
+		__el0_irq_handler_common(regs);
 }
 
 asmlinkage void noinstr el0t_32_fiq_handler(struct pt_regs *regs)
 {
-	__el0_fiq_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_fiq_ni_handler(regs);
+	else
+		__el0_fiq_handler_common(regs);
 }
 
 asmlinkage void noinstr el0t_32_error_handler(struct pt_regs *regs)
 {
-	__el0_error_handler_common(regs);
+	if (!aarch32_enabled())
+		el0t_32_error_ni_handler(regs);
+	else
+		__el0_error_handler_common(regs);
 }
 
 bool __aarch32_enabled __ro_after_init = true;
Signature · 1 line
2.35.3
#10 lore raw

Re: [PATCH 3/4] arm64/entry-common: Make Aarch32 syscalls' availability depend on aarch32_enabled()

Quoted 10 lines · Mark Rutland
Quoted 7 lines · Andrea della Porta

Another major aspect of supporting running of 32bit processes is the ability to access 32bit syscalls. Such syscalls can be invoked by using the svc instruction.

If Aarch32 emulation is disabled ensure that calling svc results in the same behavior as if CONFIG_COMPAT has not been enabled (i.e. a kernel panic).

It's not "emulation" it's directly supported by the hardware.

You're right. I also struggled to use this label but I just reused the same name from Nikolai's patchset for x86, in the hope that the option would be more recognizable (something like 'ARCH_emulation' that could be used maybe for other platforms as well), but I agree with you that this is highly misleading. I will change it to something more straightforward.

Quoted 14 lines
Quoted 14 lines
---
 arch/arm64/kernel/entry-common.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)
arch/arm64/kernel/entry-common.c +0 -0
index 69ff9b8c0bde..32761760d9dd 100644
@@ -802,6 +802,11 @@ asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs)
 }
 
Quoted 7 lines

arch/arm64/kernel/entry-common.c asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs) · line 808

Quoted 5 lines
arch/arm64/kernel/entry-common.c +4 -0
 #ifdef CONFIG_COMPAT
+UNHANDLED(el0t, 32, sync_ni)
+UNHANDLED(el0t, 32, irq_ni)
+UNHANDLED(el0t, 32, fiq_ni)
+UNHANDLED(el0t, 32, error_ni)

IRQ, FIQ, and SError are not syscalls, so the commit title is bad.

Agreed. I'll call them exceptions.

Quoted 16 lines

arch/arm64/kernel/entry-common.c static void noinstr el0_svc_compat(struct pt_regs *regs) · line 832

Quoted 13 lines
arch/arm64/kernel/entry-common.c +5 -0
+
 static void noinstr el0_cp15(struct pt_regs *regs, unsigned long esr)
 {
 	enter_from_user_mode(regs);
@@ -821,6 +826,11 @@ static void noinstr el0_svc_compat(struct pt_regs *regs)
 
 asmlinkage void noinstr el0t_32_sync_handler(struct pt_regs *regs)
 {
+	if (!aarch32_enabled()) {
+		el0t_32_sync_ni_handler(regs);
+		return;
+	}

Why do we have to do this at all?

Quoted 4 lines

If we don't have AArch32 tasks, these paths are unreachable. Why do we need to check that they aren't called?

Mark.

Agreed. Please see also my previous comments here:
https://lore.kernel.org/lkml/ZTEKabxNdegsbxyv@apocalypse/
https://lore.kernel.org/lkml/ZTD0DAes-J-YQ2eu@apocalypse/

but again, that's only speculative as of now, so we can ignore that part.

Andrea

#11 lore raw

Re: [PATCH 3/4] arm64/entry-common: Make Aarch32 syscalls' availability depend on aarch32_enabled()

In reply to Andrea della Porta
kernel test robot noticed the following build warnings:

Hi Andrea,

kernel test robot noticed the following build warnings:

[auto build test WARNING on arm64/for-next/core]
[also build test WARNING on arm-perf/for-next/perf arm/for-next arm/fixes kvmarm/next soc/for-next linus/master v6.6-rc6 next-20231020] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Andrea-della-Porta/arm64-Introduce-aarch32_enabled/20231018-191517
base:   https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
patch link:    https://lore.kernel.org/r/88bdea628a13747bff32c0c3055d6d6ef7264d96.1697614386.git.andrea.porta%40suse.com
patch subject: [PATCH 3/4] arm64/entry-common: Make Aarch32 syscalls' availability depend on aarch32_enabled()
config: arm64-randconfig-003-20231023 (https://download.01.org/0day-ci/archive/20231023/[email protected]/config)
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231023/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> arch/arm64/kernel/entry-common.c:805:11: warning: no previous prototype for 'el0t_32_sync_ni_handler' [-Wmissing-prototypes]
     805 | UNHANDLED(el0t, 32, sync_ni)
         |           ^~~~
   arch/arm64/kernel/entry-common.c:302:25: note: in definition of macro 'UNHANDLED'
     302 | asmlinkage void noinstr el##_##regsize##_##vector##_handler(struct pt_regs *regs)       \
         |                         ^~
>> arch/arm64/kernel/entry-common.c:806:11: warning: no previous prototype for 'el0t_32_irq_ni_handler' [-Wmissing-prototypes]
     806 | UNHANDLED(el0t, 32, irq_ni)
         |           ^~~~
   arch/arm64/kernel/entry-common.c:302:25: note: in definition of macro 'UNHANDLED'
     302 | asmlinkage void noinstr el##_##regsize##_##vector##_handler(struct pt_regs *regs)       \
         |                         ^~
>> arch/arm64/kernel/entry-common.c:807:11: warning: no previous prototype for 'el0t_32_fiq_ni_handler' [-Wmissing-prototypes]
     807 | UNHANDLED(el0t, 32, fiq_ni)
         |           ^~~~
   arch/arm64/kernel/entry-common.c:302:25: note: in definition of macro 'UNHANDLED'
     302 | asmlinkage void noinstr el##_##regsize##_##vector##_handler(struct pt_regs *regs)       \
         |                         ^~
>> arch/arm64/kernel/entry-common.c:808:11: warning: no previous prototype for 'el0t_32_error_ni_handler' [-Wmissing-prototypes]
     808 | UNHANDLED(el0t, 32, error_ni)
         |           ^~~~
   arch/arm64/kernel/entry-common.c:302:25: note: in definition of macro 'UNHANDLED'
     302 | asmlinkage void noinstr el##_##regsize##_##vector##_handler(struct pt_regs *regs)       \
         |                         ^~

vim +/el0t_32_sync_ni_handler +805 arch/arm64/kernel/entry-common.c

803
804 #ifdef CONFIG_COMPAT

 > 805	UNHANDLED(el0t, 32, sync_ni)
 > 806	UNHANDLED(el0t, 32, irq_ni)
 > 807	UNHANDLED(el0t, 32, fiq_ni)
 > 808	UNHANDLED(el0t, 32, error_ni)

809

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#12 lore raw

PATCH 4/4 arm64: Make Aarch32 emulation boot time configurable

In reply to Andrea della Porta

Distributions would like to reduce their attack surface as much as possible but at the same time they'd want to retain flexibility to cater to a variety of legacy software. This stems from the conjecture that compat layer is likely rarely tested and could have latent security bugs. Ideally distributions will set their default policy and also give users the ability to override it as appropriate.

To enable this use case, introduce CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED compile time option, which controls whether 32bit processes/syscalls should be allowed or not. This option is aimed mainly at distributions to set their preferred default behavior in their kernels.

To allow users to override the distro's policy, introduce the 'aarch32_emulation' parameter which allows overriding CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED state at boot time.

---
 Documentation/admin-guide/kernel-parameters.txt | 7 +++++++
 arch/arm64/Kconfig                              | 9 +++++++++
 arch/arm64/kernel/entry-common.c                | 8 +++++++-
 3 files changed, 23 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +7 -0
index 0a1731a0f0ef..a41c5e6f5d2e 100644
@@ -1,3 +1,10 @@
+	aarch32_emulation=	[ARM64]
+			Format: <bool>
+			When true, allows loading 32-bit programs and executing
+			32-bit syscalls, essentially overriding
+			AARCH32_EMULATION_DEFAULT_DISABLED at boot time. when false,
+			unconditionally disables AARCH32 emulation.
+
 	acpi=		[HW,ACPI,X86,ARM64,RISCV64]
 			Advanced Configuration and Power Interface
 			Format: { force | on | off | strict | noirq | rsdt |
arch/arm64/Kconfig +9 -0
index b10515c0200b..66c4cb273550 100644
@@ -1725,6 +1725,15 @@ config SETEND_EMULATION
 	  If unsure, say Y
 endif # ARMV8_DEPRECATED
 
+config AARCH32_EMULATION_DEFAULT_DISABLED
+	bool "Aarch32 emulation disabled by default"
+	default n
+	depends on COMPAT
+	help
+	  Make Aarch32 emulation disabled by default. This prevents loading 32-bit
+	  processes and access to 32-bit syscalls.
+
+	  If unsure, leave it to its default value.
 endif # COMPAT
 
 menu "ARMv8.1 architectural features"
arch/arm64/kernel/entry-common.c +7 -1
index 32761760d9dd..07f2b4e632b8 100644
@@ -897,7 +897,13 @@ asmlinkage void noinstr el0t_32_error_handler(struct pt_regs *regs)
 		__el0_error_handler_common(regs);
 }
 
-bool __aarch32_enabled __ro_after_init = true;
+bool __aarch32_enabled __ro_after_init = !IS_ENABLED(CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED);
+
+static int aarch32_emulation_override_cmdline(char *arg)
+{
+	return kstrtobool(arg, &__aarch32_enabled);
+}
+early_param("aarch32_emulation", aarch32_emulation_override_cmdline);
 #else /* CONFIG_COMPAT */
 UNHANDLED(el0t, 32, sync)
 UNHANDLED(el0t, 32, irq)
Signature · 1 line
2.35.3
reviewer
#13 lore raw

Re: [PATCH 4/4] arm64: Make Aarch32 emulation boot time configurable

Quoted 28 lines · Andrea della Porta

Distributions would like to reduce their attack surface as much as possible but at the same time they'd want to retain flexibility to cater to a variety of legacy software. This stems from the conjecture that compat layer is likely rarely tested and could have latent security bugs. Ideally distributions will set their default policy and also give users the ability to override it as appropriate.

To enable this use case, introduce CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED compile time option, which controls whether 32bit processes/syscalls should be allowed or not. This option is aimed mainly at distributions to set their preferred default behavior in their kernels.

To allow users to override the distro's policy, introduce the 'aarch32_emulation' parameter which allows overriding CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED state at boot time.

---
 Documentation/admin-guide/kernel-parameters.txt | 7 +++++++
 arch/arm64/Kconfig                              | 9 +++++++++
 arch/arm64/kernel/entry-common.c                | 8 +++++++-
 3 files changed, 23 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +0 -0
index 0a1731a0f0ef..a41c5e6f5d2e 100644

Documentation/admin-guide/kernel-parameters.txt line 6

Quoted 6 lines
Documentation/admin-guide/kernel-parameters.txt +6 -0
@@ -1,3 +1,10 @@
+	aarch32_emulation=	[ARM64]
+			Format: <bool>
+			When true, allows loading 32-bit programs and executing
+			32-bit syscalls, essentially overriding
+			AARCH32_EMULATION_DEFAULT_DISABLED at boot time. when false,
+			unconditionally disables AARCH32 emulation.

Can we please drop the 'emulation' part of the name? We don't use that terminology on arm64 for regular execution of compat tasks, and only use that to refer to true emulation of deprecated instructions.

We already have the 'allow_mismatched_32bit_el0' option; can we please us a name that aligns with that? e.g. 'allow_32bit_el0=false' to disable support.

Mark.

Quoted 46 lines
Documentation/admin-guide/kernel-parameters.txt +1 -0
+
 	acpi=		[HW,ACPI,X86,ARM64,RISCV64]
 			Advanced Configuration and Power Interface
 			Format: { force | on | off | strict | noirq | rsdt |
arch/arm64/Kconfig +9 -0
index b10515c0200b..66c4cb273550 100644
@@ -1725,6 +1725,15 @@ config SETEND_EMULATION
 	  If unsure, say Y
 endif # ARMV8_DEPRECATED
 
+config AARCH32_EMULATION_DEFAULT_DISABLED
+	bool "Aarch32 emulation disabled by default"
+	default n
+	depends on COMPAT
+	help
+	  Make Aarch32 emulation disabled by default. This prevents loading 32-bit
+	  processes and access to 32-bit syscalls.
+
+	  If unsure, leave it to its default value.
 endif # COMPAT
 
 menu "ARMv8.1 architectural features"
arch/arm64/kernel/entry-common.c +7 -1
index 32761760d9dd..07f2b4e632b8 100644
@@ -897,7 +897,13 @@ asmlinkage void noinstr el0t_32_error_handler(struct pt_regs *regs)
 		__el0_error_handler_common(regs);
 }
 
-bool __aarch32_enabled __ro_after_init = true;
+bool __aarch32_enabled __ro_after_init = !IS_ENABLED(CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED);
+
+static int aarch32_emulation_override_cmdline(char *arg)
+{
+	return kstrtobool(arg, &__aarch32_enabled);
+}
+early_param("aarch32_emulation", aarch32_emulation_override_cmdline);
 #else /* CONFIG_COMPAT */
 UNHANDLED(el0t, 32, sync)
 UNHANDLED(el0t, 32, irq)
Signature · 1 line
2.35.3
#14 lore raw

Re: [PATCH 4/4] arm64: Make Aarch32 emulation boot time configurable

Quoted 40 lines · Mark Rutland
Quoted 28 lines · Andrea della Porta

Distributions would like to reduce their attack surface as much as possible but at the same time they'd want to retain flexibility to cater to a variety of legacy software. This stems from the conjecture that compat layer is likely rarely tested and could have latent security bugs. Ideally distributions will set their default policy and also give users the ability to override it as appropriate.

To enable this use case, introduce CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED compile time option, which controls whether 32bit processes/syscalls should be allowed or not. This option is aimed mainly at distributions to set their preferred default behavior in their kernels.

To allow users to override the distro's policy, introduce the 'aarch32_emulation' parameter which allows overriding CONFIG_AARCH32_EMULATION_DEFAULT_DISABLED state at boot time.

---
 Documentation/admin-guide/kernel-parameters.txt | 7 +++++++
 arch/arm64/Kconfig                              | 9 +++++++++
 arch/arm64/kernel/entry-common.c                | 8 +++++++-
 3 files changed, 23 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +0 -0
index 0a1731a0f0ef..a41c5e6f5d2e 100644

Documentation/admin-guide/kernel-parameters.txt line 6

Quoted 6 lines
Documentation/admin-guide/kernel-parameters.txt +6 -0
@@ -1,3 +1,10 @@
+	aarch32_emulation=	[ARM64]
+			Format: <bool>
+			When true, allows loading 32-bit programs and executing
+			32-bit syscalls, essentially overriding
+			AARCH32_EMULATION_DEFAULT_DISABLED at boot time. when false,
+			unconditionally disables AARCH32 emulation.

Can we please drop the 'emulation' part of the name? We don't use that terminology on arm64 for regular execution of compat tasks, and only use that to refer to true emulation of deprecated instructions.

Quoted 3 lines

We already have the 'allow_mismatched_32bit_el0' option; can we please us a name that aligns with that? e.g. 'allow_32bit_el0=false' to disable support.

Sure, 'allow_mismatched_32bit_el0' will do. I'll prepare a patch accordingly.

Andrea

maintainer
#15 lore raw
In reply to Andrea della Porta

Hi,

Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

I proposed something similar in the past:

https://lkml.kernel.org/linux-fsdevel/[email protected]/

bu the conclusion there (see the reply from Kees) was that it was better to either use existing seccomp mechanisms or add something to control which binfmts can be loaded.

Will

#16 lore raw
Quoted 11 lines · Will Deacon

Hi,

Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

Quoted 7 lines

I proposed something similar in the past:

https://lkml.kernel.org/linux-fsdevel/[email protected]/

bu the conclusion there (see the reply from Kees) was that it was better to either use existing seccomp mechanisms or add something to control which binfmts can be loaded.

Right, I was going to reply along the same lines here: x86 is a bit of a special case that needs this, but I believe all the other architectures already guard the compat syscall execution on test_thread_flag(TIF_32BIT) that is only set by the compat binfmt loader.

Doing the reverse is something that has however come up in the past several times and that could be interesting: In order to run userspace emulation (qemu-user, fex, ...) we may want to allow calling syscalls and ioctls for foreign ABIs in a native task, and at that point having a mechanism to control this capability globally or per task would be useful as well.

The compat mode (arm32 on arm64) is the easiest case here, but the same thing could be done for emulating the very subtle architecture differences (x86-64 on arm64, arm64 on x86_64, arm32 on x86-compat, or any of the above on riscv or loongarch).

Arnd

#17 lore raw
Quoted 20 lines · Arnd Bergmann
Quoted 11 lines · Will Deacon

Hi,

Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

Quoted 7 lines

I proposed something similar in the past:

https://lkml.kernel.org/linux-fsdevel/[email protected]/

bu the conclusion there (see the reply from Kees) was that it was better to either use existing seccomp mechanisms or add something to control which binfmts can be loaded.

Quoted 5 lines

Right, I was going to reply along the same lines here: x86 is a bit of a special case that needs this, but I believe all the other architectures already guard the compat syscall execution on test_thread_flag(TIF_32BIT) that is only set by the compat binfmt loader.

Are you referring to the fact that x86 can switch at will between 32- and 64- bit code?

Regarding the TIF_32BIT flag, thanks for the head-up. I still believe though that this mechanism can somehow break down in the future, since prohibiting 32 bit executable loading *and* blocking 32 bit compat syscall are two separate path of execution, held together by the architecture prohibiting to switch to A32 instructions by design. Breaking the first rule and embedding wisely crafted A32 instruction in an executable is easy, while the difficult part is finding some 'reentrancy' to be able to do the execution state switch, as pinted out in https://lore.kernel.org/lkml/ZTD0DAes-J-YQ2eu@apocalypse/.
I agree it's highly speculative and not something to be concerned right now, it's just a head up, should the need arise in the future.

Quoted 7 lines

Doing the reverse is something that has however come up in the past several times and that could be interesting: In order to run userspace emulation (qemu-user, fex, ...) we may want to allow calling syscalls and ioctls for foreign ABIs in a native task, and at that point having a mechanism to control this capability globally or per task would be useful as well.

Quoted 6 lines

The compat mode (arm32 on arm64) is the easiest case here, but the same thing could be done for emulating the very subtle architecture differences (x86-64 on arm64, arm64 on x86_64, arm32 on x86-compat, or any of the above on riscv or loongarch).

Arnd

Really interesting, Since it's more related to emulation needs (my patch has another focus due to the fact that A64 can execute A32 natively), I'll take a look at this separately.

Andrea

#18 lore raw
Quoted 11 lines · Andrea della Porta
Quoted 7 lines · Arnd Bergmann

On Wed, Oct 18, 2023, at 14:27, Will Deacon wrote:

Right, I was going to reply along the same lines here: x86 is a bit of a special case that needs this, but I believe all the other architectures already guard the compat syscall execution on test_thread_flag(TIF_32BIT) that is only set by the compat binfmt loader.

Are you referring to the fact that x86 can switch at will between 32- and 64- bit code?

No.

Quoted 10 lines

Regarding the TIF_32BIT flag, thanks for the head-up. I still believe though that this mechanism can somehow break down in the future, since prohibiting 32 bit executable loading *and* blocking 32 bit compat syscall are two separate path of execution, held together by the architecture prohibiting to switch to A32 instructions by design. Breaking the first rule and embedding wisely crafted A32 instruction in an executable is easy, while the difficult part is finding some 'reentrancy' to be able to do the execution state switch, as pinted out in https://lore.kernel.org/lkml/ZTD0DAes-J-YQ2eu@apocalypse/.
I agree it's highly speculative and not something to be concerned right now, it's just a head up, should the need arise in the future.

There are (at least) five separate aspects to compat mode that are easy to mix up:

1. Instruction decoding -- switching between the modes supported by the CPU (A64/A32/T32)
2. Word size -- what happens to the upper 32 bits of a register in an arithmetic operation
3. Personality -- Which architecture string gets returned by the uname syscall (aarch64 vs armv8) as well as the format of /proc/cpuinfo
4. system call entry points -- how a process calls into native or compat syscalls, or possibly foreign OS emulation
5. Binary format -- elf32 vs elf64 executables

On most architectures with compat mode, 4. and 5. are fundamentally tied together today: a compat task can only call compat syscalls and a native task can only call native syscalls. x86 is the exception here, as it uses different instructions (int80, syscall, sysenter) and picks the syscall table based on that instruction.

I think 1. and 2. are also always tied to 5 on arm, but this is not necessarily true for other architectures. 3. used to be tied to 5 on some architectures in the past, but should be independent now.

Quoted 7 lines
Quoted 7 lines

Doing the reverse is something that has however come up in the past several times and that could be interesting: In order to run userspace emulation (qemu-user, fex, ...) we may want to allow calling syscalls and ioctls for foreign ABIs in a native task, and at that point having a mechanism to control this capability globally or per task would be useful as well.

Quoted 8 lines
Quoted 4 lines

The compat mode (arm32 on arm64) is the easiest case here, but the same thing could be done for emulating the very subtle architecture differences (x86-64 on arm64, arm64 on x86_64, arm32 on x86-compat, or any of the above on riscv or loongarch).

Really interesting, Since it's more related to emulation needs (my patch has another focus due to the fact that A64 can execute A32 natively), I'll take a look at this separately.

A64 mode (unlike some other architectures, notably mips64) cannot execute A32 or T32 instructions without a mode switch, the three are entirely incompatible on the binary level.

Many ARMv8-CPUs support both Aarch64 mode and Aarch32 (A32/T32), but a lot of the newer ones (e.g. Apple M1/M2, Cortex-R82 or Cortex-A715) only do Aarch64 and need user-space emulation to run 32-bit binaries.

Arnd

#19 lore raw
In reply to Will Deacon
Quoted 13 lines · Will Deacon

Hi,

Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

I proposed something similar in the past:

Quoted 7 lines

https://lkml.kernel.org/linux-fsdevel/[email protected]/

bu the conclusion there (see the reply from Kees) was that it was better to either use existing seccomp mechanisms or add something to control which binfmts can be loaded.

Will

I see. Seccomp sounds like a really good idea, since just blocking the compat binfmt would not avoid the call to 32-bit syscalls per se: it's true that ARM64 enforce the transition from A64 to A32 only on exception return and PSTATE.nRW flag can change only from EL1, maybe though some exploitation may arise in the future to do just that (I'm not aware of any or come up with a proof off the top of my head, but I can't exclude it either). So, assuming by absurd a switch to A32 is feasible, the further step of embedding A32 instruction in a A64 ELF executable is a breeze. Hence blocking the syscall (and not only the binfmt loading) could prove necessary. I know all of this is higly speculative right now, maybe it's worth thinking nonetheless.

Andrea

reviewer
#20 lore raw
In reply to Andrea della Porta
Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

Can you elaborate on *why* people want such a policy?

Quoted 18 lines

*** Notes about syscall management ***
VBAR_EL1 register, which holds the exception table address, is setup very early in the boot process, before parse_early_param(). This means that it's not possible to access boot parameter before setting the register. Also, setting the aforementioned register for secondary cpus is done later in the boot flow. Several ways to work around this has been considered, among which:

* resetting VBAR_EL1 to point to one of two vector tables (the former with 32-bit exceptions handler enabled and the latter pointing to unhandled stub, just as if CONFIG_COMPAT is enabled) depending on the proposed boot parameter. This has the disadvantage to produce a somewhat messy patchset involving several lines, has higher cognitive load since there are at least three places where the register is getting changed (not near to each other), and have implications on other code segments (namely kpti, kvm and vdso), requiring special care.

Quoted 7 lines

* patching the vector table contents once the early param is available. This has most of the implications of the previous option (except maybe not impacting other code segments), plus it sounds a little 'hackish'.

The chosen approach involves conditional executing 32-bit syscalls depending on the parameter value.

Why does the compat syscall path need to do anything?

On arm64 it's not possible to issue compat syscalls from a native 64-bit task. If you prevent the loading of AArch32 binaries, none of the compat syscalls will be reachable at all.

That's the proper way to implement this, and we already have logic for that as part of the mismatched AArch32 support.

Quoted 2 lines

This of course results in a little performance loss, but has the following advantages:

A performance loss for what relative to what?

How much of a performance loss?

Mark.

Quoted 32 lines

* all the cons from previously explained alternatives are solved
* users of 32-bit apps on 64-bit kernel are already suffering from performance losses due to 32-bit apps not fully leveraging the 64-bit processor, so they are already aware of this
* users of 32-bit apps on 64-bit kernel are believed
to be a minority and most of the time there are sources available to be recompiled for 64-bit as a workaround for better performance

It worth mentioning that users of 64-bit apps are, of course, unaffected.

Based on the work from Nikolay Borisov, see:

Andrea della Porta (4):
arm64: Introduce aarch32_enabled() arm64/process: Make loading of 32bit processes depend on

    aarch32_enabled()
  arm64/entry-common: Make Aarch32 syscalls' availability depend on
    aarch32_enabled()

arm64: Make Aarch32 emulation boot time configurable

 .../admin-guide/kernel-parameters.txt         |  7 ++++
 arch/arm64/Kconfig                            |  9 +++++
 arch/arm64/include/asm/compat.h               | 12 +++++++
 arch/arm64/kernel/entry-common.c              | 33 +++++++++++++++++--
 arch/arm64/kernel/process.c                   |  2 +-
 5 files changed, 59 insertions(+), 4 deletions(-)
Signature · 1 line
2.35.3
#21 lore raw
Quoted 11 lines · Mark Rutland
Quoted 7 lines · Andrea della Porta

Aarch32 compatibility mode is enabled at compile time through CONFIG_COMPAT Kconfig option. This patchset lets 32-bit support (for both processes and syscalls) be enabled at boot time using a kernel parameter. Also, it provides a mean for distributions to set their own default without sacrificing compatibility support, that is users can override default behaviour through the kernel parameter.

Can you elaborate on *why* people want such a policy?

Formerly, the reason was to reduce kernel attack surface by excluding compat syscall, wherever applicable. Much less important but still a point, I would also say this could be a good chance to get rid of somewhat old and stale 32-bit libraries and programs, but this is of course debatable.

Quoted 23 lines
Quoted 23 lines

*** Notes about syscall management ***
VBAR_EL1 register, which holds the exception table address, is setup very early in the boot process, before parse_early_param(). This means that it's not possible to access boot parameter before setting the register. Also, setting the aforementioned register for secondary cpus is done later in the boot flow. Several ways to work around this has been considered, among which:

* resetting VBAR_EL1 to point to one of two vector tables (the former with 32-bit exceptions handler enabled and the latter pointing to unhandled stub, just as if CONFIG_COMPAT is enabled) depending on the proposed boot parameter. This has the disadvantage to produce a somewhat messy patchset involving several lines, has higher cognitive load since there are at least three places where the register is getting changed (not near to each other), and have implications on other code segments (namely kpti, kvm and vdso), requiring special care.

* patching the vector table contents once the early param is available. This has most of the implications of the previous option (except maybe not impacting other code segments), plus it sounds a little 'hackish'.

Quoted 4 lines
Quoted 2 lines

The chosen approach involves conditional executing 32-bit syscalls depending on the parameter value.

Why does the compat syscall path need to do anything?

I probably didn't catch your point here, compat syscall does not need to do anything and they do not (just like they works right now with CONFIG_COMPAT alone), except for the conditional instruction that excludes them at runtime. Of course this conditional *is* doing something and somewhat redundant if compat is disabled, but in this scenario I think it's unavoidable.

Quoted 5 lines

On arm64 it's not possible to issue compat syscalls from a native 64-bit task. If you prevent the loading of AArch32 binaries, none of the compat syscalls will be reachable at all.

Quoted 7 lines

That's the proper way to implement this, and we already have logic for that as part of the mismatched AArch32 support.

Quoted 2 lines

This of course results in a little performance loss, but has the following advantages:

A performance loss for what relative to what?

of a compat syscall as it is now enabling CONFIG_COMPAT vs the patched syscall handlers that need a further conditional instruction to check whether comapt is enabled or not.

Quoted 2 lines

How much of a performance loss?

I did not take measurement yet since it was just a qualitative consideration more than a quantitative one, also considering that chances are that it would affect just very little population. The conditional instruction time taken to execute is reasonably near to negligible if compared to any syscall execution.