.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/arch/arm/vfp/vfpmodule.c |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2004 ARM Limited. |
---|
5 | 6 | * Written by Deep Blue Solutions Limited. |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | 7 | */ |
---|
11 | 8 | #include <linux/types.h> |
---|
12 | 9 | #include <linux/cpu.h> |
---|
.. | .. |
---|
26 | 23 | #include <asm/cputype.h> |
---|
27 | 24 | #include <asm/system_info.h> |
---|
28 | 25 | #include <asm/thread_notify.h> |
---|
| 26 | +#include <asm/traps.h> |
---|
29 | 27 | #include <asm/vfp.h> |
---|
30 | 28 | |
---|
31 | 29 | #include "vfpinstr.h" |
---|
.. | .. |
---|
34 | 32 | /* |
---|
35 | 33 | * Our undef handlers (in entry.S) |
---|
36 | 34 | */ |
---|
37 | | -asmlinkage void vfp_testing_entry(void); |
---|
38 | 35 | asmlinkage void vfp_support_entry(void); |
---|
39 | 36 | asmlinkage void vfp_null_entry(void); |
---|
40 | 37 | |
---|
.. | .. |
---|
45 | 42 | * Used in startup: set to non-zero if VFP checks fail |
---|
46 | 43 | * After startup, holds VFP architecture |
---|
47 | 44 | */ |
---|
48 | | -unsigned int VFP_arch; |
---|
| 45 | +static unsigned int __initdata VFP_arch; |
---|
49 | 46 | |
---|
50 | 47 | /* |
---|
51 | 48 | * The pointer to the vfpstate structure of the thread which currently |
---|
.. | .. |
---|
216 | 213 | */ |
---|
217 | 214 | static void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) |
---|
218 | 215 | { |
---|
219 | | - siginfo_t info; |
---|
220 | | - |
---|
221 | | - clear_siginfo(&info); |
---|
222 | | - info.si_signo = SIGFPE; |
---|
223 | | - info.si_code = sicode; |
---|
224 | | - info.si_addr = (void __user *)(instruction_pointer(regs) - 4); |
---|
225 | | - |
---|
226 | 216 | /* |
---|
227 | 217 | * This is the same as NWFPE, because it's not clear what |
---|
228 | 218 | * this is used for |
---|
.. | .. |
---|
230 | 220 | current->thread.error_code = 0; |
---|
231 | 221 | current->thread.trap_no = 6; |
---|
232 | 222 | |
---|
233 | | - send_sig_info(SIGFPE, &info, current); |
---|
| 223 | + send_sig_fault(SIGFPE, sicode, |
---|
| 224 | + (void __user *)(instruction_pointer(regs) - 4), |
---|
| 225 | + current); |
---|
234 | 226 | } |
---|
235 | 227 | |
---|
236 | 228 | static void vfp_panic(char *reason, u32 inst) |
---|
.. | .. |
---|
444 | 436 | * present on all CPUs within a SMP complex. Needs to be called prior to |
---|
445 | 437 | * vfp_init(). |
---|
446 | 438 | */ |
---|
447 | | -void vfp_disable(void) |
---|
| 439 | +void __init vfp_disable(void) |
---|
448 | 440 | { |
---|
449 | 441 | if (VFP_arch) { |
---|
450 | 442 | pr_debug("%s: should be called prior to vfp_init\n", __func__); |
---|
.. | .. |
---|
650 | 642 | return 0; |
---|
651 | 643 | } |
---|
652 | 644 | |
---|
653 | | -void vfp_kmode_exception(void) |
---|
| 645 | +#ifdef CONFIG_KERNEL_MODE_NEON |
---|
| 646 | + |
---|
| 647 | +static int vfp_kmode_exception(struct pt_regs *regs, unsigned int instr) |
---|
654 | 648 | { |
---|
655 | 649 | /* |
---|
656 | 650 | * If we reach this point, a floating point exception has been raised |
---|
.. | .. |
---|
668 | 662 | pr_crit("BUG: unsupported FP instruction in kernel mode\n"); |
---|
669 | 663 | else |
---|
670 | 664 | pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n"); |
---|
| 665 | + pr_crit("FPEXC == 0x%08x\n", fmrx(FPEXC)); |
---|
| 666 | + return 1; |
---|
671 | 667 | } |
---|
672 | 668 | |
---|
673 | | -#ifdef CONFIG_KERNEL_MODE_NEON |
---|
| 669 | +static struct undef_hook vfp_kmode_exception_hook[] = {{ |
---|
| 670 | + .instr_mask = 0xfe000000, |
---|
| 671 | + .instr_val = 0xf2000000, |
---|
| 672 | + .cpsr_mask = MODE_MASK | PSR_T_BIT, |
---|
| 673 | + .cpsr_val = SVC_MODE, |
---|
| 674 | + .fn = vfp_kmode_exception, |
---|
| 675 | +}, { |
---|
| 676 | + .instr_mask = 0xff100000, |
---|
| 677 | + .instr_val = 0xf4000000, |
---|
| 678 | + .cpsr_mask = MODE_MASK | PSR_T_BIT, |
---|
| 679 | + .cpsr_val = SVC_MODE, |
---|
| 680 | + .fn = vfp_kmode_exception, |
---|
| 681 | +}, { |
---|
| 682 | + .instr_mask = 0xef000000, |
---|
| 683 | + .instr_val = 0xef000000, |
---|
| 684 | + .cpsr_mask = MODE_MASK | PSR_T_BIT, |
---|
| 685 | + .cpsr_val = SVC_MODE | PSR_T_BIT, |
---|
| 686 | + .fn = vfp_kmode_exception, |
---|
| 687 | +}, { |
---|
| 688 | + .instr_mask = 0xff100000, |
---|
| 689 | + .instr_val = 0xf9000000, |
---|
| 690 | + .cpsr_mask = MODE_MASK | PSR_T_BIT, |
---|
| 691 | + .cpsr_val = SVC_MODE | PSR_T_BIT, |
---|
| 692 | + .fn = vfp_kmode_exception, |
---|
| 693 | +}, { |
---|
| 694 | + .instr_mask = 0x0c000e00, |
---|
| 695 | + .instr_val = 0x0c000a00, |
---|
| 696 | + .cpsr_mask = MODE_MASK, |
---|
| 697 | + .cpsr_val = SVC_MODE, |
---|
| 698 | + .fn = vfp_kmode_exception, |
---|
| 699 | +}}; |
---|
| 700 | + |
---|
| 701 | +static int __init vfp_kmode_exception_hook_init(void) |
---|
| 702 | +{ |
---|
| 703 | + int i; |
---|
| 704 | + |
---|
| 705 | + for (i = 0; i < ARRAY_SIZE(vfp_kmode_exception_hook); i++) |
---|
| 706 | + register_undef_hook(&vfp_kmode_exception_hook[i]); |
---|
| 707 | + return 0; |
---|
| 708 | +} |
---|
| 709 | +subsys_initcall(vfp_kmode_exception_hook_init); |
---|
674 | 710 | |
---|
675 | 711 | /* |
---|
676 | 712 | * Kernel-side NEON support functions |
---|
.. | .. |
---|
716 | 752 | |
---|
717 | 753 | #endif /* CONFIG_KERNEL_MODE_NEON */ |
---|
718 | 754 | |
---|
| 755 | +static int __init vfp_detect(struct pt_regs *regs, unsigned int instr) |
---|
| 756 | +{ |
---|
| 757 | + VFP_arch = UINT_MAX; /* mark as not present */ |
---|
| 758 | + regs->ARM_pc += 4; |
---|
| 759 | + return 0; |
---|
| 760 | +} |
---|
| 761 | + |
---|
| 762 | +static struct undef_hook vfp_detect_hook __initdata = { |
---|
| 763 | + .instr_mask = 0x0c000e00, |
---|
| 764 | + .instr_val = 0x0c000a00, |
---|
| 765 | + .cpsr_mask = MODE_MASK, |
---|
| 766 | + .cpsr_val = SVC_MODE, |
---|
| 767 | + .fn = vfp_detect, |
---|
| 768 | +}; |
---|
| 769 | + |
---|
719 | 770 | /* |
---|
720 | 771 | * VFP support code initialisation. |
---|
721 | 772 | */ |
---|
.. | .. |
---|
736 | 787 | * The handler is already setup to just log calls, so |
---|
737 | 788 | * we just need to read the VFPSID register. |
---|
738 | 789 | */ |
---|
739 | | - vfp_vector = vfp_testing_entry; |
---|
| 790 | + register_undef_hook(&vfp_detect_hook); |
---|
740 | 791 | barrier(); |
---|
741 | 792 | vfpsid = fmrx(FPSID); |
---|
742 | 793 | barrier(); |
---|
| 794 | + unregister_undef_hook(&vfp_detect_hook); |
---|
743 | 795 | vfp_vector = vfp_null_entry; |
---|
744 | 796 | |
---|
745 | 797 | pr_info("VFP support v0.3: "); |
---|