Re: [PATCH v2 9/9] arm64: armv8_deprecated: rework deprected instruction handling

Ruan Jinjie
2023-02-07T07:02:24Z

On 2023/2/6 17:13, Mark Rutland wrote:
> On Mon, Feb 06, 2023 at 10:03:17AM +0800, Ruan Jinjie wrote:
>> On 2023/2/4 1:27, Mark Rutland wrote:
>>> On Fri, Feb 03, 2023 at 06:08:08PM +0800, Ruan Jinjie wrote:
>>>> Hi, Mark, I want to merge this patch to linux-5.10.y to solve a list_add
>>>> double problem. However, I can not trigger the emulation of these
>>>> deprecated instructions in qemu with userspace program as below, but
>>>> cause IABT and DABT,
>>>
>>> Well that certainly sounds wrong!
>>>
>>> When you say "in qemu", so you mean in a KVM VM, a TCG VM, or usermode
>>> emulation?
>>>
>>> Can you tell me exactly how you're invoking qemu?
>>
>> Sorry, the information given was incomplete.I invoke qemu 6.1.1 with
>> following commands, which is a TCG VM:
>>
>> qemu-system-aarch64
>> -M virt \
>> -cpu cortex-a57 \
>> -smp 4 \
>> -m 4096 \
>> -nographic \
>> -kernel linux-stable/arch/arm64/boot/Image \
>> -drive if=none,file=rootfs.img,format=raw,id=hd0 \
>> -device virtio-net-device,netdev=net0 \
>> -netdev user,id=net0,host=10.0.2.1,hostfwd=tcp::5555-:22 \
>> -device virtio-blk-device,drive=hd0 \
>> -append "root=/dev/vda console=ttyAMA0 rootfstype=ext4 init=/linuxrc rw
>> oops=panic nmi_watchdog=panic panic=1"
>>
>>>
>>> Can you give an example of the IABT and DABT you see?
>>
>> Because the emulation of cp15 deprecated instructions is not triggered,I
>> add printk message in el0_sync_compat_handler() function
>
> Ah! So you mean that those are the *only* exceptions you see, not that those
> are *unexpected*. AFAICT, those are jsut the test code and data being faulted
> in as usual, and are not related to the emulation.
>
> I assume the test completes successfully, and returns 0?
>
> I had a play with this locally (with QEMU 6.2.94), and I see that the
> deprecated instructions work even when neither the emulation nor HW support is
> enabled (in the relevant SCTLR_ELx bits), and I think that what's happening
> here is the QEMU isn't correctly emulating the architecture:
>
> * QEMU seems to ignore SCTLR_ELx.CP15BEN, and always imeplements those rather
> than making those UNDEFINED when SCTLR_ELx.CP15BEN == 0b0.
>
> * QEMU seems to ignore SCTLR_ELx.SED, and always implements SETEND rather than
> making that UNDEFINED when SCTLR_ELx.SED == 0b1.
>
> * QEMU seems to implement SWP*, even though those instructions don't exist in
> ARMv8-A and should always be UNDEFINED.

I agree with you! It is common that the qemu implementation is
inconsistent with the architecture manual specification in some details.

>
>> to check the EC code When the following user-space code is executed.The print
>> is as follows:
>>
>> /home # ./cp15_barrier_test
>> [ 72.469366] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.470477] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x24
>> [ 72.471342] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x24
>> [ 72.472341] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.474033] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x24
>> [ 72.475145] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.476255] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.477861] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.478427] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.479111] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x24
>> [ 72.479919] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.480467] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.481616] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.483386] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.484187] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.485215] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.485882] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x24
>> [ 72.486628] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>> [ 72.487495] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x20
>> [ 72.488548] el0_sync_compat_handler, ESR_ELx_EC(esr)= 0x11
>>
>> I went through "ARM® Architecture Reference Manual ARMv8, for ARMv8-A
>> architecture profile" and check the header arch/arm64/include/asm/esr.h,
>> find it's EC codes for SVC32, IABT and DABT.
>>
>> #define ESR_ELx_EC_SVC32 (0x11)
>> #define ESR_ELx_EC_IABT_LOW (0x20)
>> #define ESR_ELx_EC_DABT_LOW (0x24)
>>
>> I also learned from the manual that the SCTLR_EL1.CP15BEN is critical,
>> which control the behavior of above deprecating memory barrier
>> instructions which can be set by the 'echo 2 >
>> /proc/sys/abi/cp15_barrier' command.However, the result is the same
>> whether it is set or not.
>
> As above, I think this is a QEMU bug, with QEMU erroneosuly implementing the
> instructions when they should be UNDEFINED. The aborts are unrelated, and are
> just the usual faulting in of code and data pages.
>
> So I'd recommend reporting tha to QEMU folk, and in the mean time testing on
> real HW rather than QEMU in TCG mode. When I wrote these patches I tested on a
> Raspberry Pi 4, under a KVM virtual machine.

Thanks for your advice. I'll test it on real HW.

>
> Thanks,
> Mark.
>
>>
>> I think the code went into the wrong code flow because of the wrong EC
>> code, but I don't know why the wrong EC value was passed in.
>>
>>>
>>>> so that I can not verify the correctness of the
>>>> patch.Can you give me some help to test the emulation of these
>>>> deprecated instructions?
>>>>
>>>> #include<stdio.h>
>>>>
>>>> #define isb(x) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c5, 4" \
>>>> : : "r" (0) : "memory")
>>>> #define dsb(x) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" \
>>>> : : "r" (0) : "memory")
>>>> #define dmb(x) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" \
>>>> : : "r" (0) : "memory")
>>>
>>> FWIW, these encodings look correc to me.
>>>
>>> Thanks,
>>> Mark.
>>>
>>>>
>>>> int main(){
>>>> isb();
>>>> dsb();
>>>> dmb();
>>>> return 0;
>>>> }
>>>>
>>>>>
>>>>> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
>>>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>>>>> Cc: James Morse <james.morse@arm.com>
>>>>> Cc: Joey Gouly <joey.gouly@arm.com>
>>>>> Cc: Peter Zijlstra <peterz@infradead.org>
>>>>> Cc: Will Deacon <will@kernel.org>
>>>>> Signed-off-by: Mark Rutland <mark.rutland@arm.com>
>>>>> ---
>>>>> arch/arm64/include/asm/traps.h | 19 +-
>>>>> arch/arm64/kernel/armv8_deprecated.c | 282 +++++++++++++--------------
>>>>> arch/arm64/kernel/traps.c | 40 +---
>>>>> 3 files changed, 149 insertions(+), 192 deletions(-)
>>>>>
>>>>> diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
>>>>> index 6e5826470bea..1f361e2da516 100644
>>>>> --- a/arch/arm64/include/asm/traps.h
>>>>> +++ b/arch/arm64/include/asm/traps.h
>>>>> @@ -13,17 +13,16 @@
>>>>>
>>>>> struct pt_regs;
>>>>>
>>>>> -struct undef_hook {
>>>>> - struct list_head node;
>>>>> - u32 instr_mask;
>>>>> - u32 instr_val;
>>>>> - u64 pstate_mask;
>>>>> - u64 pstate_val;
>>>>> - int (*fn)(struct pt_regs *regs, u32 instr);
>>>>> -};
>>>>> +#ifdef CONFIG_ARMV8_DEPRECATED
>>>>> +bool try_emulate_armv8_deprecated(struct pt_regs *regs, u32 insn);
>>>>> +#else
>>>>> +static inline bool
>>>>> +try_emulate_armv8_deprecated(struct pt_regs *regs, u32 insn)
>>>>> +{
>>>>> + return false;
>>>>> +}
>>>>> +#endif /* CONFIG_ARMV8_DEPRECATED */
>>>>>
>>>>> -void register_undef_hook(struct undef_hook *hook);
>>>>> -void unregister_undef_hook(struct undef_hook *hook);
>>>>> void force_signal_inject(int signal, int code, unsigned long address, unsigned long err);
>>>>> void arm64_notify_segfault(unsigned long addr);
>>>>> void arm64_force_sig_fault(int signo, int code, unsigned long far, const char *str);
>>>>> diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
>>>>> index 7f2ce49dbf97..ed0788cf6bbb 100644
>>>>> --- a/arch/arm64/kernel/armv8_deprecated.c
>>>>> +++ b/arch/arm64/kernel/armv8_deprecated.c
>>>>> @@ -38,17 +38,24 @@ enum insn_emulation_mode {
>>>>> enum legacy_insn_status {
>>>>> INSN_DEPRECATED,
>>>>> INSN_OBSOLETE,
>>>>> + INSN_UNAVAILABLE,
>>>>> };
>>>>>
>>>>> struct insn_emulation {
>>>>> const char *name;
>>>>> - struct list_head node;
>>>>> enum legacy_insn_status status;
>>>>> - struct undef_hook *hooks;
>>>>> + bool (*try_emulate)(struct pt_regs *regs,
>>>>> + u32 insn);
>>>>> int (*set_hw_mode)(bool enable);
>>>>> +
>>>>> int current_mode;
>>>>> int min;
>>>>> int max;
>>>>> +
>>>>> + /*
>>>>> + * sysctl for this emulation + a sentinal entry.
>>>>> + */
>>>>> + struct ctl_table sysctl[2];
>>>>> };
>>>>>
>>>>> #define ARM_OPCODE_CONDTEST_FAIL 0
>>>>> @@ -70,6 +77,7 @@ static unsigned int aarch32_check_condition(u32 opcode, u32 psr)
>>>>> return ARM_OPCODE_CONDTEST_UNCOND;
>>>>> }
>>>>>
>>>>> +#ifdef CONFIG_SWP_EMULATION
>>>>> /*
>>>>> * Implement emulation of the SWP/SWPB instructions using load-exclusive and
>>>>> * store-exclusive.
>>>>> @@ -222,28 +230,27 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
>>>>> return 0;
>>>>> }
>>>>>
>>>>> -/*
>>>>> - * Only emulate SWP/SWPB executed in ARM state/User mode.
>>>>> - * The kernel must be SWP free and SWP{B} does not exist in Thumb.
>>>>> - */
>>>>> -static struct undef_hook swp_hooks[] = {
>>>>> - {
>>>>> - .instr_mask = 0x0fb00ff0,
>>>>> - .instr_val = 0x01000090,
>>>>> - .pstate_mask = PSR_AA32_MODE_MASK,
>>>>> - .pstate_val = PSR_AA32_MODE_USR,
>>>>> - .fn = swp_handler
>>>>> - },
>>>>> - { }
>>>>> -};
>>>>> +static bool try_emulate_swp(struct pt_regs *regs, u32 insn)
>>>>> +{
>>>>> + /* SWP{B} only exists in ARM state and does not exist in Thumb */
>>>>> + if (!compat_user_mode(regs) || compat_thumb_mode(regs))
>>>>> + return false;
>>>>> +
>>>>> + if ((insn & 0x0fb00ff0) != 0x01000090)
>>>>> + return false;
>>>>> +
>>>>> + return swp_handler(regs, insn) == 0;
>>>>> +}
>>>>>
>>>>> static struct insn_emulation insn_swp = {
>>>>> .name = "swp",
>>>>> .status = INSN_OBSOLETE,
>>>>> - .hooks = swp_hooks,
>>>>> + .try_emulate = try_emulate_swp,
>>>>> .set_hw_mode = NULL,
>>>>> };
>>>>> +#endif /* CONFIG_SWP_EMULATION */
>>>>>
>>>>> +#ifdef CONFIG_CP15_BARRIER_EMULATION
>>>>> static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
>>>>> {
>>>>> perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
>>>>> @@ -306,31 +313,29 @@ static int cp15_barrier_set_hw_mode(bool enable)
>>>>> return 0;
>>>>> }
>>>>>
>>>>> -static struct undef_hook cp15_barrier_hooks[] = {
>>>>> - {
>>>>> - .instr_mask = 0x0fff0fdf,
>>>>> - .instr_val = 0x0e070f9a,
>>>>> - .pstate_mask = PSR_AA32_MODE_MASK,
>>>>> - .pstate_val = PSR_AA32_MODE_USR,
>>>>> - .fn = cp15barrier_handler,
>>>>> - },
>>>>> - {
>>>>> - .instr_mask = 0x0fff0fff,
>>>>> - .instr_val = 0x0e070f95,
>>>>> - .pstate_mask = PSR_AA32_MODE_MASK,
>>>>> - .pstate_val = PSR_AA32_MODE_USR,
>>>>> - .fn = cp15barrier_handler,
>>>>> - },
>>>>> - { }
>>>>> -};
>>>>> +static bool try_emulate_cp15_barrier(struct pt_regs *regs, u32 insn)
>>>>> +{
>>>>> + if (!compat_user_mode(regs) || compat_thumb_mode(regs))
>>>>> + return false;
>>>>> +
>>>>> + if ((insn & 0x0fff0fdf) == 0x0e070f9a)
>>>>> + return cp15barrier_handler(regs, insn) == 0;
>>>>> +
>>>>> + if ((insn & 0x0fff0fff) == 0x0e070f95)
>>>>> + return cp15barrier_handler(regs, insn) == 0;
>>>>> +
>>>>> + return false;
>>>>> +}
>>>>>
>>>>> static struct insn_emulation insn_cp15_barrier = {
>>>>> .name = "cp15_barrier",
>>>>> .status = INSN_DEPRECATED,
>>>>> - .hooks = cp15_barrier_hooks,
>>>>> + .try_emulate = try_emulate_cp15_barrier,
>>>>> .set_hw_mode = cp15_barrier_set_hw_mode,
>>>>> };
>>>>> +#endif /* CONFIG_CP15_BARRIER_EMULATION */
>>>>>
>>>>> +#ifdef CONFIG_SETEND_EMULATION
>>>>> static int setend_set_hw_mode(bool enable)
>>>>> {
>>>>> if (!cpu_supports_mixed_endian_el0())
>>>>> @@ -378,61 +383,41 @@ static int t16_setend_handler(struct pt_regs *regs, u32 instr)
>>>>> return rc;
>>>>> }
>>>>>
>>>>> -static struct undef_hook setend_hooks[] = {
>>>>> - {
>>>>> - .instr_mask = 0xfffffdff,
>>>>> - .instr_val = 0xf1010000,
>>>>> - .pstate_mask = PSR_AA32_MODE_MASK,
>>>>> - .pstate_val = PSR_AA32_MODE_USR,
>>>>> - .fn = a32_setend_handler,
>>>>> - },
>>>>> - {
>>>>> - /* Thumb mode */
>>>>> - .instr_mask = 0xfffffff7,
>>>>> - .instr_val = 0x0000b650,
>>>>> - .pstate_mask = (PSR_AA32_T_BIT | PSR_AA32_MODE_MASK),
>>>>> - .pstate_val = (PSR_AA32_T_BIT | PSR_AA32_MODE_USR),
>>>>> - .fn = t16_setend_handler,
>>>>> - },
>>>>> - {}
>>>>> -};
>>>>> +static bool try_emulate_setend(struct pt_regs *regs, u32 insn)
>>>>> +{
>>>>> + if (compat_thumb_mode(regs) &&
>>>>> + (insn & 0xfffffff7) == 0x0000b650)
>>>>> + return t16_setend_handler(regs, insn) == 0;
>>>>> +
>>>>> + if (compat_user_mode(regs) &&
>>>>> + (insn & 0xfffffdff) == 0xf1010000)
>>>>> + return a32_setend_handler(regs, insn) == 0;
>>>>> +
>>>>> + return false;
>>>>> +}
>>>>>
>>>>> static struct insn_emulation insn_setend = {
>>>>> .name = "setend",
>>>>> .status = INSN_DEPRECATED,
>>>>> - .hooks = setend_hooks,
>>>>> + .try_emulate = try_emulate_setend,
>>>>> .set_hw_mode = setend_set_hw_mode,
>>>>> };
>>>>> +#endif /* CONFIG_SETEND_EMULATION */
>>>>> +
>>>>> +static struct insn_emulation *insn_emulations[] = {
>>>>> +#ifdef CONFIG_SWP_EMULATION
>>>>> + &insn_swp,
>>>>> +#endif
>>>>> +#ifdef CONFIG_CP15_BARRIER_EMULATION
>>>>> + &insn_cp15_barrier,
>>>>> +#endif
>>>>> +#ifdef CONFIG_SETEND_EMULATION
>>>>> + &insn_setend,
>>>>> +#endif
>>>>> +};
>>>>>
>>>>> -static LIST_HEAD(insn_emulation);
>>>>> -static int nr_insn_emulated __initdata;
>>>>> -static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
>>>>> static DEFINE_MUTEX(insn_emulation_mutex);
>>>>>
>>>>> -static void register_emulation_hooks(struct insn_emulation *insn)
>>>>> -{
>>>>> - struct undef_hook *hook;
>>>>> -
>>>>> - BUG_ON(!insn->hooks);
>>>>> -
>>>>> - for (hook = insn->hooks; hook->instr_mask; hook++)
>>>>> - register_undef_hook(hook);
>>>>> -
>>>>> - pr_notice("Registered %s emulation handler\n", insn->name);
>>>>> -}
>>>>> -
>>>>> -static void remove_emulation_hooks(struct insn_emulation *insn)
>>>>> -{
>>>>> - struct undef_hook *hook;
>>>>> -
>>>>> - BUG_ON(!insn->hooks);
>>>>> -
>>>>> - for (hook = insn->hooks; hook->instr_mask; hook++)
>>>>> - unregister_undef_hook(hook);
>>>>> -
>>>>> - pr_notice("Removed %s emulation handler\n", insn->name);
>>>>> -}
>>>>> -
>>>>> static void enable_insn_hw_mode(void *data)
>>>>> {
>>>>> struct insn_emulation *insn = (struct insn_emulation *)data;
>>>>> @@ -469,18 +454,24 @@ static int run_all_insn_set_hw_mode(unsigned int cpu)
>>>>> {
>>>>> int rc = 0;
>>>>> unsigned long flags;
>>>>> - struct insn_emulation *insn;
>>>>>
>>>>> - raw_spin_lock_irqsave(&insn_emulation_lock, flags);
>>>>> - list_for_each_entry(insn, &insn_emulation, node) {
>>>>> - bool enable = (insn->current_mode == INSN_HW);
>>>>> + /*
>>>>> + * Disable IRQs to serialize against an IPI from
>>>>> + * run_all_cpu_set_hw_mode(), ensuring the HW is programmed to the most
>>>>> + * recent enablement state if the two race with one another.
>>>>> + */
>>>>> + local_irq_save(flags);
>>>>> + for (int i = 0; i < ARRAY_SIZE(insn_emulations); i++) {
>>>>> + struct insn_emulation *insn = insn_emulations[i];
>>>>> + bool enable = READ_ONCE(insn->current_mode) == INSN_HW;
>>>>> if (insn->set_hw_mode && insn->set_hw_mode(enable)) {
>>>>> pr_warn("CPU[%u] cannot support the emulation of %s",
>>>>> cpu, insn->name);
>>>>> rc = -EINVAL;
>>>>> }
>>>>> }
>>>>> - raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
>>>>> + local_irq_restore(flags);
>>>>> +
>>>>> return rc;
>>>>> }
>>>>>
>>>>> @@ -493,7 +484,6 @@ static int update_insn_emulation_mode(struct insn_emulation *insn,
>>>>> case INSN_UNDEF: /* Nothing to be done */
>>>>> break;
>>>>> case INSN_EMULATE:
>>>>> - remove_emulation_hooks(insn);
>>>>> break;
>>>>> case INSN_HW:
>>>>> if (!run_all_cpu_set_hw_mode(insn, false))
>>>>> @@ -505,7 +495,6 @@ static int update_insn_emulation_mode(struct insn_emulation *insn,
>>>>> case INSN_UNDEF:
>>>>> break;
>>>>> case INSN_EMULATE:
>>>>> - register_emulation_hooks(insn);
>>>>> break;
>>>>> case INSN_HW:
>>>>> ret = run_all_cpu_set_hw_mode(insn, true);
>>>>> @@ -517,34 +506,6 @@ static int update_insn_emulation_mode(struct insn_emulation *insn,
>>>>> return ret;
>>>>> }
>>>>>
>>>>> -static void __init register_insn_emulation(struct insn_emulation *insn)
>>>>> -{
>>>>> - unsigned long flags;
>>>>> -
>>>>> - insn->min = INSN_UNDEF;
>>>>> -
>>>>> - switch (insn->status) {
>>>>> - case INSN_DEPRECATED:
>>>>> - insn->current_mode = INSN_EMULATE;
>>>>> - /* Disable the HW mode if it was turned on at early boot time */
>>>>> - run_all_cpu_set_hw_mode(insn, false);
>>>>> - insn->max = INSN_HW;
>>>>> - break;
>>>>> - case INSN_OBSOLETE:
>>>>> - insn->current_mode = INSN_UNDEF;
>>>>> - insn->max = INSN_EMULATE;
>>>>> - break;
>>>>> - }
>>>>> -
>>>>> - raw_spin_lock_irqsave(&insn_emulation_lock, flags);
>>>>> - list_add(&insn->node, &insn_emulation);
>>>>> - nr_insn_emulated++;
>>>>> - raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
>>>>> -
>>>>> - /* Register any handlers if required */
>>>>> - update_insn_emulation_mode(insn, INSN_UNDEF);
>>>>> -}
>>>>> -
>>>>> static int emulation_proc_handler(struct ctl_table *table, int write,
>>>>> void *buffer, size_t *lenp,
>>>>> loff_t *ppos)
>>>>> @@ -562,7 +523,7 @@ static int emulation_proc_handler(struct ctl_table *table, int write,
>>>>> ret = update_insn_emulation_mode(insn, prev_mode);
>>>>> if (ret) {
>>>>> /* Mode change failed, revert to previous mode. */
>>>>> - insn->current_mode = prev_mode;
>>>>> + WRITE_ONCE(insn->current_mode, prev_mode);
>>>>> update_insn_emulation_mode(insn, INSN_UNDEF);
>>>>> }
>>>>> ret:
>>>>> @@ -570,21 +531,34 @@ static int emulation_proc_handler(struct ctl_table *table, int write,
>>>>> return ret;
>>>>> }
>>>>>
>>>>> -static void __init register_insn_emulation_sysctl(void)
>>>>> +static void __init register_insn_emulation(struct insn_emulation *insn)
>>>>> {
>>>>> - unsigned long flags;
>>>>> - int i = 0;
>>>>> - struct insn_emulation *insn;
>>>>> - struct ctl_table *insns_sysctl, *sysctl;
>>>>> + struct ctl_table *sysctl;
>>>>>
>>>>> - insns_sysctl = kcalloc(nr_insn_emulated + 1, sizeof(*sysctl),
>>>>> - GFP_KERNEL);
>>>>> - if (!insns_sysctl)
>>>>> - return;
>>>>> + insn->min = INSN_UNDEF;
>>>>>
>>>>> - raw_spin_lock_irqsave(&insn_emulation_lock, flags);
>>>>> - list_for_each_entry(insn, &insn_emulation, node) {
>>>>> - sysctl = &insns_sysctl[i];
>>>>> + switch (insn->status) {
>>>>> + case INSN_DEPRECATED:
>>>>> + insn->current_mode = INSN_EMULATE;
>>>>> + /* Disable the HW mode if it was turned on at early boot time */
>>>>> + run_all_cpu_set_hw_mode(insn, false);
>>>>> + insn->max = INSN_HW;
>>>>> + break;
>>>>> + case INSN_OBSOLETE:
>>>>> + insn->current_mode = INSN_UNDEF;
>>>>> + insn->max = INSN_EMULATE;
>>>>> + break;
>>>>> + case INSN_UNAVAILABLE:
>>>>> + insn->current_mode = INSN_UNDEF;
>>>>> + insn->max = INSN_UNDEF;
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + /* Program the HW if required */
>>>>> + update_insn_emulation_mode(insn, INSN_UNDEF);
>>>>> +
>>>>> + if (insn->status != INSN_UNAVAILABLE) {
>>>>> + sysctl = &insn->sysctl[0];
>>>>>
>>>>> sysctl->mode = 0644;
>>>>> sysctl->maxlen = sizeof(int);
>>>>> @@ -594,11 +568,32 @@ static void __init register_insn_emulation_sysctl(void)
>>>>> sysctl->extra1 = &insn->min;
>>>>> sysctl->extra2 = &insn->max;
>>>>> sysctl->proc_handler = emulation_proc_handler;
>>>>> - i++;
>>>>> +
>>>>> + register_sysctl("abi", sysctl);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +bool try_emulate_armv8_deprecated(struct pt_regs *regs, u32 insn)
>>>>> +{
>>>>> + for (int i = 0; i < ARRAY_SIZE(insn_emulations); i++) {
>>>>> + struct insn_emulation *ie = insn_emulations[i];
>>>>> +
>>>>> + if (ie->status == INSN_UNAVAILABLE)
>>>>> + continue;
>>>>> +
>>>>> + /*
>>>>> + * A trap may race with the mode being changed
>>>>> + * INSN_EMULATE<->INSN_HW. Try to emulate the instruction to
>>>>> + * avoid a spurious UNDEF.
>>>>> + */
>>>>> + if (READ_ONCE(ie->current_mode) == INSN_UNDEF)
>>>>> + continue;
>>>>> +
>>>>> + if (ie->try_emulate(regs, insn))
>>>>> + return true;
>>>>> }
>>>>> - raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
>>>>>
>>>>> - register_sysctl("abi", insns_sysctl);
>>>>> + return false;
>>>>> }
>>>>>
>>>>> /*
>>>>> @@ -607,24 +602,25 @@ static void __init register_insn_emulation_sysctl(void)
>>>>> */
>>>>> static int __init armv8_deprecated_init(void)
>>>>> {
>>>>> - if (IS_ENABLED(CONFIG_SWP_EMULATION))
>>>>> - register_insn_emulation(&insn_swp);
>>>>> +#ifdef CONFIG_SETEND_EMULATION
>>>>> + if (!system_supports_mixed_endian_el0()) {
>>>>> + insn_setend.status = INSN_UNAVAILABLE;
>>>>> + pr_info("setend instruction emulation is not supported on this system\n");
>>>>> + }
>>>>>
>>>>> - if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))
>>>>> - register_insn_emulation(&insn_cp15_barrier);
>>>>> +#endif
>>>>> + for (int i = 0; i < ARRAY_SIZE(insn_emulations); i++) {
>>>>> + struct insn_emulation *ie = insn_emulations[i];
>>>>>
>>>>> - if (IS_ENABLED(CONFIG_SETEND_EMULATION)) {
>>>>> - if (system_supports_mixed_endian_el0())
>>>>> - register_insn_emulation(&insn_setend);
>>>>> - else
>>>>> - pr_info("setend instruction emulation is not supported on this system\n");
>>>>> + if (ie->status == INSN_UNAVAILABLE)
>>>>> + continue;
>>>>> +
>>>>> + register_insn_emulation(ie);
>>>>> }
>>>>>
>>>>> cpuhp_setup_state_nocalls(CPUHP_AP_ARM64_ISNDEP_STARTING,
>>>>> "arm64/isndep:starting",
>>>>> run_all_insn_set_hw_mode, NULL);
>>>>> - register_insn_emulation_sysctl();
>>>>> -
>>>>> return 0;
>>>>> }
>>>>>
>>>>> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
>>>>> index 96eaf1aaec12..4c0caa589e12 100644
>>>>> --- a/arch/arm64/kernel/traps.c
>>>>> +++ b/arch/arm64/kernel/traps.c
>>>>> @@ -373,27 +373,6 @@ void arm64_skip_faulting_instruction(struct pt_regs *regs, unsigned long size)
>>>>> regs->pstate &= ~PSR_BTYPE_MASK;
>>>>> }
>>>>>
>>>>> -static LIST_HEAD(undef_hook);
>>>>> -static DEFINE_RAW_SPINLOCK(undef_lock);
>>>>> -
>>>>> -void register_undef_hook(struct undef_hook *hook)
>>>>> -{
>>>>> - unsigned long flags;
>>>>> -
>>>>> - raw_spin_lock_irqsave(&undef_lock, flags);
>>>>> - list_add(&hook->node, &undef_hook);
>>>>> - raw_spin_unlock_irqrestore(&undef_lock, flags);
>>>>> -}
>>>>> -
>>>>> -void unregister_undef_hook(struct undef_hook *hook)
>>>>> -{
>>>>> - unsigned long flags;
>>>>> -
>>>>> - raw_spin_lock_irqsave(&undef_lock, flags);
>>>>> - list_del(&hook->node);
>>>>> - raw_spin_unlock_irqrestore(&undef_lock, flags);
>>>>> -}
>>>>> -
>>>>> static int user_insn_read(struct pt_regs *regs, u32 *insnp)
>>>>> {
>>>>> u32 instr;
>>>>> @@ -425,23 +404,6 @@ static int user_insn_read(struct pt_regs *regs, u32 *insnp)
>>>>> return 0;
>>>>> }
>>>>>
>>>>> -static int call_undef_hook(struct pt_regs *regs, u32 instr)
>>>>> -{
>>>>> - struct undef_hook *hook;
>>>>> - unsigned long flags;
>>>>> - int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
>>>>> -
>>>>> - raw_spin_lock_irqsave(&undef_lock, flags);
>>>>> - list_for_each_entry(hook, &undef_hook, node)
>>>>> - if ((instr & hook->instr_mask) == hook->instr_val &&
>>>>> - (regs->pstate & hook->pstate_mask) == hook->pstate_val)
>>>>> - fn = hook->fn;
>>>>> -
>>>>> - raw_spin_unlock_irqrestore(&undef_lock, flags);
>>>>> -
>>>>> - return fn ? fn(regs, instr) : 1;
>>>>> -}
>>>>> -
>>>>> void force_signal_inject(int signal, int code, unsigned long address, unsigned long err)
>>>>> {
>>>>> const char *desc;
>>>>> @@ -502,7 +464,7 @@ void do_el0_undef(struct pt_regs *regs, unsigned long esr)
>>>>> if (try_emulate_mrs(regs, insn))
>>>>> return;
>>>>>
>>>>> - if (call_undef_hook(regs, insn) == 0)
>>>>> + if (try_emulate_armv8_deprecated(regs, insn))
>>>>> return;
>>>>>
>>>>> out_err:
>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> linux-arm-kernel@lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>