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 #include <stdint.h>
30 #include <mach/exclaves.h>
31 #include <mach/kern_return.h>
32
33 #include "exclaves_boot.h"
34 #include "exclaves_resource.h"
35 #include "exclaves_sensor.h"
36
37 #if CONFIG_EXCLAVES
38
39 #include <kern/locks.h>
40 #include <kern/thread_call.h>
41
42 #include "kern/exclaves.tightbeam.h"
43
44 /* -------------------------------------------------------------------------- */
45 #pragma mark EIC
46
47 #define EXCLAVES_EIC "com.apple.service.ExclaveIndicatorController"
48
49 /* Default to 120Hz */
50 static uint64_t exclaves_display_healthcheck_rate_hz =
51 EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_120;
52
53 static exclaveindicatorcontroller_sensorrequest_s eic_client;
54
55 static inline __unused exclaveindicatorcontroller_sensortype_s
sensor_type_to_eic_sensortype(exclaves_sensor_type_t type)56 sensor_type_to_eic_sensortype(exclaves_sensor_type_t type)
57 {
58 assert3u(type, >, 0);
59 assert3u(type, <=, EXCLAVES_SENSOR_MAX);
60
61 switch (type) {
62 case EXCLAVES_SENSOR_CAM:
63 return EXCLAVEINDICATORCONTROLLER_SENSORTYPE_SENSOR_CAM;
64 case EXCLAVES_SENSOR_MIC:
65 return EXCLAVEINDICATORCONTROLLER_SENSORTYPE_SENSOR_MIC;
66 case EXCLAVES_SENSOR_CAM_ALT_FACEID:
67 return EXCLAVEINDICATORCONTROLLER_SENSORTYPE_SENSOR_CAM_ALT_FACEID;
68 default:
69 panic("unknown sensor type");
70 }
71 }
72
73 static inline exclaves_sensor_status_t
eic_sensorstatus_to_sensor_status(exclaveindicatorcontroller_sensorstatusresponse_s status)74 eic_sensorstatus_to_sensor_status(exclaveindicatorcontroller_sensorstatusresponse_s status)
75 {
76 assert3u(status, >, 0);
77 assert3u(status, <=, EXCLAVEINDICATORCONTROLLER_SENSORSTATUSRESPONSE_SENSOR_CONTROL);
78
79 switch (status) {
80 case EXCLAVEINDICATORCONTROLLER_SENSORSTATUSRESPONSE_SENSOR_ALLOWED:
81 return EXCLAVES_SENSOR_STATUS_ALLOWED;
82 case EXCLAVEINDICATORCONTROLLER_SENSORSTATUSRESPONSE_SENSOR_DENIED:
83 return EXCLAVES_SENSOR_STATUS_DENIED;
84 case EXCLAVEINDICATORCONTROLLER_SENSORSTATUSRESPONSE_SENSOR_CONTROL:
85 return EXCLAVES_SENSOR_STATUS_CONTROL;
86 default:
87 panic("unknown sensor status");
88 }
89 }
90
91 static kern_return_t
exclaves_eic_init(void)92 exclaves_eic_init(void)
93 {
94 /*
95 * As this happens during exclaves_boot() we can't rely on
96 * exclaves_endpoint_lookup().
97 */
98 exclaves_id_t eic_id = exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, EXCLAVES_EIC);
99 if (eic_id == UINT64_C(~0)) {
100 return KERN_FAILURE;
101 }
102
103 tb_endpoint_t ep = tb_endpoint_create_with_value(
104 TB_TRANSPORT_TYPE_XNU, eic_id, TB_ENDPOINT_OPTIONS_NONE);
105
106 tb_error_t ret =
107 exclaveindicatorcontroller_sensorrequest__init(&eic_client, ep);
108
109 return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
110 }
111
112 static kern_return_t
exclaves_eic_display_healthcheck_rate(uint64_t ns)113 exclaves_eic_display_healthcheck_rate(uint64_t ns)
114 {
115 exclaveindicatorcontroller_requestedrefreshrate_s rate;
116
117 /* Convert time to frequency and round up to nearest supported value. */
118 switch (NSEC_PER_SEC / ns) {
119 case 0 ... 30:
120 exclaves_display_healthcheck_rate_hz = 30;
121 rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_30;
122 break;
123 case 31 ... 60:
124 exclaves_display_healthcheck_rate_hz = 60;
125 rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_60;
126 break;
127 default:
128 exclaves_display_healthcheck_rate_hz = 120;
129 rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_120;
130 break;
131 }
132
133 tb_error_t ret = exclaveindicatorcontroller_sensorrequest_requestdisplayhealthcheckrate(
134 &eic_client, rate, ^(__unused exclaveindicatorcontroller_requestresponse_s result) {});
135
136 return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
137 }
138
139 static kern_return_t
exclaves_eic_sensor_start(exclaves_sensor_type_t __unused sensor_type,__assert_only uint64_t flags,exclaves_sensor_status_t * status)140 exclaves_eic_sensor_start(exclaves_sensor_type_t __unused sensor_type,
141 __assert_only uint64_t flags, exclaves_sensor_status_t *status)
142 {
143 assert3p(status, !=, NULL);
144 assert3u(flags, ==, 0);
145
146 *status = EXCLAVES_SENSOR_STATUS_ALLOWED;
147 return KERN_SUCCESS;
148 }
149
150 static kern_return_t
exclaves_eic_sensor_stop(exclaves_sensor_type_t __unused sensor_type)151 exclaves_eic_sensor_stop(exclaves_sensor_type_t __unused sensor_type)
152 {
153 return KERN_SUCCESS;
154 }
155
156 static kern_return_t
exclaves_eic_sensor_status(exclaves_sensor_type_t __unused sensor_type,__assert_only uint64_t flags,exclaves_sensor_status_t * status)157 exclaves_eic_sensor_status(exclaves_sensor_type_t __unused sensor_type,
158 __assert_only uint64_t flags, exclaves_sensor_status_t *status)
159 {
160 assert3p(status, !=, NULL);
161 assert3u(flags, ==, 0);
162
163 *status = EXCLAVES_SENSOR_STATUS_ALLOWED;
164 return KERN_SUCCESS;
165 }
166
167 /*
168 * It is intentional to keep "buffer" untyped here as it avoids xnu having to
169 * understand what those IDs are at all. They are simply passed through from the
170 * resource table as-is.
171 */
172 static kern_return_t
exclaves_eic_sensor_copy(uint32_t buffer,uint64_t size1,uint64_t offset1,uint64_t size2,uint64_t offset2,exclaves_sensor_status_t * status)173 exclaves_eic_sensor_copy(uint32_t buffer, uint64_t size1, uint64_t offset1,
174 uint64_t size2, uint64_t offset2, exclaves_sensor_status_t *status)
175 {
176 assert3u(size1, >, 0);
177 assert3p(status, !=, NULL);
178
179 /*
180 * The plan in the near future is that this TB call will take both sets
181 * of size/offset. In the meantime call it twice here.
182 */
183 tb_error_t ret = exclaveindicatorcontroller_sensorrequest_copy(
184 &eic_client, buffer, 0, offset1, size1,
185 ^(exclaveindicatorcontroller_sensorstatusresponse_s result) {
186 *status = eic_sensorstatus_to_sensor_status(result);
187 });
188
189 if (ret != TB_ERROR_SUCCESS) {
190 return ret;
191 }
192
193 /* Return early if the status isn't EXCLAVES_SENSOR_STATUS_ALLOWED */
194 if (*status != EXCLAVES_SENSOR_STATUS_ALLOWED || size2 == 0) {
195 return KERN_SUCCESS;
196 }
197
198 ret = exclaveindicatorcontroller_sensorrequest_copy(
199 &eic_client, buffer, 0, offset2, size2,
200 ^(exclaveindicatorcontroller_sensorstatusresponse_s result) {
201 *status = eic_sensorstatus_to_sensor_status(result);
202 });
203
204 return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
205 }
206
207 /* -------------------------------------------------------------------------- */
208 #pragma mark sensor
209
210 static LCK_GRP_DECLARE(sensor_lck_grp, "exclaves_sensor");
211
212 typedef struct {
213 /*
214 * Count of how many times sensor_start has been called on this sensor
215 * without a corresponding sensor_stop.
216 */
217 uint64_t s_startcount;
218
219 /* mutex to protect updates to the above */
220 lck_mtx_t s_mutex;
221
222 /* Keep track of whether this sensor was initialised or not. */
223 bool s_initialised;
224 } exclaves_sensor_t;
225
226 /**
227 * A reverse lookup table for the sensor resources,
228 * as the kpi uses sensor ids directly to access the same resources */
229 static exclaves_sensor_t sensors[EXCLAVES_SENSOR_MAX];
230
231 /*
232 * A thread call used to periodically call "status" on any open sensors.
233 */
234 static thread_call_t sensor_healthcheck_tcall = NULL;
235
236 static inline bool
valid_sensor(exclaves_sensor_type_t sensor_type)237 valid_sensor(exclaves_sensor_type_t sensor_type)
238 {
239 switch (sensor_type) {
240 case EXCLAVES_SENSOR_CAM:
241 case EXCLAVES_SENSOR_MIC:
242 case EXCLAVES_SENSOR_CAM_ALT_FACEID:
243 return true;
244 default:
245 return false;
246 }
247 }
248
249 static inline exclaves_sensor_t *
sensor_type_to_sensor(exclaves_sensor_type_t sensor_type)250 sensor_type_to_sensor(exclaves_sensor_type_t sensor_type)
251 {
252 assert(valid_sensor(sensor_type));
253 return &sensors[sensor_type - 1];
254 }
255
256 static inline exclaves_sensor_type_t
sensor_to_sensor_type(exclaves_sensor_t * sensor)257 sensor_to_sensor_type(exclaves_sensor_t *sensor)
258 {
259 assert3p(sensor, <=, &sensors[EXCLAVES_SENSOR_MAX]);
260 assert3p(sensor, >=, &sensors[0]);
261
262 return (exclaves_sensor_type_t)((sensor - &sensors[0]) + 1);
263 }
264
265 /*
266 * Called from the threadcall to call into exclaves with a status command for
267 * every started sensor. Re-arms itself so it runs at a frequency set by the
268 * display healthcheck rate. Exits when there are no longer any started sensors.
269 */
270 static void
exclaves_sensor_healthcheck(__unused void * param0,__unused void * param1)271 exclaves_sensor_healthcheck(__unused void *param0, __unused void *param1)
272 {
273 bool reschedule = false;
274
275 /*
276 * Calculate the next deadline up-front so the overhead of calling into
277 * exclaves doesn't add to the period.
278 */
279 uint64_t deadline = 0;
280 uint64_t leeway = 0;
281 const uint32_t interval =
282 NSEC_PER_SEC / exclaves_display_healthcheck_rate_hz;
283 clock_interval_to_deadline(interval, 1, &deadline);
284 nanoseconds_to_absolutetime(interval / 2, &leeway);
285
286 for (int i = 0; i < EXCLAVES_SENSOR_MAX; i++) {
287 exclaves_sensor_t *sensor = &sensors[i];
288
289 if (!sensor->s_initialised) {
290 continue;
291 }
292
293 lck_mtx_lock(&sensor->s_mutex);
294
295 exclaves_sensor_status_t status;
296 if (sensor->s_startcount != 0) {
297 (void) exclaves_sensor_status(
298 sensor_to_sensor_type(sensor), 0, &status);
299 reschedule = true;
300 }
301
302 lck_mtx_unlock(&sensor->s_mutex);
303 }
304
305 if (reschedule) {
306 thread_call_enter_delayed_with_leeway(sensor_healthcheck_tcall,
307 NULL, deadline, leeway, THREAD_CALL_DELAY_LEEWAY);
308 }
309 }
310
311 static kern_return_t
exclaves_sensor_init(void)312 exclaves_sensor_init(void)
313 {
314 kern_return_t kr = exclaves_eic_init();
315 if (kr != KERN_SUCCESS) {
316 return kr;
317 }
318
319 for (uint32_t i = 1; i <= EXCLAVES_SENSOR_MAX; i++) {
320 exclaves_sensor_t *sensor = sensor_type_to_sensor(i);
321
322 lck_mtx_init(&sensor->s_mutex, &sensor_lck_grp, NULL);
323
324 sensor->s_startcount = 0;
325 sensor->s_initialised = true;
326 }
327
328 sensor_healthcheck_tcall =
329 thread_call_allocate_with_priority(exclaves_sensor_healthcheck,
330 NULL, THREAD_CALL_PRIORITY_KERNEL);
331
332 return KERN_SUCCESS;
333 }
334 EXCLAVES_BOOT_TASK(exclaves_sensor_init, EXCLAVES_BOOT_RANK_ANY);
335
336 kern_return_t
exclaves_sensor_start(exclaves_sensor_type_t sensor_type,uint64_t flags,exclaves_sensor_status_t * status)337 exclaves_sensor_start(exclaves_sensor_type_t sensor_type, uint64_t flags,
338 exclaves_sensor_status_t *status)
339 {
340 if (!valid_sensor(sensor_type)) {
341 return KERN_INVALID_ARGUMENT;
342 }
343
344 exclaves_sensor_t *sensor = sensor_type_to_sensor(sensor_type);
345 if (!sensor->s_initialised) {
346 return KERN_FAILURE;
347 }
348
349 lck_mtx_lock(&sensor->s_mutex);
350 kern_return_t kr;
351
352 if (sensor->s_startcount == UINT64_MAX) {
353 lck_mtx_unlock(&sensor->s_mutex);
354 return KERN_INVALID_ARGUMENT;
355 }
356
357 if (sensor->s_startcount > 0) {
358 kr = exclaves_eic_sensor_status(sensor_type, flags, status);
359 if (kr == KERN_SUCCESS) {
360 sensor->s_startcount += 1;
361 }
362 lck_mtx_unlock(&sensor->s_mutex);
363 return kr;
364 }
365
366 // call start iff startcount is 0
367 kr = exclaves_eic_sensor_start(sensor_type, flags, status);
368 if (kr != KERN_SUCCESS) {
369 lck_mtx_unlock(&sensor->s_mutex);
370 return kr;
371 }
372
373 sensor->s_startcount += 1;
374
375 lck_mtx_unlock(&sensor->s_mutex);
376
377 /* Kick off the periodic status check. */
378 (void)thread_call_enter(sensor_healthcheck_tcall);
379
380 return KERN_SUCCESS;
381 }
382
383 kern_return_t
exclaves_sensor_stop(exclaves_sensor_type_t sensor_type,uint64_t flags,exclaves_sensor_status_t * status)384 exclaves_sensor_stop(exclaves_sensor_type_t sensor_type, uint64_t flags,
385 exclaves_sensor_status_t *status)
386 {
387 if (!valid_sensor(sensor_type)) {
388 return KERN_INVALID_ARGUMENT;
389 }
390
391 exclaves_sensor_t *sensor = sensor_type_to_sensor(sensor_type);
392 if (!sensor->s_initialised) {
393 return KERN_FAILURE;
394 }
395
396 kern_return_t kr;
397
398 lck_mtx_lock(&sensor->s_mutex);
399
400 if (sensor->s_startcount == 0) {
401 lck_mtx_unlock(&sensor->s_mutex);
402 return KERN_INVALID_ARGUMENT;
403 }
404
405 if (sensor->s_startcount > 1) {
406 kr = exclaves_eic_sensor_status(sensor_type, flags, status);
407 if (kr == KERN_SUCCESS) {
408 sensor->s_startcount -= 1;
409 }
410 lck_mtx_unlock(&sensor->s_mutex);
411 return kr;
412 }
413
414 // call stop iff startcount is going to go to 0
415 kr = exclaves_eic_sensor_stop(sensor_type);
416 if (kr != KERN_SUCCESS) {
417 lck_mtx_unlock(&sensor->s_mutex);
418 return kr;
419 }
420
421 sensor->s_startcount = 0;
422 kr = exclaves_eic_sensor_status(sensor_type, flags, status);
423
424 lck_mtx_unlock(&sensor->s_mutex);
425
426 return kr;
427 }
428
429 kern_return_t
exclaves_sensor_status(exclaves_sensor_type_t sensor_type,uint64_t flags,exclaves_sensor_status_t * status)430 exclaves_sensor_status(exclaves_sensor_type_t sensor_type, uint64_t flags,
431 exclaves_sensor_status_t *status)
432 {
433 if (!valid_sensor(sensor_type)) {
434 return KERN_INVALID_ARGUMENT;
435 }
436
437 exclaves_sensor_t *sensor = sensor_type_to_sensor(sensor_type);
438 if (!sensor->s_initialised) {
439 return KERN_FAILURE;
440 }
441
442 return exclaves_eic_sensor_status(sensor_type, flags, status);
443 }
444
445 kern_return_t
exclaves_display_healthcheck_rate(uint64_t ns)446 exclaves_display_healthcheck_rate(uint64_t ns)
447 {
448 /*
449 * Make sure that the initialisation has taken place before calling into
450 * the EIC. Any sensor is sufficient.
451 */
452 exclaves_sensor_t *sensor = sensor_type_to_sensor(EXCLAVES_SENSOR_CAM);
453 if (!sensor->s_initialised) {
454 return KERN_FAILURE;
455 }
456
457 return exclaves_eic_display_healthcheck_rate(ns);
458 }
459
460 kern_return_t
exclaves_sensor_copy(uint32_t buffer,uint64_t size1,uint64_t offset1,uint64_t size2,uint64_t offset2,exclaves_sensor_status_t * status)461 exclaves_sensor_copy(uint32_t buffer, uint64_t size1, uint64_t offset1,
462 uint64_t size2, uint64_t offset2, exclaves_sensor_status_t *status)
463 {
464 /*
465 * Make sure that the initialisation has taken place before calling into
466 * the EIC. Any sensor is sufficient.
467 */
468 exclaves_sensor_t *sensor = sensor_type_to_sensor(EXCLAVES_SENSOR_CAM);
469 if (!sensor->s_initialised) {
470 return KERN_FAILURE;
471 }
472
473
474 return exclaves_eic_sensor_copy(buffer, size1, offset1, size2, offset2,
475 status);
476 }
477
478 #else /* CONFIG_EXCLAVES */
479
480 kern_return_t
exclaves_display_healthcheck_rate(__unused uint64_t ns)481 exclaves_display_healthcheck_rate(__unused uint64_t ns)
482 {
483 return KERN_NOT_SUPPORTED;
484 }
485
486 #endif /* CONFIG_EXCLAVES */
487