| .. | .. |
|---|
| 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: "); |
|---|