xref: /xnu-10063.101.15/osfmk/kern/exclaves_sensor.c (revision 94d3b452840153a99b38a3a9659680b2a006908e)
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