fpsimd.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. * FP/SIMD context switching and fault handling
  3. *
  4. * Copyright (C) 2012 ARM Ltd.
  5. * Author: Catalin Marinas <catalin.marinas@arm.com>
  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. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <linux/cpu_pm.h>
  20. #include <linux/kernel.h>
  21. #include <linux/init.h>
  22. #include <linux/sched.h>
  23. #include <linux/signal.h>
  24. #include <linux/hardirq.h>
  25. #include <asm/fpsimd.h>
  26. #include <asm/cputype.h>
  27. #define FPEXC_IOF (1 << 0)
  28. #define FPEXC_DZF (1 << 1)
  29. #define FPEXC_OFF (1 << 2)
  30. #define FPEXC_UFF (1 << 3)
  31. #define FPEXC_IXF (1 << 4)
  32. #define FPEXC_IDF (1 << 7)
  33. /*
  34. * Trapped FP/ASIMD access.
  35. */
  36. void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
  37. {
  38. /* TODO: implement lazy context saving/restoring */
  39. WARN_ON(1);
  40. }
  41. /*
  42. * Raise a SIGFPE for the current process.
  43. */
  44. void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)
  45. {
  46. siginfo_t info;
  47. unsigned int si_code = 0;
  48. if (esr & FPEXC_IOF)
  49. si_code = FPE_FLTINV;
  50. else if (esr & FPEXC_DZF)
  51. si_code = FPE_FLTDIV;
  52. else if (esr & FPEXC_OFF)
  53. si_code = FPE_FLTOVF;
  54. else if (esr & FPEXC_UFF)
  55. si_code = FPE_FLTUND;
  56. else if (esr & FPEXC_IXF)
  57. si_code = FPE_FLTRES;
  58. memset(&info, 0, sizeof(info));
  59. info.si_signo = SIGFPE;
  60. info.si_code = si_code;
  61. info.si_addr = (void __user *)instruction_pointer(regs);
  62. send_sig_info(SIGFPE, &info, current);
  63. }
  64. void fpsimd_thread_switch(struct task_struct *next)
  65. {
  66. /* check if not kernel threads */
  67. if (current->mm)
  68. fpsimd_save_state(&current->thread.fpsimd_state);
  69. if (next->mm)
  70. fpsimd_load_state(&next->thread.fpsimd_state);
  71. }
  72. void fpsimd_flush_thread(void)
  73. {
  74. preempt_disable();
  75. memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
  76. fpsimd_load_state(&current->thread.fpsimd_state);
  77. preempt_enable();
  78. }
  79. #ifdef CONFIG_KERNEL_MODE_NEON
  80. /*
  81. * Kernel-side NEON support functions
  82. */
  83. void kernel_neon_begin(void)
  84. {
  85. /* Avoid using the NEON in interrupt context */
  86. BUG_ON(in_interrupt());
  87. preempt_disable();
  88. if (current->mm)
  89. fpsimd_save_state(&current->thread.fpsimd_state);
  90. }
  91. EXPORT_SYMBOL(kernel_neon_begin);
  92. void kernel_neon_end(void)
  93. {
  94. if (current->mm)
  95. fpsimd_load_state(&current->thread.fpsimd_state);
  96. preempt_enable();
  97. }
  98. EXPORT_SYMBOL(kernel_neon_end);
  99. #endif /* CONFIG_KERNEL_MODE_NEON */
  100. #ifdef CONFIG_CPU_PM
  101. static int fpsimd_cpu_pm_notifier(struct notifier_block *self,
  102. unsigned long cmd, void *v)
  103. {
  104. switch (cmd) {
  105. case CPU_PM_ENTER:
  106. if (current->mm)
  107. fpsimd_save_state(&current->thread.fpsimd_state);
  108. break;
  109. case CPU_PM_EXIT:
  110. if (current->mm)
  111. fpsimd_load_state(&current->thread.fpsimd_state);
  112. break;
  113. case CPU_PM_ENTER_FAILED:
  114. default:
  115. return NOTIFY_DONE;
  116. }
  117. return NOTIFY_OK;
  118. }
  119. static struct notifier_block fpsimd_cpu_pm_notifier_block = {
  120. .notifier_call = fpsimd_cpu_pm_notifier,
  121. };
  122. static void fpsimd_pm_init(void)
  123. {
  124. cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block);
  125. }
  126. #else
  127. static inline void fpsimd_pm_init(void) { }
  128. #endif /* CONFIG_CPU_PM */
  129. /*
  130. * FP/SIMD support code initialisation.
  131. */
  132. static int __init fpsimd_init(void)
  133. {
  134. u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
  135. if (pfr & (0xf << 16)) {
  136. pr_notice("Floating-point is not implemented\n");
  137. return 0;
  138. }
  139. elf_hwcap |= HWCAP_FP;
  140. if (pfr & (0xf << 20))
  141. pr_notice("Advanced SIMD is not implemented\n");
  142. else
  143. elf_hwcap |= HWCAP_ASIMD;
  144. fpsimd_pm_init();
  145. return 0;
  146. }
  147. late_initcall(fpsimd_init);