1 /*
2 * Copyright (c) 2023 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #if DEBUG || DEVELOPMENT
30
31 #include <kern/exclaves_test_stackshot.h>
32 #include <kern/thread.h>
33
34 static const char*
testpoint_name(tp_id_t testpoint)35 testpoint_name(tp_id_t testpoint)
36 {
37 switch (testpoint) {
38 case TP_BLOCK_START_STACKSHOT:
39 return "TP_BLOCK_START_STACKSHOT";
40 case TP_WAIT_START_STACKSHOT:
41 return "TP_WAIT_START_STACKSHOT";
42 case TP_UPCALL:
43 return "TP_UPCALL";
44 case TP_START_COLLECTION:
45 return "TP_START_COLLECTION";
46 case TP_AST:
47 return "TP_AST";
48 case TP_STACKSHOT_DONE:
49 return "TP_STACKSHOT_DONE";
50 default:
51 return "<unknown>";
52 }
53 }
54
55 /*
56 *
57 * STACKSHOT_LONG_UPCALL scenario
58 * ==============================
59 * Test return from upcall of already collected thread.
60 *
61 * stackshot thread (main test thread)
62 |
63 |
64 | [BLOCK_START_STACKSHOT @ userspace]
65 | set WAIT_START_STACKSHOT tag to to wait
66 |
67 |
68 | create exclaves thread --------------------------------------> exclaves thread starts
69 | |
70 | |
71 | start stackshot call c-hello-world exclave service
72 | |
73 | |
74 | [WAIT_START_STACKSHOT] upcall into xnu
75 | waits until exclave thread hangs in upcall |
76 | |
77 | [UPCALL]
78 | unblock <----------------------------------------------------- release stackshot thread and wait in upcall
79 | until it's flagged for AST check
80 | |
81 | stackshot collection |
82 | |
83 | |
84 | [STACKSHOT_DONE] --------------------------------------------> stop hanging in upcall and return
85 | |
86 | |
87 | compose kcdata done
88 |
89 |
90 | check kcdata in userspace
91 */
92
93 void
tp_call_stackshot_long_upcall(tp_id_t testpoint,tp_val_t __unused val)94 tp_call_stackshot_long_upcall(tp_id_t testpoint, tp_val_t __unused val)
95 {
96 printf("tp_call(STACKSHOT_LONG_UPCALL/%s) pre-start\n", testpoint_name(testpoint));
97 // do nothing for unsupported testpoints
98 switch (testpoint) {
99 case TP_BLOCK_START_STACKSHOT:
100 break;
101 case TP_WAIT_START_STACKSHOT:
102 break;
103 case TP_UPCALL:
104 break;
105 case TP_STACKSHOT_DONE:
106 break;
107 default:
108 return;
109 }
110
111 printf("tp_call(STACKSHOT_LONG_UPCALL/%s) start\n", testpoint_name(testpoint));
112 lck_mtx_lock(&tp_mtx);
113 switch (testpoint) {
114 case TP_BLOCK_START_STACKSHOT: // stackshot thread, from userspace
115 tp_block(TP_WAIT_START_STACKSHOT);
116 break;
117 case TP_WAIT_START_STACKSHOT: // stackshot thread
118 tp_wait(testpoint);
119 break;
120 case TP_UPCALL: // upcall thread, block itself and start stackshot
121 tp_relay(testpoint, TP_WAIT_START_STACKSHOT);
122 break;
123 case TP_STACKSHOT_DONE: // release a thread hanging in upcall
124 tp_unblock(TP_UPCALL);
125 break;
126 default:
127 break;
128 }
129 lck_mtx_unlock(&tp_mtx);
130 printf("tp_call(STACKSHOT_LONG_UPCALL/%s) finish\n", testpoint_name(testpoint));
131 }
132
133 /*
134 *
135 * STACKSHOT_UPCALL scenario
136 * =========================
137 * Send one thread into upcall and start exclaves collection when the thread starts waiting on AST.
138 *
139 * stackshot thread (main test thread)
140 |
141 |
142 | [BLOCK_START_STACKSHOT @ userspace]
143 | set WAIT_START_STACKSHOT tag to to wait
144 |
145 |
146 | create exclaves thread --------------------------------------> exclaves thread starts
147 | |
148 | |
149 | start stackshot call c-hello-world exclave service
150 | |
151 | |
152 | [WAIT_START_STACKSHOT] upcall into xnu
153 | waits until exclave thread hangs in upcall |
154 | |
155 | unblock <----------------------------------------------------- [UPCALL]
156 | release stackshot thread and wait in upcall
157 | |
158 | collect xnu (without exclaves) mark exclave threads blocked |
159 | |
160 | |
161 | [START_COLLECTION] ------------------------------------------> stop hanging in upcall and try to return
162 | block here, just before exclave threads collection |
163 | |
164 | |
165 | unblock <----------------------------------------------------- [TP_AST]
166 | collection pending just before AST flag check
167 | |
168 | |
169 | wait on AST check (assuming collection takes some time)
170 | |
171 | |
172 | collection done ---------------------------------------------> unblock AST
173 | |
174 | |
175 | compose kcdata done
176 |
177 |
178 | check kcdata in userspace
179 */
180
181 void
tp_call_stackshot_upcall(tp_id_t testpoint,tp_val_t __unused val)182 tp_call_stackshot_upcall(tp_id_t testpoint, tp_val_t __unused val)
183 {
184 // do nothing for unsupported testpoints
185 switch (testpoint) {
186 case TP_BLOCK_START_STACKSHOT:
187 break;
188 case TP_WAIT_START_STACKSHOT:
189 break;
190 case TP_UPCALL:
191 break;
192 case TP_START_COLLECTION:
193 break;
194 case TP_AST:
195 break;
196 default:
197 return;
198 }
199
200 printf("tp_call(STACKSHOT_UPCALL/%s) start\n", testpoint_name(testpoint));
201 lck_mtx_lock(&tp_mtx);
202 switch (testpoint) {
203 case TP_BLOCK_START_STACKSHOT: // stackshot thread, from userspace
204 tp_block(TP_WAIT_START_STACKSHOT);
205 break;
206 case TP_WAIT_START_STACKSHOT: // stackshot thread
207 tp_wait(testpoint);
208 break;
209 case TP_UPCALL: // upcall thread, block itself and start stackshot
210 tp_relay(testpoint, TP_WAIT_START_STACKSHOT);
211 break;
212 case TP_START_COLLECTION: // stackshot thread, just before exclaves collection start
213 tp_relay(testpoint, TP_UPCALL);
214 break;
215 case TP_AST: // upcall thread, just before it starts wait for AST
216 tp_unblock(TP_START_COLLECTION);
217 break;
218 default:
219 break;
220 }
221 lck_mtx_unlock(&tp_mtx);
222 printf("tp_call(STACKSHOT_UPCALL/%s) finish\n", testpoint_name(testpoint));
223 }
224
225 #endif /* DEBUG || DEVELOPMENT */
226