Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* * Copyright (c) 2019 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /** * On devices that support it, this test ensures that a mach exception is * generated when an ARMv8 floating point exception is triggered. * Also verifies that the main thread's FPCR value matches its expected default. */ #include <darwintest.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <mach/mach.h> #include <mach/thread_status.h> #include <sys/sysctl.h> #include <inttypes.h> #include "exc_helpers.h" T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); /* The bit to set in FPCR to enable the divide-by-zero floating point exception. */ #define FPCR_DIV_EXC 0x200 #define FPCR_INIT (0x0) /* Whether we caught the EXC_ARITHMETIC mach exception or not. */ static volatile bool mach_exc_caught = false; #ifdef __arm64__ static size_t exc_arithmetic_handler( exception_type_t type, mach_exception_data_t codes_64) { /* Floating point divide by zero should cause an EXC_ARITHMETIC exception. */ T_ASSERT_EQ(type, EXC_ARITHMETIC, "Caught an EXC_ARITHMETIC exception"); /* Verify the exception is a floating point divide-by-zero exception. */ T_ASSERT_EQ(codes_64[0], (mach_exception_data_type_t)EXC_ARM_FP_DZ, "The subcode is EXC_ARM_FP_DZ (floating point divide-by-zero)"); mach_exc_caught = true; return 4; } #endif #define KERNEL_BOOTARGS_MAX_SIZE 1024 static char kernel_bootargs[KERNEL_BOOTARGS_MAX_SIZE]; T_DECL(armv8_fp_exception, "Test that ARMv8 floating point exceptions generate Mach exceptions, verify default FPCR value.") { #ifndef __arm64__ T_SKIP("Running on non-arm64 target, skipping..."); #else mach_port_t exc_port = MACH_PORT_NULL; size_t kernel_bootargs_len; uint64_t fpcr = __builtin_arm_rsr64("FPCR"); if (fpcr != FPCR_INIT) { T_FAIL("The floating point control register has a non-default value" "%" PRIx64, fpcr); } /* Attempt to enable Divide-by-Zero floating point exceptions in hardware. */ uint64_t fpcr_divexc = fpcr | FPCR_DIV_EXC; __builtin_arm_wsr64("FPCR", fpcr_divexc); #define DSB_ISH 0xb __builtin_arm_dsb(DSB_ISH); /* Devices that don't support floating point exceptions have FPCR as RAZ/WI. */ if (__builtin_arm_rsr64("FPCR") != fpcr_divexc) { T_SKIP("Running on a device that doesn't support floating point exceptions, skipping..."); } /* Check if floating-point exceptions are enabled */ kernel_bootargs_len = sizeof(kernel_bootargs); kern_return_t kr = sysctlbyname("kern.bootargs", kernel_bootargs, &kernel_bootargs_len, NULL, 0); if (kr != 0) { T_SKIP("Could not get kernel bootargs, skipping..."); } if (NULL == strstr(kernel_bootargs, "-fp_exceptions")) { T_SKIP("Floating-point exceptions are disabled, skipping..."); } /* Create the mach port the exception messages will be sent to. */ exc_port = create_exception_port(EXC_MASK_ARITHMETIC); /* Spawn the exception server's thread. */ run_exception_handler(exc_port, exc_arithmetic_handler); /** * This should cause a floating point divide-by-zero exception to get triggered. * * The kernel shouldn't resume this thread until the mach exception is handled * by the exception server that was just spawned. The exception handler will * explicitly increment the PC += 4 to move to the next instruction. */ float a = 6.5f; float b = 0.0f; __asm volatile ("fdiv %s0, %s1, %s2" : "=w" (a) : "w" (a), "w" (b)); if (mach_exc_caught) { T_PASS("The expected floating point divide-by-zero exception was caught!"); } else { T_FAIL("The floating point divide-by-zero exception was not captured :("); } #endif /* __arm64__ */ } |