2
votes

Linux supports running 32-bit application, as long as

  1. kernel enables CONFIG_COMPAT
  2. the hardware supports the AArch32

I assume that 32-bit application must run in arm AArch32 execution state and if the environment has 32-bit application and 64-bit application.

32-bit application process -> arm state is AArch32

64-bit application process and kernel -> arm state is AArch64

Is it correct?

If so,
how does the Linux handle the AArch32 and AArch64 switch?
Does the kernel know the running process is 32-bit or 64-bit?

1
Due to special trampolines to (un)aligned addresses. That said, few LSBs responsible to switch modes.0andriy
@0andriy No. You're thinking of the ARM/Thumb interworking, not the AArch64/AArch32 interprocessing.EOF
@EOF I will wait for your answer, meanwhile according to community.arm.com/developer/ip-products/processors/f/… any exception can do that, so, my comment might be correct. But I don't know that area in the kernel.0andriy
@0andriy I'm not writing an answer. I've only had a short look at the ARM64 ARM. Seems every EL (except EL3) can be configured to be either AArch32 or AArch64, depending on the relevant PSTATE register for the EL. Linux sets the appropriate bit in the PSTATE register corresponding to userspace, so once the kernel returns (exception return), the userspace executes AArch32.EOF

1 Answers

3
votes

The link https://community.arm.com/developer/ip-products/processors/f/cortex-a-forum/6706/in-aarch32-state-what-is-the-mechanism-to-switch-to-aarch64-in-software posted in comments by 0andriy (kernel developer) has explanation of switching between AArch32 user-space process and AArch64 linux kernel by Martin Weidmann. The 32->64 mode switch is done at exceptions; and 64->32 switch is done at exception return.

If you are currently running one of the 32-bit apps and you take an exception (e.g. IRQ, SVC from a system call, abort from a page fault,....) you enter the 64-bit OS. So a AArch32 --> AArch64 transition. When the OS performs an exception return back into the app, that's an AArch64-->AArch32 transition. ... Any exception type in AArch32 state could potentially lead to Execution state changing to AArch64. ... For exceptions returns the reverse is true. An exception return in AArch64 might cause execution state to change to AArch32.
For both exceptions and exception returns, a change of Execution state can only occur if there is also a change in EL. That is an exception from EL0 to EL1 could lead to a change in Execution state. But an exception from EL1 to EL1 could not.

The https://community.arm.com/developer/ip-products/processors/f/cortex-a-forum/6706/in-aarch32-state-what-is-the-mechanism-to-switch-to-aarch64-in-software thread has some more details. Or there is much simpler explanation in https://medium.com/@om.nara/aarch64-exception-levels-60d3a74280e6 in "Moving between AArch32 and AArch64"

On taking an exception, if the Exception level changes, the Execution state can: Remain unchanged, OR Change from AArch32 to AArch64.

On returning from an exception, if the Exception level changes, the Execution state can: Remain unchanged, OR Change from AArch64 to AArch32.

Same in https://events.static.linuxfound.org/images/stories/pdf/lcna_co2012_marinas.pdf presentation (slide 5) or in https://developer.arm.com/architectures/learn-the-architecture/exception-model/execution-and-security-states or in https://www.realworldtech.com/arm64/2/:

AArch64 Exception Model

  • Privilege levels: EL3 – highest, EL0 – lowest
  • Transition to higher levels via exceptions

  • Register width cannot be higher in lower levels

    • E.g. no 64-bit EL0 with 32-bit EL1
  • Transition between AArch32 and AArch64 via exceptions
    • AArch32/AArch64 interworking not possible

Now for your questions:

how does the Linux handle the AArch32 and AArch64 switch?

By using hardware capability of exception handling (and return) with EL0/EL1 switch with different PSTATE values.

Does the kernel know the running process is 32-bit or 64-bit?

Yes, there is the check in kernel for 32-bit processes ("tasks") on 64-bit kernels (compat syscalls): arch/arm64/kernel/syscall.c

static long do_ni_syscall(struct pt_regs *regs, int scno)
{
#ifdef CONFIG_COMPAT
    long ret;
    if (is_compat_task()) {
        ret = compat_arm_syscall(regs, scno);
        if (ret != -ENOSYS)
            return ret;
    }
#endif

    return sys_ni_syscall();
}

Test is defined in include/asm/compat.h and arch/arm64/include/asm/thread_info.h as

#define TIF_32BIT       22  /* 32bit process */
static inline int is_compat_task(void)
{
    return test_thread_flag(TIF_32BIT);
}

TIF_32BIT is set by in fs/compat_binfmt_elf.c on 32-bit elf loading with several personality macro:

https://elixir.bootlin.com/linux/v4.19.107/source/arch/arm64/include/asm/elf.h#L208

/*
 * Unlike the native SET_PERSONALITY macro, the compat version maintains
 * READ_IMPLIES_EXEC across an execve() since this is the behaviour on
 * arch/arm/.
 */
#define COMPAT_SET_PERSONALITY(ex)                  \
({                                  \
    set_thread_flag(TIF_32BIT);                 \
 })

https://elixir.bootlin.com/linux/v4.19.107/source/fs/compat_binfmt_elf.c#L104

 #define    SET_PERSONALITY     COMPAT_SET_PERSONALITY

https://elixir.bootlin.com/linux/v4.19.107/source/fs/binfmt_elf.c#L690

#define SET_PERSONALITY2(ex, state) \
    SET_PERSONALITY(ex)
 static int load_elf_binary(struct linux_binprm *bprm)
    SET_PERSONALITY2(loc->elf_ex, &arch_state);