1*bbb1b6f9SApple OSS Distributions /*
2*bbb1b6f9SApple OSS Distributions * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
3*bbb1b6f9SApple OSS Distributions *
4*bbb1b6f9SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5*bbb1b6f9SApple OSS Distributions *
6*bbb1b6f9SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*bbb1b6f9SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*bbb1b6f9SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*bbb1b6f9SApple OSS Distributions * compliance with the License. The rights granted to you under the License
10*bbb1b6f9SApple OSS Distributions * may not be used to create, or enable the creation or redistribution of,
11*bbb1b6f9SApple OSS Distributions * unlawful or unlicensed copies of an Apple operating system, or to
12*bbb1b6f9SApple OSS Distributions * circumvent, violate, or enable the circumvention or violation of, any
13*bbb1b6f9SApple OSS Distributions * terms of an Apple operating system software license agreement.
14*bbb1b6f9SApple OSS Distributions *
15*bbb1b6f9SApple OSS Distributions * Please obtain a copy of the License at
16*bbb1b6f9SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this file.
17*bbb1b6f9SApple OSS Distributions *
18*bbb1b6f9SApple OSS Distributions * The Original Code and all software distributed under the License are
19*bbb1b6f9SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20*bbb1b6f9SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21*bbb1b6f9SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22*bbb1b6f9SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23*bbb1b6f9SApple OSS Distributions * Please see the License for the specific language governing rights and
24*bbb1b6f9SApple OSS Distributions * limitations under the License.
25*bbb1b6f9SApple OSS Distributions *
26*bbb1b6f9SApple OSS Distributions * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27*bbb1b6f9SApple OSS Distributions */
28*bbb1b6f9SApple OSS Distributions /**
29*bbb1b6f9SApple OSS Distributions * On devices that support it, this test ensures that a mach exception is
30*bbb1b6f9SApple OSS Distributions * generated when a matrix-math exception is triggered, and that the
31*bbb1b6f9SApple OSS Distributions * matrix register file is correctly preserved or zeroed on context switch.
32*bbb1b6f9SApple OSS Distributions */
33*bbb1b6f9SApple OSS Distributions
34*bbb1b6f9SApple OSS Distributions /*
35*bbb1b6f9SApple OSS Distributions * IMPLEMENTATION NOTE:
36*bbb1b6f9SApple OSS Distributions *
37*bbb1b6f9SApple OSS Distributions * This test code goes to some unusual lengths to avoid calling out to libc or
38*bbb1b6f9SApple OSS Distributions * libdarwintest while the CPU is in streaming SVE mode (i.e., between
39*bbb1b6f9SApple OSS Distributions * ops->start() and ops->stop()). Both of these libraries are built with SIMD
40*bbb1b6f9SApple OSS Distributions * instructions that will cause the test executable to crash while in streaming
41*bbb1b6f9SApple OSS Distributions * SVE mode.
42*bbb1b6f9SApple OSS Distributions *
43*bbb1b6f9SApple OSS Distributions * Ordinarily this is the wrong way to solve this problem. Functions that use
44*bbb1b6f9SApple OSS Distributions * streaming SVE mode should have annotations telling the compiler so, and the
45*bbb1b6f9SApple OSS Distributions * compiler will automatically generate appropriate interworking code. However
46*bbb1b6f9SApple OSS Distributions * this interworking code will stash SME state to memory and temporarily exit
47*bbb1b6f9SApple OSS Distributions * streaming SVE mode. We're specifically testing how xnu manages live SME
48*bbb1b6f9SApple OSS Distributions * register state, so we can't let the compiler stash and disable this state
49*bbb1b6f9SApple OSS Distributions * behind our backs.
50*bbb1b6f9SApple OSS Distributions */
51*bbb1b6f9SApple OSS Distributions
52*bbb1b6f9SApple OSS Distributions #ifdef __arm64__
53*bbb1b6f9SApple OSS Distributions #include <mach/error.h>
54*bbb1b6f9SApple OSS Distributions #endif /* __arm64__ */
55*bbb1b6f9SApple OSS Distributions
56*bbb1b6f9SApple OSS Distributions #include <darwintest.h>
57*bbb1b6f9SApple OSS Distributions #include <pthread.h>
58*bbb1b6f9SApple OSS Distributions #include <stdlib.h>
59*bbb1b6f9SApple OSS Distributions #include <mach/mach.h>
60*bbb1b6f9SApple OSS Distributions #include <mach/thread_act.h>
61*bbb1b6f9SApple OSS Distributions #include <mach/thread_status.h>
62*bbb1b6f9SApple OSS Distributions #include <mach/exception.h>
63*bbb1b6f9SApple OSS Distributions #include <machine/cpu_capabilities.h>
64*bbb1b6f9SApple OSS Distributions #include <sys/types.h>
65*bbb1b6f9SApple OSS Distributions #include <sys/sysctl.h>
66*bbb1b6f9SApple OSS Distributions #include <sys/wait.h>
67*bbb1b6f9SApple OSS Distributions
68*bbb1b6f9SApple OSS Distributions #include "arm_matrix.h"
69*bbb1b6f9SApple OSS Distributions #include "exc_helpers.h"
70*bbb1b6f9SApple OSS Distributions #include "test_utils.h"
71*bbb1b6f9SApple OSS Distributions
72*bbb1b6f9SApple OSS Distributions T_GLOBAL_META(
73*bbb1b6f9SApple OSS Distributions T_META_NAMESPACE("xnu.arm"),
74*bbb1b6f9SApple OSS Distributions T_META_RADAR_COMPONENT_NAME("xnu"),
75*bbb1b6f9SApple OSS Distributions T_META_RADAR_COMPONENT_VERSION("arm"),
76*bbb1b6f9SApple OSS Distributions T_META_OWNER("ghackmann"),
77*bbb1b6f9SApple OSS Distributions T_META_RUN_CONCURRENTLY(true)
78*bbb1b6f9SApple OSS Distributions );
79*bbb1b6f9SApple OSS Distributions
80*bbb1b6f9SApple OSS Distributions #ifdef __arm64__
81*bbb1b6f9SApple OSS Distributions
82*bbb1b6f9SApple OSS Distributions #ifndef EXC_ARM_SME_DISALLOWED
83*bbb1b6f9SApple OSS Distributions #define EXC_ARM_SME_DISALLOWED 2
84*bbb1b6f9SApple OSS Distributions #endif
85*bbb1b6f9SApple OSS Distributions
86*bbb1b6f9SApple OSS Distributions /* Whether we caught the EXC_BAD_INSTRUCTION mach exception or not. */
87*bbb1b6f9SApple OSS Distributions static volatile bool mach_exc_caught = false;
88*bbb1b6f9SApple OSS Distributions
89*bbb1b6f9SApple OSS Distributions static size_t
bad_instruction_exception_handler(__unused mach_port_t task,__unused mach_port_t thread,exception_type_t type,mach_exception_data_t codes,__unused uint64_t exception_pc)90*bbb1b6f9SApple OSS Distributions bad_instruction_exception_handler(
91*bbb1b6f9SApple OSS Distributions __unused mach_port_t task,
92*bbb1b6f9SApple OSS Distributions __unused mach_port_t thread,
93*bbb1b6f9SApple OSS Distributions exception_type_t type,
94*bbb1b6f9SApple OSS Distributions mach_exception_data_t codes,
95*bbb1b6f9SApple OSS Distributions __unused uint64_t exception_pc)
96*bbb1b6f9SApple OSS Distributions {
97*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(type, EXC_BAD_INSTRUCTION, "Caught an EXC_BAD_INSTRUCTION exception");
98*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(codes[0], (uint64_t)EXC_ARM_UNDEFINED, "The subcode is EXC_ARM_UNDEFINED");
99*bbb1b6f9SApple OSS Distributions
100*bbb1b6f9SApple OSS Distributions mach_exc_caught = true;
101*bbb1b6f9SApple OSS Distributions return 4;
102*bbb1b6f9SApple OSS Distributions }
103*bbb1b6f9SApple OSS Distributions #endif
104*bbb1b6f9SApple OSS Distributions
105*bbb1b6f9SApple OSS Distributions
106*bbb1b6f9SApple OSS Distributions #ifdef __arm64__
107*bbb1b6f9SApple OSS Distributions static void
test_matrix_not_started(const struct arm_matrix_operations * ops)108*bbb1b6f9SApple OSS Distributions test_matrix_not_started(const struct arm_matrix_operations *ops)
109*bbb1b6f9SApple OSS Distributions {
110*bbb1b6f9SApple OSS Distributions if (!ops->is_available()) {
111*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-%s target, skipping...", ops->name);
112*bbb1b6f9SApple OSS Distributions }
113*bbb1b6f9SApple OSS Distributions
114*bbb1b6f9SApple OSS Distributions mach_port_t exc_port = create_exception_port(EXC_MASK_BAD_INSTRUCTION);
115*bbb1b6f9SApple OSS Distributions
116*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
117*bbb1b6f9SApple OSS Distributions uint8_t *d = ops->alloc_data();
118*bbb1b6f9SApple OSS Distributions bzero(d, size);
119*bbb1b6f9SApple OSS Distributions
120*bbb1b6f9SApple OSS Distributions ops->start();
121*bbb1b6f9SApple OSS Distributions ops->load_one_vector(d);
122*bbb1b6f9SApple OSS Distributions ops->stop();
123*bbb1b6f9SApple OSS Distributions T_PASS("%s instruction after start instruction should not cause an exception", ops->name);
124*bbb1b6f9SApple OSS Distributions
125*bbb1b6f9SApple OSS Distributions mach_exc_caught = false;
126*bbb1b6f9SApple OSS Distributions run_exception_handler(exc_port, bad_instruction_exception_handler);
127*bbb1b6f9SApple OSS Distributions ops->load_one_vector(d);
128*bbb1b6f9SApple OSS Distributions T_EXPECT_TRUE(mach_exc_caught, "%s instruction before start instruction should cause an exception", ops->name);
129*bbb1b6f9SApple OSS Distributions
130*bbb1b6f9SApple OSS Distributions free(d);
131*bbb1b6f9SApple OSS Distributions }
132*bbb1b6f9SApple OSS Distributions #endif
133*bbb1b6f9SApple OSS Distributions
134*bbb1b6f9SApple OSS Distributions
135*bbb1b6f9SApple OSS Distributions T_DECL(sme_not_started,
136*bbb1b6f9SApple OSS Distributions "Test that SME instructions before smstart generate mach exceptions.")
137*bbb1b6f9SApple OSS Distributions {
138*bbb1b6f9SApple OSS Distributions #ifndef __arm64__
139*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-arm64 target, skipping...");
140*bbb1b6f9SApple OSS Distributions #else
141*bbb1b6f9SApple OSS Distributions test_matrix_not_started(&sme_operations);
142*bbb1b6f9SApple OSS Distributions #endif
143*bbb1b6f9SApple OSS Distributions }
144*bbb1b6f9SApple OSS Distributions
145*bbb1b6f9SApple OSS Distributions #ifdef __arm64__
146*bbb1b6f9SApple OSS Distributions struct test_thread;
147*bbb1b6f9SApple OSS Distributions typedef bool (*thread_fn_t)(struct test_thread const* thread);
148*bbb1b6f9SApple OSS Distributions
149*bbb1b6f9SApple OSS Distributions struct test_thread {
150*bbb1b6f9SApple OSS Distributions pthread_t thread;
151*bbb1b6f9SApple OSS Distributions pthread_t companion_thread;
152*bbb1b6f9SApple OSS Distributions thread_fn_t thread_fn;
153*bbb1b6f9SApple OSS Distributions uint32_t cpuid;
154*bbb1b6f9SApple OSS Distributions uint32_t thread_id;
155*bbb1b6f9SApple OSS Distributions const struct arm_matrix_operations *ops;
156*bbb1b6f9SApple OSS Distributions };
157*bbb1b6f9SApple OSS Distributions
158*bbb1b6f9SApple OSS Distributions static uint32_t barrier;
159*bbb1b6f9SApple OSS Distributions static pthread_cond_t barrier_cond = PTHREAD_COND_INITIALIZER;
160*bbb1b6f9SApple OSS Distributions static pthread_mutex_t barrier_lock = PTHREAD_MUTEX_INITIALIZER;
161*bbb1b6f9SApple OSS Distributions
162*bbb1b6f9SApple OSS Distributions static uint32_t end_barrier;
163*bbb1b6f9SApple OSS Distributions static pthread_cond_t end_barrier_cond = PTHREAD_COND_INITIALIZER;
164*bbb1b6f9SApple OSS Distributions static pthread_mutex_t end_barrier_lock = PTHREAD_MUTEX_INITIALIZER;
165*bbb1b6f9SApple OSS Distributions
166*bbb1b6f9SApple OSS Distributions static void
test_thread_barrier(void)167*bbb1b6f9SApple OSS Distributions test_thread_barrier(void)
168*bbb1b6f9SApple OSS Distributions {
169*bbb1b6f9SApple OSS Distributions /* Wait for all threads to reach this barrier */
170*bbb1b6f9SApple OSS Distributions pthread_mutex_lock(&barrier_lock);
171*bbb1b6f9SApple OSS Distributions barrier--;
172*bbb1b6f9SApple OSS Distributions if (barrier) {
173*bbb1b6f9SApple OSS Distributions while (barrier) {
174*bbb1b6f9SApple OSS Distributions pthread_cond_wait(&barrier_cond, &barrier_lock);
175*bbb1b6f9SApple OSS Distributions }
176*bbb1b6f9SApple OSS Distributions } else {
177*bbb1b6f9SApple OSS Distributions pthread_cond_broadcast(&barrier_cond);
178*bbb1b6f9SApple OSS Distributions }
179*bbb1b6f9SApple OSS Distributions pthread_mutex_unlock(&barrier_lock);
180*bbb1b6f9SApple OSS Distributions }
181*bbb1b6f9SApple OSS Distributions
182*bbb1b6f9SApple OSS Distributions static void
test_thread_notify_exited(void)183*bbb1b6f9SApple OSS Distributions test_thread_notify_exited(void)
184*bbb1b6f9SApple OSS Distributions {
185*bbb1b6f9SApple OSS Distributions pthread_mutex_lock(&end_barrier_lock);
186*bbb1b6f9SApple OSS Distributions if (0 == --end_barrier) {
187*bbb1b6f9SApple OSS Distributions pthread_cond_signal(&end_barrier_cond);
188*bbb1b6f9SApple OSS Distributions }
189*bbb1b6f9SApple OSS Distributions pthread_mutex_unlock(&end_barrier_lock);
190*bbb1b6f9SApple OSS Distributions }
191*bbb1b6f9SApple OSS Distributions
192*bbb1b6f9SApple OSS Distributions static void
wait_for_test_threads(void)193*bbb1b6f9SApple OSS Distributions wait_for_test_threads(void)
194*bbb1b6f9SApple OSS Distributions {
195*bbb1b6f9SApple OSS Distributions pthread_mutex_lock(&end_barrier_lock);
196*bbb1b6f9SApple OSS Distributions while (end_barrier) {
197*bbb1b6f9SApple OSS Distributions pthread_cond_wait(&end_barrier_cond, &end_barrier_lock);
198*bbb1b6f9SApple OSS Distributions }
199*bbb1b6f9SApple OSS Distributions pthread_mutex_unlock(&end_barrier_lock);
200*bbb1b6f9SApple OSS Distributions }
201*bbb1b6f9SApple OSS Distributions
202*bbb1b6f9SApple OSS Distributions static uint32_t
ncpus(void)203*bbb1b6f9SApple OSS Distributions ncpus(void)
204*bbb1b6f9SApple OSS Distributions {
205*bbb1b6f9SApple OSS Distributions uint32_t ncpu;
206*bbb1b6f9SApple OSS Distributions size_t ncpu_size = sizeof(ncpu);
207*bbb1b6f9SApple OSS Distributions int err = sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0);
208*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(err, "Retrieved CPU count");
209*bbb1b6f9SApple OSS Distributions
210*bbb1b6f9SApple OSS Distributions return ncpu;
211*bbb1b6f9SApple OSS Distributions }
212*bbb1b6f9SApple OSS Distributions
213*bbb1b6f9SApple OSS Distributions static int
thread_bind_cpu_unchecked(uint32_t cpuid)214*bbb1b6f9SApple OSS Distributions thread_bind_cpu_unchecked(uint32_t cpuid)
215*bbb1b6f9SApple OSS Distributions {
216*bbb1b6f9SApple OSS Distributions /*
217*bbb1b6f9SApple OSS Distributions * libc's sysctl() implementation calls strlen(name), which is
218*bbb1b6f9SApple OSS Distributions * SIMD-accelerated. Avoid this by directly invoking the libsyscall
219*bbb1b6f9SApple OSS Distributions * wrapper with namelen computed at compile time.
220*bbb1b6f9SApple OSS Distributions */
221*bbb1b6f9SApple OSS Distributions #define THREAD_BIND_CPU "kern.sched_thread_bind_cpu"
222*bbb1b6f9SApple OSS Distributions extern int __sysctlbyname(const char *name, size_t namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
223*bbb1b6f9SApple OSS Distributions const char *name = THREAD_BIND_CPU;
224*bbb1b6f9SApple OSS Distributions size_t namelen = sizeof(THREAD_BIND_CPU) - 1;
225*bbb1b6f9SApple OSS Distributions return __sysctlbyname(name, namelen, NULL, 0, &cpuid, sizeof(cpuid));
226*bbb1b6f9SApple OSS Distributions }
227*bbb1b6f9SApple OSS Distributions
228*bbb1b6f9SApple OSS Distributions static void
thread_bind_cpu(uint32_t cpuid)229*bbb1b6f9SApple OSS Distributions thread_bind_cpu(uint32_t cpuid)
230*bbb1b6f9SApple OSS Distributions {
231*bbb1b6f9SApple OSS Distributions int err = thread_bind_cpu_unchecked(cpuid);
232*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_ZERO(err, "Bound thread to CPU %u", cpuid);
233*bbb1b6f9SApple OSS Distributions }
234*bbb1b6f9SApple OSS Distributions
235*bbb1b6f9SApple OSS Distributions static void *
test_thread_shim(void * arg)236*bbb1b6f9SApple OSS Distributions test_thread_shim(void *arg)
237*bbb1b6f9SApple OSS Distributions {
238*bbb1b6f9SApple OSS Distributions struct test_thread const *thread = arg;
239*bbb1b6f9SApple OSS Distributions
240*bbb1b6f9SApple OSS Distributions thread_bind_cpu(thread->cpuid);
241*bbb1b6f9SApple OSS Distributions bool const ret = thread->thread_fn(thread);
242*bbb1b6f9SApple OSS Distributions test_thread_notify_exited();
243*bbb1b6f9SApple OSS Distributions return (void *)(uintptr_t)ret;
244*bbb1b6f9SApple OSS Distributions }
245*bbb1b6f9SApple OSS Distributions
246*bbb1b6f9SApple OSS Distributions static void
test_on_each_cpu(thread_fn_t thread_fn,const struct arm_matrix_operations * ops,const char * desc)247*bbb1b6f9SApple OSS Distributions test_on_each_cpu(thread_fn_t thread_fn, const struct arm_matrix_operations *ops, const char *desc)
248*bbb1b6f9SApple OSS Distributions {
249*bbb1b6f9SApple OSS Distributions uint32_t ncpu = ncpus();
250*bbb1b6f9SApple OSS Distributions uint32_t nthreads = ncpu * 2;
251*bbb1b6f9SApple OSS Distributions barrier = 1 /* This thread */ + nthreads;
252*bbb1b6f9SApple OSS Distributions end_barrier = nthreads;
253*bbb1b6f9SApple OSS Distributions struct test_thread *threads = calloc(nthreads, sizeof(threads[0]));
254*bbb1b6f9SApple OSS Distributions
255*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < nthreads; i++) {
256*bbb1b6f9SApple OSS Distributions threads[i].thread_fn = thread_fn;
257*bbb1b6f9SApple OSS Distributions threads[i].cpuid = i % ncpu;
258*bbb1b6f9SApple OSS Distributions threads[i].thread_id = i;
259*bbb1b6f9SApple OSS Distributions threads[i].ops = ops;
260*bbb1b6f9SApple OSS Distributions
261*bbb1b6f9SApple OSS Distributions int const err = pthread_create(&threads[i].thread, NULL, test_thread_shim, &threads[i]);
262*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(err, 0, "%s: created thread #%u", desc, i);
263*bbb1b6f9SApple OSS Distributions
264*bbb1b6f9SApple OSS Distributions // The other of two threads under test pinned to the same CPU.
265*bbb1b6f9SApple OSS Distributions threads[(ncpu + i) % nthreads].companion_thread = threads[i].thread;
266*bbb1b6f9SApple OSS Distributions }
267*bbb1b6f9SApple OSS Distributions
268*bbb1b6f9SApple OSS Distributions // Wait for all companion_threads to be set.
269*bbb1b6f9SApple OSS Distributions test_thread_barrier();
270*bbb1b6f9SApple OSS Distributions
271*bbb1b6f9SApple OSS Distributions // like pthread_join()ing all threads, but without the priority boosting shenanigans.
272*bbb1b6f9SApple OSS Distributions wait_for_test_threads();
273*bbb1b6f9SApple OSS Distributions
274*bbb1b6f9SApple OSS Distributions for (uint32_t i = 0; i < nthreads; i++) {
275*bbb1b6f9SApple OSS Distributions void *thread_ret_ptr;
276*bbb1b6f9SApple OSS Distributions int err = pthread_join(threads[i].thread, &thread_ret_ptr);
277*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(err, 0, "%s: joined thread #%u", desc, i);
278*bbb1b6f9SApple OSS Distributions
279*bbb1b6f9SApple OSS Distributions bool thread_ret = (uintptr_t)thread_ret_ptr;
280*bbb1b6f9SApple OSS Distributions if (thread_ret) {
281*bbb1b6f9SApple OSS Distributions T_PASS("%s: thread #%u passed", desc, i);
282*bbb1b6f9SApple OSS Distributions } else {
283*bbb1b6f9SApple OSS Distributions T_FAIL("%s: thread #%u failed", desc, i);
284*bbb1b6f9SApple OSS Distributions }
285*bbb1b6f9SApple OSS Distributions }
286*bbb1b6f9SApple OSS Distributions
287*bbb1b6f9SApple OSS Distributions free(threads);
288*bbb1b6f9SApple OSS Distributions }
289*bbb1b6f9SApple OSS Distributions
290*bbb1b6f9SApple OSS Distributions static bool
active_context_switch_thread(struct test_thread const * thread)291*bbb1b6f9SApple OSS Distributions active_context_switch_thread(struct test_thread const* thread)
292*bbb1b6f9SApple OSS Distributions {
293*bbb1b6f9SApple OSS Distributions const struct arm_matrix_operations *ops = thread->ops;
294*bbb1b6f9SApple OSS Distributions const uint32_t thread_id = thread->thread_id;
295*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
296*bbb1b6f9SApple OSS Distributions uint8_t *d1 = ops->alloc_data();
297*bbb1b6f9SApple OSS Distributions memset(d1, (char)thread_id, size);
298*bbb1b6f9SApple OSS Distributions
299*bbb1b6f9SApple OSS Distributions uint8_t *d2 = ops->alloc_data();
300*bbb1b6f9SApple OSS Distributions
301*bbb1b6f9SApple OSS Distributions test_thread_barrier();
302*bbb1b6f9SApple OSS Distributions
303*bbb1b6f9SApple OSS Distributions // companion_thread will be valid only after the barrier.
304*bbb1b6f9SApple OSS Distributions thread_t const companion_thread = pthread_mach_thread_np(thread->companion_thread);
305*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NE(companion_thread, THREAD_NULL, "pthread_mach_thread_np");
306*bbb1b6f9SApple OSS Distributions
307*bbb1b6f9SApple OSS Distributions bool ok = true;
308*bbb1b6f9SApple OSS Distributions for (unsigned int i = 0; i < 100000 && ok; i++) {
309*bbb1b6f9SApple OSS Distributions ops->start();
310*bbb1b6f9SApple OSS Distributions ops->load_data(d1);
311*bbb1b6f9SApple OSS Distributions
312*bbb1b6f9SApple OSS Distributions /*
313*bbb1b6f9SApple OSS Distributions * Rescheduling with the matrix registers active must preserve
314*bbb1b6f9SApple OSS Distributions * state, even after a context switch.
315*bbb1b6f9SApple OSS Distributions */
316*bbb1b6f9SApple OSS Distributions thread_switch(companion_thread, SWITCH_OPTION_NONE, 0);
317*bbb1b6f9SApple OSS Distributions
318*bbb1b6f9SApple OSS Distributions ops->store_data(d2);
319*bbb1b6f9SApple OSS Distributions ops->stop();
320*bbb1b6f9SApple OSS Distributions
321*bbb1b6f9SApple OSS Distributions if (memcmp(d1, d2, size)) {
322*bbb1b6f9SApple OSS Distributions ok = false;
323*bbb1b6f9SApple OSS Distributions }
324*bbb1b6f9SApple OSS Distributions }
325*bbb1b6f9SApple OSS Distributions
326*bbb1b6f9SApple OSS Distributions free(d2);
327*bbb1b6f9SApple OSS Distributions free(d1);
328*bbb1b6f9SApple OSS Distributions return ok;
329*bbb1b6f9SApple OSS Distributions }
330*bbb1b6f9SApple OSS Distributions
331*bbb1b6f9SApple OSS Distributions static bool
inactive_context_switch_thread(struct test_thread const * thread)332*bbb1b6f9SApple OSS Distributions inactive_context_switch_thread(struct test_thread const* thread)
333*bbb1b6f9SApple OSS Distributions {
334*bbb1b6f9SApple OSS Distributions const struct arm_matrix_operations *ops = thread->ops;
335*bbb1b6f9SApple OSS Distributions const uint32_t thread_id = thread->thread_id;
336*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
337*bbb1b6f9SApple OSS Distributions uint8_t *d1 = ops->alloc_data();
338*bbb1b6f9SApple OSS Distributions memset(d1, (char)thread_id, size);
339*bbb1b6f9SApple OSS Distributions
340*bbb1b6f9SApple OSS Distributions uint8_t *d2 = ops->alloc_data();
341*bbb1b6f9SApple OSS Distributions
342*bbb1b6f9SApple OSS Distributions test_thread_barrier();
343*bbb1b6f9SApple OSS Distributions
344*bbb1b6f9SApple OSS Distributions // companion_thread will be valid only after the barrier.
345*bbb1b6f9SApple OSS Distributions thread_t const companion_thread = pthread_mach_thread_np(thread->companion_thread);
346*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NE(companion_thread, THREAD_NULL, "pthread_mach_thread_np");
347*bbb1b6f9SApple OSS Distributions
348*bbb1b6f9SApple OSS Distributions bool ok = true;
349*bbb1b6f9SApple OSS Distributions for (unsigned int i = 0; i < 100000 && ok; i++) {
350*bbb1b6f9SApple OSS Distributions ops->start();
351*bbb1b6f9SApple OSS Distributions ops->load_data(d1);
352*bbb1b6f9SApple OSS Distributions ops->stop();
353*bbb1b6f9SApple OSS Distributions
354*bbb1b6f9SApple OSS Distributions /*
355*bbb1b6f9SApple OSS Distributions * Rescheduling with the matrix registers inactive may preserve
356*bbb1b6f9SApple OSS Distributions * state or may zero it out.
357*bbb1b6f9SApple OSS Distributions */
358*bbb1b6f9SApple OSS Distributions thread_switch(companion_thread, SWITCH_OPTION_NONE, 0);
359*bbb1b6f9SApple OSS Distributions
360*bbb1b6f9SApple OSS Distributions ops->start();
361*bbb1b6f9SApple OSS Distributions ops->store_data(d2);
362*bbb1b6f9SApple OSS Distributions ops->stop();
363*bbb1b6f9SApple OSS Distributions
364*bbb1b6f9SApple OSS Distributions for (size_t j = 0; j < size; j++) {
365*bbb1b6f9SApple OSS Distributions if (d1[j] != d2[j] && d2[j] != 0) {
366*bbb1b6f9SApple OSS Distributions ok = false;
367*bbb1b6f9SApple OSS Distributions }
368*bbb1b6f9SApple OSS Distributions }
369*bbb1b6f9SApple OSS Distributions }
370*bbb1b6f9SApple OSS Distributions
371*bbb1b6f9SApple OSS Distributions free(d2);
372*bbb1b6f9SApple OSS Distributions free(d1);
373*bbb1b6f9SApple OSS Distributions return ok;
374*bbb1b6f9SApple OSS Distributions }
375*bbb1b6f9SApple OSS Distributions
376*bbb1b6f9SApple OSS Distributions static void
test_thread_migration(const struct arm_matrix_operations * ops)377*bbb1b6f9SApple OSS Distributions test_thread_migration(const struct arm_matrix_operations *ops)
378*bbb1b6f9SApple OSS Distributions {
379*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
380*bbb1b6f9SApple OSS Distributions uint8_t *d = ops->alloc_data();
381*bbb1b6f9SApple OSS Distributions arc4random_buf(d, size);
382*bbb1b6f9SApple OSS Distributions
383*bbb1b6f9SApple OSS Distributions uint32_t ncpu = ncpus();
384*bbb1b6f9SApple OSS Distributions uint8_t *cpu_d[ncpu];
385*bbb1b6f9SApple OSS Distributions for (uint32_t cpuid = 0; cpuid < ncpu; cpuid++) {
386*bbb1b6f9SApple OSS Distributions cpu_d[cpuid] = ops->alloc_data();
387*bbb1b6f9SApple OSS Distributions memset(cpu_d[cpuid], 0, size);
388*bbb1b6f9SApple OSS Distributions }
389*bbb1b6f9SApple OSS Distributions
390*bbb1b6f9SApple OSS Distributions ops->start();
391*bbb1b6f9SApple OSS Distributions ops->load_data(d);
392*bbb1b6f9SApple OSS Distributions for (uint32_t cpuid = 0; cpuid < ncpu; cpuid++) {
393*bbb1b6f9SApple OSS Distributions int err = thread_bind_cpu_unchecked(cpuid);
394*bbb1b6f9SApple OSS Distributions if (err) {
395*bbb1b6f9SApple OSS Distributions ops->stop();
396*bbb1b6f9SApple OSS Distributions T_ASSERT_POSIX_ZERO(err, "Bound thread to CPU %u", cpuid);
397*bbb1b6f9SApple OSS Distributions }
398*bbb1b6f9SApple OSS Distributions ops->store_data(cpu_d[cpuid]);
399*bbb1b6f9SApple OSS Distributions }
400*bbb1b6f9SApple OSS Distributions ops->stop();
401*bbb1b6f9SApple OSS Distributions
402*bbb1b6f9SApple OSS Distributions for (uint32_t cpuid = 0; cpuid < ncpu; cpuid++) {
403*bbb1b6f9SApple OSS Distributions int cmp = memcmp(d, cpu_d[cpuid], size);
404*bbb1b6f9SApple OSS Distributions T_EXPECT_EQ(cmp, 0, "Matrix state migrated to CPU %u", cpuid);
405*bbb1b6f9SApple OSS Distributions free(cpu_d[cpuid]);
406*bbb1b6f9SApple OSS Distributions }
407*bbb1b6f9SApple OSS Distributions free(d);
408*bbb1b6f9SApple OSS Distributions }
409*bbb1b6f9SApple OSS Distributions #endif
410*bbb1b6f9SApple OSS Distributions
411*bbb1b6f9SApple OSS Distributions
412*bbb1b6f9SApple OSS Distributions T_DECL(sme_context_switch,
413*bbb1b6f9SApple OSS Distributions "Test that SME contexts are migrated during context switch and do not leak between process contexts.",
414*bbb1b6f9SApple OSS Distributions T_META_BOOTARGS_SET("enable_skstb=1"),
415*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_SME2", 1),
416*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
417*bbb1b6f9SApple OSS Distributions {
418*bbb1b6f9SApple OSS Distributions #ifndef __arm64__
419*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-arm64 target, skipping...");
420*bbb1b6f9SApple OSS Distributions #else
421*bbb1b6f9SApple OSS Distributions if (!sme_operations.is_available()) {
422*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-SME target, skipping...");
423*bbb1b6f9SApple OSS Distributions }
424*bbb1b6f9SApple OSS Distributions
425*bbb1b6f9SApple OSS Distributions test_thread_migration(&sme_operations);
426*bbb1b6f9SApple OSS Distributions test_on_each_cpu(active_context_switch_thread, &sme_operations, "SME context migrates when active");
427*bbb1b6f9SApple OSS Distributions test_on_each_cpu(inactive_context_switch_thread, &sme_operations, "SME context does not leak across processes");
428*bbb1b6f9SApple OSS Distributions #endif
429*bbb1b6f9SApple OSS Distributions }
430*bbb1b6f9SApple OSS Distributions
431*bbb1b6f9SApple OSS Distributions
432*bbb1b6f9SApple OSS Distributions #if __arm64__
433*bbb1b6f9SApple OSS Distributions /*
434*bbb1b6f9SApple OSS Distributions * Sequence of events in thread_{get,set}_state test:
435*bbb1b6f9SApple OSS Distributions *
436*bbb1b6f9SApple OSS Distributions * 1. Parent creates child thread.
437*bbb1b6f9SApple OSS Distributions * 2. Child thread signals parent thread to proceed.
438*bbb1b6f9SApple OSS Distributions * 3. Parent populates child's matrix state registers via thread_set_state(),
439*bbb1b6f9SApple OSS Distributions * and signals child thread to proceed.
440*bbb1b6f9SApple OSS Distributions * 4. Child arbitrarily updates each byte in its local matrix register state
441*bbb1b6f9SApple OSS Distributions * by adding 1, and signals parent thread to proceed.
442*bbb1b6f9SApple OSS Distributions * 5. Parent reads back the child's updated matrix state with
443*bbb1b6f9SApple OSS Distributions * thread_get_state(), and confirms that every byte has been modified as
444*bbb1b6f9SApple OSS Distributions * expected.
445*bbb1b6f9SApple OSS Distributions */
446*bbb1b6f9SApple OSS Distributions static enum thread_state_test_state {
447*bbb1b6f9SApple OSS Distributions INIT,
448*bbb1b6f9SApple OSS Distributions CHILD_READY,
449*bbb1b6f9SApple OSS Distributions PARENT_POPULATED_MATRIX_STATE,
450*bbb1b6f9SApple OSS Distributions CHILD_UPDATED_MATRIX_STATE,
451*bbb1b6f9SApple OSS Distributions DONE
452*bbb1b6f9SApple OSS Distributions } thread_state_test_state;
453*bbb1b6f9SApple OSS Distributions
454*bbb1b6f9SApple OSS Distributions static pthread_cond_t thread_state_test_cond = PTHREAD_COND_INITIALIZER;
455*bbb1b6f9SApple OSS Distributions static pthread_mutex_t thread_state_test_lock = PTHREAD_MUTEX_INITIALIZER;
456*bbb1b6f9SApple OSS Distributions
457*bbb1b6f9SApple OSS Distributions static void
wait_for_thread_state_test_state(enum thread_state_test_state state)458*bbb1b6f9SApple OSS Distributions wait_for_thread_state_test_state(enum thread_state_test_state state)
459*bbb1b6f9SApple OSS Distributions {
460*bbb1b6f9SApple OSS Distributions pthread_mutex_lock(&thread_state_test_lock);
461*bbb1b6f9SApple OSS Distributions while (thread_state_test_state != state) {
462*bbb1b6f9SApple OSS Distributions pthread_cond_wait(&thread_state_test_cond, &thread_state_test_lock);
463*bbb1b6f9SApple OSS Distributions }
464*bbb1b6f9SApple OSS Distributions pthread_mutex_unlock(&thread_state_test_lock);
465*bbb1b6f9SApple OSS Distributions }
466*bbb1b6f9SApple OSS Distributions
467*bbb1b6f9SApple OSS Distributions static void
thread_set_state_test_state(enum thread_state_test_state state)468*bbb1b6f9SApple OSS Distributions thread_set_state_test_state(enum thread_state_test_state state)
469*bbb1b6f9SApple OSS Distributions {
470*bbb1b6f9SApple OSS Distributions pthread_mutex_lock(&thread_state_test_lock);
471*bbb1b6f9SApple OSS Distributions thread_state_test_state = state;
472*bbb1b6f9SApple OSS Distributions pthread_cond_broadcast(&thread_state_test_cond);
473*bbb1b6f9SApple OSS Distributions pthread_mutex_unlock(&thread_state_test_lock);
474*bbb1b6f9SApple OSS Distributions }
475*bbb1b6f9SApple OSS Distributions
476*bbb1b6f9SApple OSS Distributions static void *
test_matrix_thread_state_child(void * arg __unused)477*bbb1b6f9SApple OSS Distributions test_matrix_thread_state_child(void *arg __unused)
478*bbb1b6f9SApple OSS Distributions {
479*bbb1b6f9SApple OSS Distributions const struct arm_matrix_operations *ops = arg;
480*bbb1b6f9SApple OSS Distributions
481*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
482*bbb1b6f9SApple OSS Distributions uint8_t *d = ops->alloc_data();
483*bbb1b6f9SApple OSS Distributions
484*bbb1b6f9SApple OSS Distributions
485*bbb1b6f9SApple OSS Distributions thread_set_state_test_state(CHILD_READY);
486*bbb1b6f9SApple OSS Distributions wait_for_thread_state_test_state(PARENT_POPULATED_MATRIX_STATE);
487*bbb1b6f9SApple OSS Distributions ops->store_data(d);
488*bbb1b6f9SApple OSS Distributions for (size_t i = 0; i < size; i++) {
489*bbb1b6f9SApple OSS Distributions d[i]++;
490*bbb1b6f9SApple OSS Distributions }
491*bbb1b6f9SApple OSS Distributions ops->load_data(d);
492*bbb1b6f9SApple OSS Distributions thread_set_state_test_state(CHILD_UPDATED_MATRIX_STATE);
493*bbb1b6f9SApple OSS Distributions
494*bbb1b6f9SApple OSS Distributions wait_for_thread_state_test_state(DONE);
495*bbb1b6f9SApple OSS Distributions ops->stop();
496*bbb1b6f9SApple OSS Distributions return NULL;
497*bbb1b6f9SApple OSS Distributions }
498*bbb1b6f9SApple OSS Distributions
499*bbb1b6f9SApple OSS Distributions static void
test_matrix_thread_state(const struct arm_matrix_operations * ops)500*bbb1b6f9SApple OSS Distributions test_matrix_thread_state(const struct arm_matrix_operations *ops)
501*bbb1b6f9SApple OSS Distributions {
502*bbb1b6f9SApple OSS Distributions if (!ops->is_available()) {
503*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-%s target, skipping...", ops->name);
504*bbb1b6f9SApple OSS Distributions }
505*bbb1b6f9SApple OSS Distributions
506*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
507*bbb1b6f9SApple OSS Distributions uint8_t *d = ops->alloc_data();
508*bbb1b6f9SApple OSS Distributions arc4random_buf(d, size);
509*bbb1b6f9SApple OSS Distributions
510*bbb1b6f9SApple OSS Distributions thread_state_test_state = INIT;
511*bbb1b6f9SApple OSS Distributions
512*bbb1b6f9SApple OSS Distributions pthread_t thread;
513*bbb1b6f9SApple OSS Distributions #pragma clang diagnostic push
514*bbb1b6f9SApple OSS Distributions #pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
515*bbb1b6f9SApple OSS Distributions void *arg = ops;
516*bbb1b6f9SApple OSS Distributions #pragma clang diagnostic pop
517*bbb1b6f9SApple OSS Distributions int err = pthread_create(&thread, NULL, test_matrix_thread_state_child, arg);
518*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(err, 0, "pthread_create()");
519*bbb1b6f9SApple OSS Distributions
520*bbb1b6f9SApple OSS Distributions mach_port_t mach_thread = pthread_mach_thread_np(thread);
521*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_NE(mach_thread, MACH_PORT_NULL, "pthread_mach_thread_np()");
522*bbb1b6f9SApple OSS Distributions
523*bbb1b6f9SApple OSS Distributions wait_for_thread_state_test_state(CHILD_READY);
524*bbb1b6f9SApple OSS Distributions kern_return_t kr = ops->thread_set_state(mach_thread, d);
525*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(kr, KERN_SUCCESS, "%s thread_set_state()", ops->name);
526*bbb1b6f9SApple OSS Distributions thread_set_state_test_state(PARENT_POPULATED_MATRIX_STATE);
527*bbb1b6f9SApple OSS Distributions
528*bbb1b6f9SApple OSS Distributions wait_for_thread_state_test_state(CHILD_UPDATED_MATRIX_STATE);
529*bbb1b6f9SApple OSS Distributions uint8_t *thread_d = ops->alloc_data();
530*bbb1b6f9SApple OSS Distributions kr = ops->thread_get_state(mach_thread, thread_d);
531*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(kr, KERN_SUCCESS, "%s thread_get_state()", ops->name);
532*bbb1b6f9SApple OSS Distributions for (size_t i = 0; i < size; i++) {
533*bbb1b6f9SApple OSS Distributions d[i]++;
534*bbb1b6f9SApple OSS Distributions }
535*bbb1b6f9SApple OSS Distributions T_EXPECT_EQ(memcmp(d, thread_d, size), 0, "thread_get_state() read expected %s data from child thread", ops->name);
536*bbb1b6f9SApple OSS Distributions
537*bbb1b6f9SApple OSS Distributions thread_set_state_test_state(DONE);
538*bbb1b6f9SApple OSS Distributions free(thread_d);
539*bbb1b6f9SApple OSS Distributions free(d);
540*bbb1b6f9SApple OSS Distributions pthread_join(thread, NULL);
541*bbb1b6f9SApple OSS Distributions }
542*bbb1b6f9SApple OSS Distributions
543*bbb1b6f9SApple OSS Distributions #endif
544*bbb1b6f9SApple OSS Distributions
545*bbb1b6f9SApple OSS Distributions #ifdef __arm64__
546*bbb1b6f9SApple OSS Distributions
547*bbb1b6f9SApple OSS Distributions T_DECL(sme_thread_state,
548*bbb1b6f9SApple OSS Distributions "Test thread_{get,set}_state with SME thread state.",
549*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
550*bbb1b6f9SApple OSS Distributions {
551*bbb1b6f9SApple OSS Distributions test_matrix_thread_state(&sme_operations);
552*bbb1b6f9SApple OSS Distributions }
553*bbb1b6f9SApple OSS Distributions
554*bbb1b6f9SApple OSS Distributions T_DECL(sme_exception_ports,
555*bbb1b6f9SApple OSS Distributions "Test that thread_set_exception_ports rejects SME thread-state flavors.",
556*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
557*bbb1b6f9SApple OSS Distributions {
558*bbb1b6f9SApple OSS Distributions mach_port_t exc_port;
559*bbb1b6f9SApple OSS Distributions mach_port_t task = mach_task_self();
560*bbb1b6f9SApple OSS Distributions mach_port_t thread = mach_thread_self();
561*bbb1b6f9SApple OSS Distributions
562*bbb1b6f9SApple OSS Distributions kern_return_t kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &exc_port);
563*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "Allocated mach exception port");
564*bbb1b6f9SApple OSS Distributions kr = mach_port_insert_right(task, exc_port, exc_port, MACH_MSG_TYPE_MAKE_SEND);
565*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_MACH_SUCCESS(kr, "Inserted a SEND right into the exception port");
566*bbb1b6f9SApple OSS Distributions
567*bbb1b6f9SApple OSS Distributions kr = thread_set_exception_ports(thread, EXC_MASK_ALL, exc_port, EXCEPTION_STATE, ARM_THREAD_STATE64);
568*bbb1b6f9SApple OSS Distributions T_EXPECT_MACH_SUCCESS(kr, "thread_set_exception_ports accepts flavor %u", (unsigned int)ARM_THREAD_STATE64);
569*bbb1b6f9SApple OSS Distributions
570*bbb1b6f9SApple OSS Distributions for (thread_state_flavor_t flavor = ARM_SME_STATE; flavor <= ARM_SME2_STATE; flavor++) {
571*bbb1b6f9SApple OSS Distributions kr = thread_set_exception_ports(thread, EXC_MASK_ALL, exc_port, EXCEPTION_STATE, flavor);
572*bbb1b6f9SApple OSS Distributions T_EXPECT_MACH_ERROR(kr, KERN_INVALID_ARGUMENT, "thread_set_exception_ports rejects flavor %u", (unsigned int)flavor);
573*bbb1b6f9SApple OSS Distributions }
574*bbb1b6f9SApple OSS Distributions }
575*bbb1b6f9SApple OSS Distributions
576*bbb1b6f9SApple OSS Distributions T_DECL(sme_max_svl_b_sysctl,
577*bbb1b6f9SApple OSS Distributions "Test the hw.optional.arm.sme_max_svl_b sysctl",
578*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
579*bbb1b6f9SApple OSS Distributions {
580*bbb1b6f9SApple OSS Distributions unsigned int max_svl_b;
581*bbb1b6f9SApple OSS Distributions size_t max_svl_b_size = sizeof(max_svl_b);
582*bbb1b6f9SApple OSS Distributions
583*bbb1b6f9SApple OSS Distributions int err = sysctlbyname("hw.optional.arm.sme_max_svl_b", &max_svl_b, &max_svl_b_size, NULL, 0);
584*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(err, "sysctlbyname(hw.optional.arm.sme_max_svl_b)");
585*bbb1b6f9SApple OSS Distributions if (sme_operations.is_available()) {
586*bbb1b6f9SApple OSS Distributions /* Architecturally SVL must be a power-of-two between 128 and 2048 bits */
587*bbb1b6f9SApple OSS Distributions const unsigned int ARCH_MIN_SVL_B = 128 / 8;
588*bbb1b6f9SApple OSS Distributions const unsigned int ARCH_MAX_SVL_B = 2048 / 8;
589*bbb1b6f9SApple OSS Distributions
590*bbb1b6f9SApple OSS Distributions T_EXPECT_EQ(__builtin_popcount(max_svl_b), 1, "Maximum SVL_B is a power of 2");
591*bbb1b6f9SApple OSS Distributions T_EXPECT_GE(max_svl_b, ARCH_MIN_SVL_B, "Maximum SVL_B >= architectural minimum");
592*bbb1b6f9SApple OSS Distributions T_EXPECT_LE(max_svl_b, ARCH_MAX_SVL_B, "Maximum SVL_B <= architectural maximum");
593*bbb1b6f9SApple OSS Distributions } else {
594*bbb1b6f9SApple OSS Distributions T_EXPECT_EQ(max_svl_b, 0, "Maximum SVL_B is 0 when SME is unavailable");
595*bbb1b6f9SApple OSS Distributions }
596*bbb1b6f9SApple OSS Distributions }
597*bbb1b6f9SApple OSS Distributions
598*bbb1b6f9SApple OSS Distributions static void
dup_and_check_matrix_state(const struct arm_matrix_operations * ops)599*bbb1b6f9SApple OSS Distributions dup_and_check_matrix_state(const struct arm_matrix_operations *ops)
600*bbb1b6f9SApple OSS Distributions {
601*bbb1b6f9SApple OSS Distributions if (!ops->is_available()) {
602*bbb1b6f9SApple OSS Distributions T_SKIP("Running on non-%s target, skipping...", ops->name);
603*bbb1b6f9SApple OSS Distributions }
604*bbb1b6f9SApple OSS Distributions
605*bbb1b6f9SApple OSS Distributions size_t size = ops->data_size();
606*bbb1b6f9SApple OSS Distributions uint8_t *d_in = ops->alloc_data();
607*bbb1b6f9SApple OSS Distributions uint8_t *d_out = ops->alloc_data();
608*bbb1b6f9SApple OSS Distributions arc4random_buf(d_in, size);
609*bbb1b6f9SApple OSS Distributions
610*bbb1b6f9SApple OSS Distributions ops->start();
611*bbb1b6f9SApple OSS Distributions ops->load_data(d_in);
612*bbb1b6f9SApple OSS Distributions
613*bbb1b6f9SApple OSS Distributions pid_t pid = fork();
614*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(pid, "fork()");
615*bbb1b6f9SApple OSS Distributions if (pid == 0) {
616*bbb1b6f9SApple OSS Distributions ops->store_data(d_out);
617*bbb1b6f9SApple OSS Distributions ops->stop();
618*bbb1b6f9SApple OSS Distributions
619*bbb1b6f9SApple OSS Distributions int cmp = memcmp(d_in, d_out, size);
620*bbb1b6f9SApple OSS Distributions free(d_out);
621*bbb1b6f9SApple OSS Distributions free(d_in);
622*bbb1b6f9SApple OSS Distributions exit(cmp);
623*bbb1b6f9SApple OSS Distributions }
624*bbb1b6f9SApple OSS Distributions
625*bbb1b6f9SApple OSS Distributions ops->stop();
626*bbb1b6f9SApple OSS Distributions free(d_out);
627*bbb1b6f9SApple OSS Distributions free(d_in);
628*bbb1b6f9SApple OSS Distributions
629*bbb1b6f9SApple OSS Distributions siginfo_t info;
630*bbb1b6f9SApple OSS Distributions int err = waitid(P_PID, (id_t)pid, &info, WEXITED);
631*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_POSIX_SUCCESS(err, "waitid()");
632*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(info.si_signo, SIGCHLD, "child exited");
633*bbb1b6f9SApple OSS Distributions T_QUIET; T_ASSERT_EQ(info.si_code, CLD_EXITED, "child exited");
634*bbb1b6f9SApple OSS Distributions int cmp = info.si_status;
635*bbb1b6f9SApple OSS Distributions
636*bbb1b6f9SApple OSS Distributions T_EXPECT_EQ(cmp, 0, "%s state correctly duplicated during fork()", ops->name);
637*bbb1b6f9SApple OSS Distributions }
638*bbb1b6f9SApple OSS Distributions
639*bbb1b6f9SApple OSS Distributions
640*bbb1b6f9SApple OSS Distributions T_DECL(sme_thread_dup,
641*bbb1b6f9SApple OSS Distributions "Test duplicating SME thread saved-state",
642*bbb1b6f9SApple OSS Distributions T_META_REQUIRES_SYSCTL_EQ("hw.optional.arm.FEAT_SME2", 1),
643*bbb1b6f9SApple OSS Distributions XNU_T_META_SOC_SPECIFIC)
644*bbb1b6f9SApple OSS Distributions {
645*bbb1b6f9SApple OSS Distributions /*
646*bbb1b6f9SApple OSS Distributions * libsystem has streaming-incompatible atfork handlers, so for this
647*bbb1b6f9SApple OSS Distributions * test we can only set SVCR.ZA.
648*bbb1b6f9SApple OSS Distributions */
649*bbb1b6f9SApple OSS Distributions dup_and_check_matrix_state(&sme_za_operations);
650*bbb1b6f9SApple OSS Distributions }
651*bbb1b6f9SApple OSS Distributions
652*bbb1b6f9SApple OSS Distributions #endif /* __arm64__ */
653