xref: /xnu-10063.141.1/osfmk/kern/exclaves_sensor.c (revision d8b80295118ef25ac3a784134bcf95cd8e88109f)
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 	exclaves_id_t eic_id = exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL,
95 	    EXCLAVES_EIC);
96 	if (eic_id == UINT64_C(~0)) {
97 		return KERN_FAILURE;
98 	}
99 
100 	tb_endpoint_t ep = tb_endpoint_create_with_value(
101 		TB_TRANSPORT_TYPE_XNU, eic_id, TB_ENDPOINT_OPTIONS_NONE);
102 
103 	tb_error_t ret =
104 	    exclaveindicatorcontroller_sensorrequest__init(&eic_client, ep);
105 
106 	return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
107 }
108 
109 static kern_return_t
exclaves_eic_display_healthcheck_rate(uint64_t ns)110 exclaves_eic_display_healthcheck_rate(uint64_t ns)
111 {
112 	exclaveindicatorcontroller_requestedrefreshrate_s rate;
113 
114 	/* Convert time to frequency and round up to nearest supported value. */
115 	switch (NSEC_PER_SEC / ns) {
116 	case 0 ... 30:
117 		exclaves_display_healthcheck_rate_hz = 30;
118 		rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_30;
119 		break;
120 	case 31 ... 60:
121 		exclaves_display_healthcheck_rate_hz = 60;
122 		rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_60;
123 		break;
124 	default:
125 		exclaves_display_healthcheck_rate_hz = 120;
126 		rate = EXCLAVEINDICATORCONTROLLER_REQUESTEDREFRESHRATE_HZ_120;
127 		break;
128 	}
129 
130 	tb_error_t ret = exclaveindicatorcontroller_sensorrequest_requestdisplayhealthcheckrate(
131 		&eic_client, rate, ^(__unused exclaveindicatorcontroller_requestresponse_s result) {});
132 
133 	return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
134 }
135 
136 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)137 exclaves_eic_sensor_start(exclaves_sensor_type_t __unused sensor_type,
138     __assert_only uint64_t flags, exclaves_sensor_status_t *status)
139 {
140 	assert3p(status, !=, NULL);
141 	assert3u(flags, ==, 0);
142 
143 	*status = EXCLAVES_SENSOR_STATUS_ALLOWED;
144 	return KERN_SUCCESS;
145 }
146 
147 static kern_return_t
exclaves_eic_sensor_stop(exclaves_sensor_type_t __unused sensor_type)148 exclaves_eic_sensor_stop(exclaves_sensor_type_t __unused sensor_type)
149 {
150 	return KERN_SUCCESS;
151 }
152 
153 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)154 exclaves_eic_sensor_status(exclaves_sensor_type_t __unused sensor_type,
155     __assert_only uint64_t flags, exclaves_sensor_status_t *status)
156 {
157 	assert3p(status, !=, NULL);
158 	assert3u(flags, ==, 0);
159 
160 	*status = EXCLAVES_SENSOR_STATUS_ALLOWED;
161 	return KERN_SUCCESS;
162 }
163 
164 /*
165  * It is intentional to keep "buffer" untyped here as it avoids xnu having to
166  * understand what those IDs are at all. They are simply passed through from the
167  * resource table as-is.
168  */
169 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)170 exclaves_eic_sensor_copy(uint32_t buffer, uint64_t size1, uint64_t offset1,
171     uint64_t size2, uint64_t offset2, exclaves_sensor_status_t *status)
172 {
173 	assert3u(size1, >, 0);
174 	assert3p(status, !=, NULL);
175 
176 	/*
177 	 * The plan in the near future is that this TB call will take both sets
178 	 * of size/offset. In the meantime call it twice here.
179 	 */
180 	tb_error_t ret = exclaveindicatorcontroller_sensorrequest_copy(
181 		&eic_client, buffer, 0, offset1, size1,
182 		^(exclaveindicatorcontroller_sensorstatusresponse_s result) {
183 		*status = eic_sensorstatus_to_sensor_status(result);
184 	});
185 
186 	if (ret != TB_ERROR_SUCCESS) {
187 		return ret;
188 	}
189 
190 	/* Return early if the status isn't EXCLAVES_SENSOR_STATUS_ALLOWED */
191 	if (*status != EXCLAVES_SENSOR_STATUS_ALLOWED || size2 == 0) {
192 		return KERN_SUCCESS;
193 	}
194 
195 	ret = exclaveindicatorcontroller_sensorrequest_copy(
196 		&eic_client, buffer, 0, offset2, size2,
197 		^(exclaveindicatorcontroller_sensorstatusresponse_s result) {
198 		*status = eic_sensorstatus_to_sensor_status(result);
199 	});
200 
201 	return ret == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
202 }
203 
204 /* -------------------------------------------------------------------------- */
205 #pragma mark sensor
206 
207 static LCK_GRP_DECLARE(sensor_lck_grp, "exclaves_sensor");
208 
209 typedef struct {
210 	/*
211 	 * Count of how many times sensor_start has been called on this sensor
212 	 * without a corresponding sensor_stop.
213 	 */
214 	uint64_t s_startcount;
215 
216 	/* mutex to protect updates to the above */
217 	lck_mtx_t s_mutex;
218 
219 	/* Keep track of whether this sensor was initialised or not. */
220 	bool s_initialised;
221 } exclaves_sensor_t;
222 
223 /**
224  * A reverse lookup table for the sensor resources,
225  * as the kpi uses sensor ids directly to access the same resources */
226 static exclaves_sensor_t sensors[EXCLAVES_SENSOR_MAX];
227 
228 /*
229  * A thread call used to periodically call "status" on any open sensors.
230  */
231 static thread_call_t sensor_healthcheck_tcall = NULL;
232 
233 static inline bool
valid_sensor(exclaves_sensor_type_t sensor_type)234 valid_sensor(exclaves_sensor_type_t sensor_type)
235 {
236 	switch (sensor_type) {
237 	case EXCLAVES_SENSOR_CAM:
238 	case EXCLAVES_SENSOR_MIC:
239 	case EXCLAVES_SENSOR_CAM_ALT_FACEID:
240 		return true;
241 	default:
242 		return false;
243 	}
244 }
245 
246 static inline exclaves_sensor_t *
sensor_type_to_sensor(exclaves_sensor_type_t sensor_type)247 sensor_type_to_sensor(exclaves_sensor_type_t sensor_type)
248 {
249 	assert(valid_sensor(sensor_type));
250 	return &sensors[sensor_type - 1];
251 }
252 
253 static inline exclaves_sensor_type_t
sensor_to_sensor_type(exclaves_sensor_t * sensor)254 sensor_to_sensor_type(exclaves_sensor_t *sensor)
255 {
256 	assert3p(sensor, <=, &sensors[EXCLAVES_SENSOR_MAX]);
257 	assert3p(sensor, >=, &sensors[0]);
258 
259 	return (exclaves_sensor_type_t)((sensor - &sensors[0]) + 1);
260 }
261 
262 /*
263  * Called from the threadcall to call into exclaves with a status command for
264  * every started sensor. Re-arms itself so it runs at a frequency set by the
265  * display healthcheck rate. Exits when there are no longer any started sensors.
266  */
267 static void
exclaves_sensor_healthcheck(__unused void * param0,__unused void * param1)268 exclaves_sensor_healthcheck(__unused void *param0, __unused void *param1)
269 {
270 	bool reschedule = false;
271 
272 	/*
273 	 * Calculate the next deadline up-front so the overhead of calling into
274 	 * exclaves doesn't add to the period.
275 	 */
276 	uint64_t deadline = 0;
277 	uint64_t leeway = 0;
278 	const uint32_t interval =
279 	    NSEC_PER_SEC / exclaves_display_healthcheck_rate_hz;
280 	clock_interval_to_deadline(interval, 1, &deadline);
281 	nanoseconds_to_absolutetime(interval / 2, &leeway);
282 
283 	for (int i = 0; i < EXCLAVES_SENSOR_MAX; i++) {
284 		exclaves_sensor_t *sensor = &sensors[i];
285 
286 		if (!sensor->s_initialised) {
287 			continue;
288 		}
289 
290 		lck_mtx_lock(&sensor->s_mutex);
291 
292 		exclaves_sensor_status_t status;
293 		if (sensor->s_startcount != 0) {
294 			(void) exclaves_sensor_status(
295 				sensor_to_sensor_type(sensor), 0, &status);
296 			reschedule = true;
297 		}
298 
299 		lck_mtx_unlock(&sensor->s_mutex);
300 	}
301 
302 	if (reschedule) {
303 		thread_call_enter_delayed_with_leeway(sensor_healthcheck_tcall,
304 		    NULL, deadline, leeway, THREAD_CALL_DELAY_LEEWAY);
305 	}
306 }
307 
308 static kern_return_t
exclaves_sensor_init(void)309 exclaves_sensor_init(void)
310 {
311 	kern_return_t kr = exclaves_eic_init();
312 	if (kr != KERN_SUCCESS) {
313 		return kr;
314 	}
315 
316 	for (uint32_t i = 1; i <= EXCLAVES_SENSOR_MAX; i++) {
317 		exclaves_sensor_t *sensor = sensor_type_to_sensor(i);
318 
319 		lck_mtx_init(&sensor->s_mutex, &sensor_lck_grp, NULL);
320 
321 		sensor->s_startcount = 0;
322 		sensor->s_initialised = true;
323 	}
324 
325 	sensor_healthcheck_tcall =
326 	    thread_call_allocate_with_priority(exclaves_sensor_healthcheck,
327 	    NULL, THREAD_CALL_PRIORITY_KERNEL);
328 
329 	return KERN_SUCCESS;
330 }
331 EXCLAVES_BOOT_TASK(exclaves_sensor_init, EXCLAVES_BOOT_RANK_ANY);
332 
333 kern_return_t
exclaves_sensor_start(exclaves_sensor_type_t sensor_type,uint64_t flags,exclaves_sensor_status_t * status)334 exclaves_sensor_start(exclaves_sensor_type_t sensor_type, uint64_t flags,
335     exclaves_sensor_status_t *status)
336 {
337 	if (!valid_sensor(sensor_type)) {
338 		return KERN_INVALID_ARGUMENT;
339 	}
340 
341 	exclaves_sensor_t *sensor = sensor_type_to_sensor(sensor_type);
342 	if (!sensor->s_initialised) {
343 		return KERN_FAILURE;
344 	}
345 
346 	lck_mtx_lock(&sensor->s_mutex);
347 	kern_return_t kr;
348 
349 	if (sensor->s_startcount == UINT64_MAX) {
350 		lck_mtx_unlock(&sensor->s_mutex);
351 		return KERN_INVALID_ARGUMENT;
352 	}
353 
354 	if (sensor->s_startcount > 0) {
355 		kr = exclaves_eic_sensor_status(sensor_type, flags, status);
356 		if (kr == KERN_SUCCESS) {
357 			sensor->s_startcount += 1;
358 		}
359 		lck_mtx_unlock(&sensor->s_mutex);
360 		return kr;
361 	}
362 
363 	// call start iff startcount is 0
364 	kr = exclaves_eic_sensor_start(sensor_type, flags, status);
365 	if (kr != KERN_SUCCESS) {
366 		lck_mtx_unlock(&sensor->s_mutex);
367 		return kr;
368 	}
369 
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_startcount = 0;
419 	kr = exclaves_eic_sensor_status(sensor_type, flags, status);
420 
421 	lck_mtx_unlock(&sensor->s_mutex);
422 
423 	return kr;
424 }
425 
426 kern_return_t
exclaves_sensor_status(exclaves_sensor_type_t sensor_type,uint64_t flags,exclaves_sensor_status_t * status)427 exclaves_sensor_status(exclaves_sensor_type_t sensor_type, uint64_t flags,
428     exclaves_sensor_status_t *status)
429 {
430 	if (!valid_sensor(sensor_type)) {
431 		return KERN_INVALID_ARGUMENT;
432 	}
433 
434 	exclaves_sensor_t *sensor = sensor_type_to_sensor(sensor_type);
435 	if (!sensor->s_initialised) {
436 		return KERN_FAILURE;
437 	}
438 
439 	return exclaves_eic_sensor_status(sensor_type, flags, status);
440 }
441 
442 kern_return_t
exclaves_display_healthcheck_rate(uint64_t ns)443 exclaves_display_healthcheck_rate(uint64_t ns)
444 {
445 	/*
446 	 * Make sure that the initialisation has taken place before calling into
447 	 * the EIC. Any sensor is sufficient.
448 	 */
449 	exclaves_sensor_t *sensor = sensor_type_to_sensor(EXCLAVES_SENSOR_CAM);
450 	if (!sensor->s_initialised) {
451 		return KERN_FAILURE;
452 	}
453 
454 	return exclaves_eic_display_healthcheck_rate(ns);
455 }
456 
457 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)458 exclaves_sensor_copy(uint32_t buffer, uint64_t size1, uint64_t offset1,
459     uint64_t size2, uint64_t offset2, exclaves_sensor_status_t *status)
460 {
461 	/*
462 	 * Make sure that the initialisation has taken place before calling into
463 	 * the EIC. Any sensor is sufficient.
464 	 */
465 	exclaves_sensor_t *sensor = sensor_type_to_sensor(EXCLAVES_SENSOR_CAM);
466 	if (!sensor->s_initialised) {
467 		return KERN_FAILURE;
468 	}
469 
470 
471 	return exclaves_eic_sensor_copy(buffer, size1, offset1, size2, offset2,
472 	           status);
473 }
474 
475 #else /* CONFIG_EXCLAVES */
476 
477 kern_return_t
exclaves_display_healthcheck_rate(__unused uint64_t ns)478 exclaves_display_healthcheck_rate(__unused uint64_t ns)
479 {
480 	return KERN_NOT_SUPPORTED;
481 }
482 
483 #endif /* CONFIG_EXCLAVES */
484