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