xref: /xnu-12377.81.4/osfmk/kern/exclaves_test_stackshot.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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