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 CONFIG_EXCLAVES
30
31#if DEVELOPMENT || DEBUG
32
33#include <kern/kalloc.h>
34#include <kern/locks.h>
35
36#include <mach/exclaves_l4.h>
37
38#include <Tightbeam/tightbeam.h>
39#include <Tightbeam/tightbeam_private.h>
40#include "kern/exclaves.tightbeam.h"
41#include "exclaves_debug.h"
42
43/* External & generated headers */
44#include <xrt_hosted_types/types.h>
45#include <xnuproxy/messages.h>
46#include "exclaves_resource.h"
47
48#if __has_include(<Tightbeam/tightbeam.h>)
49
50#define EXCLAVES_ID_HELLO_EXCLAVE_EP \
51 (exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \
52 "com.apple.service.HelloExclave"))
53
54static int
55exclaves_hello_exclave_test(__unused int64_t in, int64_t *out)
56{
57 kern_return_t kr = KERN_SUCCESS;
58
59 if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
60 exclaves_debug_printf(show_test_output,
61 "%s: SKIPPED: Exclaves not available\n", __func__);
62 *out = -1;
63 return 0;
64 }
65
66 exclaves_debug_printf(show_test_output, "%s: STARTING\n", __func__);
67
68 Exclaves_L4_IpcBuffer_t *ipcb;
69 kr = exclaves_allocate_ipc_buffer((void**)&ipcb);
70 assert(kr == KERN_SUCCESS);
71 assert(ipcb != NULL);
72
73 exclaves_tag_t tag = Exclaves_L4_MessageTag(0, 0, 0x1338ul,
74 Exclaves_L4_False);
75
76 exclaves_debug_printf(show_test_output,
77 "exclaves: exclaves_endpoint_call() sending tag 0x%llx, "
78 "label 0x%lx\n", tag, Exclaves_L4_MessageTag_Label(tag));
79
80 exclaves_error_t error;
81 kr = exclaves_endpoint_call(IPC_PORT_NULL, EXCLAVES_ID_HELLO_EXCLAVE_EP,
82 &tag, &error);
83 assert(kr == KERN_SUCCESS);
84
85 exclaves_debug_printf(show_test_output,
86 "exclaves: exclaves_endpoint_call() returned tag 0x%llx, "
87 "label 0x%lx, error 0x%llx\n", tag, Exclaves_L4_MessageTag_Label(tag),
88 error);
89
90 assert(error == Exclaves_L4_Success);
91 assert(Exclaves_L4_MessageTag_Mrs(tag) == 0);
92 assert((uint16_t)Exclaves_L4_MessageTag_Label(tag) == (uint16_t)0x1339ul);
93
94 kr = exclaves_free_ipc_buffer();
95 assert(kr == KERN_SUCCESS);
96
97 exclaves_debug_printf(show_test_output, "%s: SUCCESS\n", __func__);
98 *out = 1;
99
100 return 0;
101}
102SYSCTL_TEST_REGISTER(exclaves_hello_exclave_test, exclaves_hello_exclave_test);
103
104static int
105exclaves_panic_exclave_test(__unused int64_t in, int64_t *out)
106{
107 kern_return_t kr = KERN_SUCCESS;
108
109 if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
110 exclaves_debug_printf(show_test_output,
111 "%s: SKIPPED: Exclaves not available\n", __func__);
112 *out = -1;
113 return 0;
114 }
115
116 printf("%s: STARTING\n", __func__);
117
118 Exclaves_L4_IpcBuffer_t *ipcb;
119 kr = exclaves_allocate_ipc_buffer((void**)&ipcb);
120 assert(kr == KERN_SUCCESS);
121 assert(ipcb != NULL);
122
123 // 0x9 tag will panic the HELLO C Exclave example
124 exclaves_tag_t tag = Exclaves_L4_MessageTag(0, 0, 0x9ul,
125 Exclaves_L4_False);
126
127 exclaves_debug_printf(show_test_output,
128 "exclaves: exclaves_endpoint_call() sending tag 0x%llx, "
129 "label 0x%lx\n", tag, Exclaves_L4_MessageTag_Label(tag));
130
131 exclaves_error_t error;
132 kr = exclaves_endpoint_call(IPC_PORT_NULL, EXCLAVES_ID_HELLO_EXCLAVE_EP,
133 &tag, &error);
134
135 /* Should never reach here */
136 assert(kr == KERN_SUCCESS);
137
138 exclaves_debug_printf(show_test_output,
139 "exclaves: exclaves_endpoint_call() returned tag 0x%llx, "
140 "label 0x%lx, error 0x%llx\n", tag, Exclaves_L4_MessageTag_Label(tag),
141 error);
142
143 assert(error == Exclaves_L4_Success);
144
145 kr = exclaves_free_ipc_buffer();
146 assert(kr == KERN_SUCCESS);
147
148 /* This should not be reachable. Hence, failed. */
149 exclaves_debug_printf(show_errors, "%s: FAILED\n", __func__);
150 *out = 1;
151
152 return 0;
153}
154SYSCTL_TEST_REGISTER(exclaves_panic_exclave_test, exclaves_panic_exclave_test);
155
156#define EXCLAVES_ID_SWIFT_HELLO_EXCLAVE_EP \
157 (exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \
158 "com.apple.service.HelloTightbeam"))
159
160static int
161exclaves_hello_tightbeam_test(__unused int64_t in, int64_t *out)
162{
163 if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
164 exclaves_debug_printf(show_test_output,
165 "%s: SKIPPED: Exclaves not available\n", __func__);
166 *out = -1;
167 return 0;
168 }
169
170 exclaves_debug_printf(show_test_output, "%s: STARTING\n", __func__);
171 tb_endpoint_t ep = tb_endpoint_create_with_value(
172 TB_TRANSPORT_TYPE_XNU, EXCLAVES_ID_SWIFT_HELLO_EXCLAVE_EP, 0);
173
174 tb_client_connection_t client =
175 tb_client_connection_create_with_endpoint(ep);
176
177 tb_client_connection_activate(client);
178
179 tb_message_t message = NULL;
180 tb_transport_message_buffer_t tpt_buf = NULL;
181
182 message = kalloc_type(struct tb_message_s, Z_WAITOK | Z_ZERO | Z_NOFAIL);
183 tpt_buf = kalloc_type(struct tb_transport_message_buffer_s,
184 Z_WAITOK | Z_ZERO | Z_NOFAIL);
185
186 const char *hello_tb = "Hello!";
187 const char *hello = hello_tb;
188 const char *goodbye = "Goodbye!";
189
190 tb_error_t err = TB_ERROR_SUCCESS;
191 err = tb_client_connection_message_construct(client, message,
192 tpt_buf, strlen(hello), 0);
193 if (err != TB_ERROR_SUCCESS) {
194 exclaves_debug_printf(show_errors,
195 "%s: FAILURE -- Failed to construct message\n", __func__);
196 *out = 0;
197 goto out;
198 }
199 exclaves_debug_printf(show_test_output,
200 "%s: Tightbeam constructing message: ", __func__);
201 for (const char *c = hello; *c; c++) {
202 exclaves_debug_printf(show_test_output, "%c", (uint8_t)*c);
203 tb_message_encode_u8(message, (uint8_t)*c);
204 }
205 exclaves_debug_printf(show_test_output, "\n");
206 tb_message_complete(message);
207 exclaves_debug_printf(show_test_output,
208 "%s: Tightbeam message completed\n", __func__);
209
210 tb_message_t response = NULL;
211
212 err = tb_connection_send_query(client, message, &response,
213 TB_CONNECTION_WAIT_FOR_REPLY);
214 if (err != TB_ERROR_SUCCESS) {
215 exclaves_debug_printf(show_errors,
216 "%s: FAILURE -- Failed to send message\n", __func__);
217 goto out;
218 }
219 exclaves_debug_printf(show_test_output,
220 "%s: Tightbeam message send success, reply: ", __func__);
221
222 bool mismatch = false;
223 uint8_t val = 0;
224 for (const char *c = goodbye; *c; c++) {
225 tb_message_decode_u8(response, &val);
226 exclaves_debug_printf(show_test_output, "%c", val);
227 if (val != (uint8_t)*c) {
228 mismatch = true;
229 }
230 }
231 exclaves_debug_printf(show_test_output, "\n");
232 if (mismatch) {
233 exclaves_debug_printf(show_errors,
234 "%s: FAILURE -- Mismatched reply message\n", __func__);
235 *out = 0;
236 goto out;
237 }
238 tb_client_connection_message_destruct(client, message);
239
240 exclaves_debug_printf(show_test_output, "%s: SUCCESS\n", __func__);
241 *out = 1;
242
243out:
244 kfree_type(struct tb_message_s, message);
245 kfree_type(struct tb_transport_message_buffer_s, tpt_buf);
246 return 0;
247}
248
249SYSCTL_TEST_REGISTER(exclaves_hello_tightbeam_test,
250 exclaves_hello_tightbeam_test);
251
252#endif /* __has_include(<Tightbeam/tightbeam.h>) */
253
254static int
255exclaves_sensor_kpi_test(int64_t in, int64_t *out)
256{
257#pragma unused(in)
258#define SENSOR_TEST(x) \
259 if (!(x)) { \
260 exclaves_debug_printf(show_errors, \
261 "%s: FAILURE -- %s:%d\n", __func__, __FILE__, __LINE__); \
262 success = false; \
263 goto out; \
264 }
265
266 bool success = true;
267 exclaves_debug_printf(show_test_output, "%s: STARTING\n", __func__);
268
269 exclaves_sensor_type_t sensors[] = {
270 EXCLAVES_SENSOR_CAM,
271 EXCLAVES_SENSOR_MIC,
272 EXCLAVES_SENSOR_CAM_ALT_FACEID,
273 };
274 unsigned num_sensors = sizeof(sensors) / sizeof(sensors[0]);
275 exclaves_sensor_status_t sensor_status = EXCLAVES_SENSOR_STATUS_DENIED;
276 exclaves_sensor_type_t bad =
277 (exclaves_sensor_type_t) (unsigned) (EXCLAVES_SENSOR_MAX + 1);
278 kern_return_t kr;
279
280 /* invalid sensor */
281 kr = exclaves_sensor_stop(bad, 0, &sensor_status);
282 SENSOR_TEST(kr == KERN_INVALID_ARGUMENT);
283
284 kr = exclaves_sensor_start(bad, 0, &sensor_status);
285 SENSOR_TEST(kr == KERN_INVALID_ARGUMENT);
286
287 kr = exclaves_sensor_status(bad, 0, &sensor_status);
288 SENSOR_TEST(kr == KERN_INVALID_ARGUMENT);
289
290 /* stop before start */
291 for (unsigned i = 0; i < num_sensors; i++) {
292 kr = exclaves_sensor_stop(sensors[i], 0, &sensor_status);
293 SENSOR_TEST(kr == KERN_INVALID_ARGUMENT);
294 }
295
296 /* start status is denied */
297 for (unsigned i = 0; i < num_sensors; i++) {
298 kr = exclaves_sensor_status(sensors[i], 0, &sensor_status);
299 SENSOR_TEST(kr == KERN_SUCCESS);
300 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED); /* Not enforced */
301 }
302
303 /* ALLOWED after at least 1 start */
304 unsigned const n = 5;
305 for (unsigned i = 0; i < num_sensors; i++) {
306 for (unsigned j = 0; j < n; j++) {
307 kr = exclaves_sensor_start(sensors[i], 0, &sensor_status);
308 SENSOR_TEST(kr == KERN_SUCCESS);
309 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED);
310 kr = exclaves_sensor_status(sensors[i], 0, &sensor_status);
311 SENSOR_TEST(kr == KERN_SUCCESS);
312 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED);
313 }
314 }
315
316 /* ALLOWED after n-1 stops */
317 for (unsigned i = 0; i < num_sensors; i++) {
318 for (unsigned j = 0; j < n - 1; j++) {
319 kr = exclaves_sensor_stop(sensors[i], 0, &sensor_status);
320 SENSOR_TEST(kr == KERN_SUCCESS);
321 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED);
322 kr = exclaves_sensor_status(sensors[i], 0, &sensor_status);
323 SENSOR_TEST(kr == KERN_SUCCESS);
324 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED);
325 }
326 }
327
328 /* DENIED after final stop */
329 for (unsigned i = 0; i < num_sensors; i++) {
330 kr = exclaves_sensor_stop(sensors[i], 0, &sensor_status);
331 SENSOR_TEST(kr == KERN_SUCCESS);
332 SENSOR_TEST(sensor_status == EXCLAVES_SENSOR_STATUS_ALLOWED); /* Not enforced */
333 }
334
335 /* exclaves_display_healthcheck_rate does something */
336 kr = exclaves_display_healthcheck_rate(NSEC_PER_SEC / 60);
337 SENSOR_TEST(kr == KERN_SUCCESS);
338
339#undef SENSOR_TEST
340out:
341 if (success) {
342 exclaves_debug_printf(show_test_output,
343 "%s: SUCCESS\n", __func__);
344 *out = 1;
345 } else {
346 exclaves_debug_printf(show_errors, "%s: FAILED\n", __func__);
347 *out = 0;
348 }
349 return 0;
350}
351SYSCTL_TEST_REGISTER(exclaves_sensor_kpi_test,
352 exclaves_sensor_kpi_test);
353
354static int
355exclaves_check_mem_usage_test(__unused int64_t in, int64_t *out)
356{
357 if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
358 exclaves_debug_printf(show_test_output,
359 "%s: SKIPPED: Exclaves not available\n", __func__);
360 *out = -1;
361 return 0;
362 }
363
364 exclaves_debug_printf(show_test_output, "%s: STARTING\n", __func__);
365
366 kern_return_t r = exclaves_xnu_proxy_check_mem_usage();
367 if (r == KERN_FAILURE) {
368 exclaves_debug_printf(show_errors,
369 "Exclave Check Memory Usage failed: Kernel Failure\n");
370 return 0;
371 }
372
373 exclaves_debug_printf(show_test_output, "%s: SUCCESS\n", __func__);
374 *out = 1;
375
376 return 0;
377}
378SYSCTL_TEST_REGISTER(exclaves_check_mem_usage_test, exclaves_check_mem_usage_test);
379
380
381#endif /* DEVELOPMENT || DEBUG */
382
383#endif /* CONFIG_EXCLAVES */
384