| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 148 | exclaves_eic_sensor_stop(exclaves_sensor_type_t __unused sensor_type) | 
| 149 | { | 
| 150 | 	return KERN_SUCCESS; | 
| 151 | } | 
| 152 |  | 
| 153 | static kern_return_t | 
| 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 | 
| 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 | 
| 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 * | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 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 | 
| 478 | exclaves_display_healthcheck_rate(__unused uint64_t ns) | 
| 479 | { | 
| 480 | 	return KERN_NOT_SUPPORTED; | 
| 481 | } | 
| 482 |  | 
| 483 | #endif /* CONFIG_EXCLAVES */ | 
| 484 |  |