vfp.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. vfp.c
  9. Abstract:
  10. This module implements kernel support routines for the Vector Floating
  11. Point Unit and Advanced SIMD hardware on ARM.
  12. Author:
  13. Evan Green 10-Nov-2015
  14. Environment:
  15. Kernel
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/kernel/kernel.h>
  21. #include <minoca/kernel/arm.h>
  22. //
  23. // ---------------------------------------------------------------- Definitions
  24. //
  25. //
  26. // Define opcode masks for VFP/NEON instructions.
  27. //
  28. #define ARM_VFP_INSTRUCTION_MASK1 0xFF100000
  29. #define ARM_VFP_INSTRUCTION_VALUE1 0xF4000000
  30. #define ARM_VFP_INSTRUCTION_MASK2 0xFE000000
  31. #define ARM_VFP_INSTRUCTION_VALUE2 0xF2000000
  32. #define THUMB_VFP_INSTRUCTION_MASK1 0xEF000000
  33. #define THUMB_VFP_INSTRUCTION_VALUE1 0xEF000000
  34. #define THUMB_VFP_INSTRUCTION_MASK2 0xFF100000
  35. #define THUMB_VFP_INSTRUCTION_VALUE2 0xFA000000
  36. #define ARM_COPROCESSOR_INSTRUCTION_MASK 0x0C000000
  37. #define ARM_COPROCESSOR_INSTRUCTION_VALUE 0x0C000000
  38. #define ARM_COPROCESSOR_INSTRUCTION_COPROCESSOR_MASK 0x00000F00
  39. #define ARM_COPROCESSOR_INSTRUCTION_COPROCESSOR_SHIFT 8
  40. //
  41. // ------------------------------------------------------ Data Type Definitions
  42. //
  43. //
  44. // ----------------------------------------------- Internal Function Prototypes
  45. //
  46. BOOL
  47. ArpHandleVfpException (
  48. PTRAP_FRAME TrapFrame
  49. );
  50. VOID
  51. ArpRestoreFpuState (
  52. PFPU_CONTEXT Context,
  53. BOOL SimdSupport
  54. );
  55. BOOL
  56. ArpDummyVfpExceptionHandler (
  57. PTRAP_FRAME TrapFrame
  58. );
  59. //
  60. // -------------------------------------------------------------------- Globals
  61. //
  62. PARM_HANDLE_EXCEPTION ArHandleVfpException;
  63. //
  64. // Remember if there are 32 or 16 VFP registers.
  65. //
  66. BOOL ArVfpRegisters32;
  67. //
  68. // ------------------------------------------------------------------ Functions
  69. //
  70. VOID
  71. ArInitializeVfpSupport (
  72. VOID
  73. )
  74. /*++
  75. Routine Description:
  76. This routine initializes processor support for the VFP unit, and sets the
  77. related feature bits in the user shared data.
  78. Arguments:
  79. None.
  80. Return Value:
  81. None.
  82. --*/
  83. {
  84. ULONG CoprocessorAccess;
  85. ULONG Extensions;
  86. ULONG FpsId;
  87. PARM_HANDLE_EXCEPTION OldHandler;
  88. ULONG Subarchitecture;
  89. PUSER_SHARED_DATA UserSharedData;
  90. UserSharedData = MmGetUserSharedData();
  91. //
  92. // Enable access to coprocessors 10 and 11.
  93. //
  94. CoprocessorAccess = ArGetCoprocessorAccessRegister();
  95. CoprocessorAccess &= ~(ARM_COPROCESSOR_ACCESS_MASK(10) |
  96. ARM_COPROCESSOR_ACCESS_MASK(11));
  97. CoprocessorAccess |=
  98. ARM_COPROCESSOR_ACCESS(10, ARM_COPROCESSOR_ACCESS_FULL) |
  99. ARM_COPROCESSOR_ACCESS(11, ARM_COPROCESSOR_ACCESS_FULL);
  100. ArSetCoprocessorAccessRegister(CoprocessorAccess);
  101. //
  102. // Get the floating point ID register. This register might not exist, so a
  103. // dummy handler is set up to just return 0 in that case. This kind of
  104. // thing is obviously only safe during early kernel init, as while the
  105. // global is set any real FPU exceptions would be completely mishandled.
  106. //
  107. OldHandler = ArHandleVfpException;
  108. ArHandleVfpException = ArpDummyVfpExceptionHandler;
  109. FpsId = ArGetFloatingPointIdRegister();
  110. ArHandleVfpException = OldHandler;
  111. if (FpsId == 0) {
  112. return;
  113. }
  114. if (((FpsId & ARM_FPSID_IMPLEMENTER_MASK) >> ARM_FPSID_IMPLEMENTER_SHIFT) !=
  115. ARM_FPSID_IMPLEMENTER_ARM) {
  116. return;
  117. }
  118. Subarchitecture = (FpsId & ARM_FPSID_SUBARCHITECTURE_MASK) >>
  119. ARM_FPSID_SUBARCHITECTURE_SHIFT;
  120. if (Subarchitecture < ARM_FPSID_SUBARCHITECTURE_VFPV2) {
  121. return;
  122. }
  123. UserSharedData->ProcessorFeatures |= ARM_FEATURE_VFP2;
  124. ArHandleVfpException = ArpHandleVfpException;
  125. if (Subarchitecture >= ARM_FPSID_SUBARCHITECTURE_VFPV3_COMMON_V2) {
  126. UserSharedData->ProcessorFeatures |= ARM_FEATURE_VFP3;
  127. Extensions = ArGetMvfr0Register();
  128. if ((Extensions & ARM_MVFR0_SIMD_REGISTERS_MASK) ==
  129. ARM_MVFR0_SIMD_REGISTERS_32) {
  130. UserSharedData->ProcessorFeatures |= ARM_FEATURE_NEON32;
  131. ArVfpRegisters32 = TRUE;
  132. }
  133. }
  134. return;
  135. }
  136. VOID
  137. ArSaveFpuState (
  138. PFPU_CONTEXT Buffer
  139. )
  140. /*++
  141. Routine Description:
  142. This routine saves the current FPU context into the given buffer.
  143. Arguments:
  144. Buffer - Supplies a pointer to the buffer where the information will be
  145. saved to.
  146. Return Value:
  147. None.
  148. --*/
  149. {
  150. PFPU_CONTEXT AlignedContext;
  151. AlignedContext = (PVOID)(UINTN)ALIGN_RANGE_UP((UINTN)Buffer,
  152. FPU_CONTEXT_ALIGNMENT);
  153. ArSaveVfp(AlignedContext, ArVfpRegisters32);
  154. return;
  155. }
  156. BOOL
  157. ArCheckForVfpException (
  158. PTRAP_FRAME TrapFrame,
  159. ULONG Instruction
  160. )
  161. /*++
  162. Routine Description:
  163. This routine checks for VFP or NEON undefined instruction faults, and
  164. potentially handles them if found.
  165. Arguments:
  166. TrapFrame - Supplies a pointer to the state immediately before the
  167. exception.
  168. Instruction - Supplies the instruction that caused the abort.
  169. Return Value:
  170. None.
  171. --*/
  172. {
  173. ULONG Coprocessor;
  174. BOOL IsVfp;
  175. IsVfp = FALSE;
  176. //
  177. // Check for coprocessor access to CP10 or 11.
  178. //
  179. if ((Instruction & ARM_COPROCESSOR_INSTRUCTION_MASK) ==
  180. ARM_COPROCESSOR_INSTRUCTION_VALUE) {
  181. Coprocessor = (Instruction &
  182. ARM_COPROCESSOR_INSTRUCTION_COPROCESSOR_MASK) >>
  183. ARM_COPROCESSOR_INSTRUCTION_COPROCESSOR_SHIFT;
  184. if ((Coprocessor == 10) || (Coprocessor == 11)) {
  185. IsVfp = TRUE;
  186. }
  187. } else if ((TrapFrame->Cpsr & PSR_FLAG_THUMB) != 0) {
  188. if (((Instruction & THUMB_VFP_INSTRUCTION_MASK1) ==
  189. THUMB_VFP_INSTRUCTION_VALUE1) ||
  190. ((Instruction & THUMB_VFP_INSTRUCTION_MASK2) ==
  191. THUMB_VFP_INSTRUCTION_VALUE2)) {
  192. IsVfp = TRUE;
  193. }
  194. } else {
  195. if (((Instruction & ARM_VFP_INSTRUCTION_MASK1) ==
  196. ARM_VFP_INSTRUCTION_VALUE1) ||
  197. ((Instruction & ARM_VFP_INSTRUCTION_MASK2) ==
  198. ARM_VFP_INSTRUCTION_VALUE2)) {
  199. IsVfp = TRUE;
  200. }
  201. }
  202. //
  203. // If it's not a VFP instruction or there is no handler, bail.
  204. //
  205. if ((IsVfp == FALSE) || (ArHandleVfpException == NULL)) {
  206. return FALSE;
  207. }
  208. return ArHandleVfpException(TrapFrame);
  209. }
  210. VOID
  211. ArDisableFpu (
  212. VOID
  213. )
  214. /*++
  215. Routine Description:
  216. This routine disallows access to the FPU on the current processor, causing
  217. all future accesses to generate exceptions.
  218. Arguments:
  219. None.
  220. Return Value:
  221. None.
  222. --*/
  223. {
  224. ArSetVfpExceptionRegister(0);
  225. return;
  226. }
  227. PFPU_CONTEXT
  228. ArAllocateFpuContext (
  229. ULONG AllocationTag
  230. )
  231. /*++
  232. Routine Description:
  233. This routine allocates a buffer that can be used for FPU context.
  234. Arguments:
  235. AllocationTag - Supplies the pool allocation tag to use for the allocation.
  236. Return Value:
  237. Returns a pointer to the newly allocated FPU context on success.
  238. NULL on allocation failure.
  239. --*/
  240. {
  241. UINTN AllocationSize;
  242. PVOID Context;
  243. PFPU_CONTEXT ContextStructure;
  244. PUSER_SHARED_DATA UserSharedData;
  245. AllocationSize = sizeof(FPU_CONTEXT) + FPU_CONTEXT_ALIGNMENT;
  246. Context = MmAllocateNonPagedPool(AllocationSize, AllocationTag);
  247. if (Context == NULL) {
  248. return NULL;
  249. }
  250. RtlZeroMemory(Context, AllocationSize);
  251. ContextStructure = (PVOID)(UINTN)ALIGN_RANGE_UP((UINTN)Context,
  252. FPU_CONTEXT_ALIGNMENT);
  253. //
  254. // Currently the software assist support needed for VFPv2 and older is not
  255. // implemented. The bounce code usually covers denormalized numbers, so
  256. // set the flush to zero bit to cover the gap. This is not completely
  257. // IEEE754 compliant, but is good enough to limp along on these older
  258. // cores.
  259. //
  260. UserSharedData = MmGetUserSharedData();
  261. if ((UserSharedData->ProcessorFeatures & ARM_FEATURE_VFP3) == 0) {
  262. ContextStructure->Fpscr = ARM_FPSCR_FLUSH_TO_ZERO |
  263. ARM_FPSCR_DEFAULT_NAN;
  264. }
  265. return Context;
  266. }
  267. VOID
  268. ArDestroyFpuContext (
  269. PFPU_CONTEXT Context
  270. )
  271. /*++
  272. Routine Description:
  273. This routine destroys a previously allocated FPU context buffer.
  274. Arguments:
  275. Context - Supplies a pointer to the context to destroy.
  276. Return Value:
  277. None.
  278. --*/
  279. {
  280. MmFreeNonPagedPool(Context);
  281. return;
  282. }
  283. //
  284. // --------------------------------------------------------- Internal Functions
  285. //
  286. BOOL
  287. ArpHandleVfpException (
  288. PTRAP_FRAME TrapFrame
  289. )
  290. /*++
  291. Routine Description:
  292. This routine handles a floating point access exception.
  293. Arguments:
  294. TrapFrame - Supplies a pointer to the exception trap frame.
  295. Return Value:
  296. TRUE if the exception was handled.
  297. FALSE if the exception was not handled.
  298. --*/
  299. {
  300. ULONG Control;
  301. BOOL Handled;
  302. RUNLEVEL OldRunLevel;
  303. PKTHREAD Thread;
  304. ASSERT(ArAreInterruptsEnabled() != FALSE);
  305. Handled = FALSE;
  306. Thread = KeGetCurrentThread();
  307. //
  308. // Kernel mode should not be tripping into FPU code, as it would destroy
  309. // user FPU context without the proper care.
  310. //
  311. ASSERT(ArIsTrapFrameFromPrivilegedMode(TrapFrame) == FALSE);
  312. //
  313. // If the thread has never used the FPU before, allocate FPU context while
  314. // still at low level.
  315. //
  316. if (Thread->FpuContext == NULL) {
  317. ASSERT((Thread->FpuFlags & THREAD_FPU_FLAG_IN_USE) == 0);
  318. Thread->FpuContext =
  319. ArAllocateFpuContext(PS_FPU_CONTEXT_ALLOCATION_TAG);
  320. if (Thread->FpuContext == NULL) {
  321. PsSignalThread(Thread, SIGNAL_BUS_ERROR, NULL, TRUE);
  322. goto HandleVfpExceptionEnd;
  323. }
  324. }
  325. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  326. //
  327. // Enable the FPU. If it is already enabled then this is probably an
  328. // unsupported FPU instruction.
  329. //
  330. Control = ArGetVfpExceptionRegister();
  331. if ((Control & ARM_FPEXC_ENABLE) != 0) {
  332. if ((Control & ARM_FPEXC_EXCEPTION) != 0) {
  333. RtlDebugPrint("VFP Exception: 0x%x\n", Control);
  334. } else {
  335. RtlDebugPrint("Unsupported VFP instruction.\n");
  336. }
  337. RtlDebugPrint("FPINST 0x%x FPSCR 0x%x\n",
  338. ArGetVfpInstructionRegister(),
  339. ArGetFpscr());
  340. Control &= ~ARM_FPEXC_EXCEPTION;
  341. ArSetVfpExceptionRegister(Control);
  342. KeLowerRunLevel(OldRunLevel);
  343. goto HandleVfpExceptionEnd;
  344. }
  345. Control |= ARM_FPEXC_ENABLE;
  346. ArSetVfpExceptionRegister(Control);
  347. //
  348. // Unless the thread already owns the FPU, do a full restore. This also
  349. // serves as an init for a new FPU user.
  350. //
  351. if ((Thread->FpuFlags & THREAD_FPU_FLAG_OWNER) == 0) {
  352. ArpRestoreFpuState(Thread->FpuContext, ArVfpRegisters32);
  353. }
  354. Thread->FpuFlags |= THREAD_FPU_FLAG_OWNER | THREAD_FPU_FLAG_IN_USE;
  355. KeLowerRunLevel(OldRunLevel);
  356. Handled = TRUE;
  357. HandleVfpExceptionEnd:
  358. return Handled;
  359. }
  360. VOID
  361. ArpRestoreFpuState (
  362. PFPU_CONTEXT Context,
  363. BOOL SimdSupport
  364. )
  365. /*++
  366. Routine Description:
  367. This routine restores the Vector Floating Point unit state into the
  368. hardware.
  369. Arguments:
  370. Context - Supplies a pointer to the context to restore.
  371. SimdSupport - Supplies a boolean indicating whether the VFP unit contains
  372. 32 64-bit registers (TRUE) or 16 64-bit registers (FALSE).
  373. Return Value:
  374. None.
  375. --*/
  376. {
  377. PFPU_CONTEXT AlignedContext;
  378. AlignedContext = (PVOID)(UINTN)ALIGN_RANGE_UP((UINTN)Context,
  379. FPU_CONTEXT_ALIGNMENT);
  380. ArRestoreVfp(AlignedContext, SimdSupport);
  381. return;
  382. }
  383. BOOL
  384. ArpDummyVfpExceptionHandler (
  385. PTRAP_FRAME TrapFrame
  386. )
  387. /*++
  388. Routine Description:
  389. This routine contains a no-op VFP exception handler, which simply always
  390. sets R0 to zero. It is used only during early kernel VFP detection.
  391. Arguments:
  392. TrapFrame - Supplies a pointer to the exception trap frame.
  393. Return Value:
  394. TRUE if the exception was handled.
  395. FALSE if the exception was not handled.
  396. --*/
  397. {
  398. TrapFrame->R0 = 0;
  399. return TRUE;
  400. }