vfp.c 12 KB

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