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