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 <mach/exclaves.h>
30#include "exclaves_upcalls.h"
31
32#if CONFIG_EXCLAVES
33
34#if __has_include(<Tightbeam/tightbeam.h>)
35
36#include <mach/exclaves_l4.h>
37
38#include <stdint.h>
39
40#include <Tightbeam/tightbeam.h>
41#include <Tightbeam/tightbeam_private.h>
42
43#include <kern/kalloc.h>
44#include <kern/locks.h>
45#include <kern/task.h>
46
47#include <xnuproxy/exclaves.h>
48
49#include "kern/exclaves.tightbeam.h"
50
51#include "exclaves_boot.h"
52#include "exclaves_debug.h"
53#include "exclaves_driverkit.h"
54#include "exclaves_storage.h"
55#include "exclaves_test_stackshot.h"
56#include "exclaves_conclave.h"
57#include "exclaves_memory.h"
58
59#include <sys/errno.h>
60
61#define EXCLAVES_ID_HELLO_EXCLAVE_EP \
62 (exclaves_service_lookup(EXCLAVES_DOMAIN_KERNEL, \
63 "com.apple.service.HelloExclave"))
64
65#define EXCLAVES_ID_TIGHTBEAM_UPCALL \
66 ((exclaves_id_t)XNUPROXY_UPCALL_TIGHTBEAM)
67
68extern lck_mtx_t exclaves_boot_lock;
69
70typedef struct exclaves_upcall_handler_registration {
71 exclaves_upcall_handler_t handler;
72 void *context;
73} exclaves_upcall_handler_registration_t;
74
75static exclaves_upcall_handler_registration_t
76 exclaves_upcall_handlers[NUM_XNUPROXY_UPCALLS];
77
78#if DEVELOPMENT || DEBUG
79static kern_return_t
80exclaves_test_hello_upcall_handler(void *, exclaves_tag_t *, exclaves_badge_t);
81#endif /* DEVELOPMENT || DEBUG */
82
83extern kern_return_t exclaves_xnu_proxy_send(xnuproxy_msg_t *,
84 Exclaves_L4_Word_t *);
85
86/* -------------------------------------------------------------------------- */
87#pragma mark Upcall Callouts
88
89static tb_error_t
90exclaves_helloupcall(const uint64_t arg, tb_error_t (^completion)(uint64_t));
91
92/*
93 * Tightbeam upcall callout table.
94 * Don't add inline functionality here, instead call directly into your
95 * sub-system.
96 */
97
98static const xnuupcalls_xnuupcalls__server_s exclaves_tightbeam_upcalls = {
99 /* BEGIN IGNORE CODESTYLE */
100 /* Uncrustify doesn't deal well with Blocks. */
101 .helloupcall = ^(const uint64_t arg, tb_error_t (^completion)(uint64_t)) {
102 return exclaves_helloupcall(arg, completion);
103 },
104
105 .alloc = ^(const uint32_t npages, xnuupcalls_pagekind_s kind,
106 tb_error_t (^completion)(xnuupcalls_pagelist_s)) {
107 return exclaves_memory_upcall_alloc(npages, kind, completion);
108 },
109
110 .free = ^(const uint32_t pages[_Nonnull EXCLAVES_MEMORY_MAX_REQUEST],
111 const uint32_t npages, const xnuupcalls_pagekind_s kind,
112 tb_error_t (^completion)(void)) {
113 return exclaves_memory_upcall_free(pages, npages, kind, completion);
114 },
115
116 .root = ^(const uint8_t exclaveid[_Nonnull 32],
117 tb_error_t (^completion)(xnuupcalls_xnuupcalls_root__result_s)) {
118 return exclaves_storage_upcall_root(exclaveid, completion);
119 },
120
121 .open = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t rootid,
122 const uint8_t name[_Nonnull 256],
123 tb_error_t (^completion)(xnuupcalls_xnuupcalls_open__result_s)) {
124 return exclaves_storage_upcall_open(fstag, rootid, name, completion);
125 },
126
127 .close = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t fileid,
128 tb_error_t (^completion)(xnuupcalls_xnuupcalls_close__result_s)) {
129 return exclaves_storage_upcall_close(fstag, fileid, completion);
130 },
131
132 .create = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t rootid,
133 const uint8_t name[_Nonnull 256],
134 tb_error_t (^completion)(xnuupcalls_xnuupcalls_create__result_s)) {
135 return exclaves_storage_upcall_create(fstag, rootid, name, completion);
136 },
137
138 .read = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t fileid,
139 const struct xnuupcalls_iodesc_s *descriptor,
140 tb_error_t (^completion)(xnuupcalls_xnuupcalls_read__result_s)) {
141 return exclaves_storage_upcall_read(fstag, fileid, descriptor, completion);
142 },
143
144 .write = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t fileid,
145 const struct xnuupcalls_iodesc_s *descriptor,
146 tb_error_t (^completion)(xnuupcalls_xnuupcalls_write__result_s)) {
147 return exclaves_storage_upcall_write(fstag, fileid, descriptor, completion);
148 },
149
150 .remove = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t rootid,
151 const uint8_t name[_Nonnull 256],
152 tb_error_t (^completion)(xnuupcalls_xnuupcalls_remove__result_s)) {
153 return exclaves_storage_upcall_remove(fstag, rootid, name, completion);
154 },
155
156 .sync = ^(const enum xnuupcalls_fstag_s fstag,
157 const enum xnuupcalls_syncop_s op,
158 const uint64_t fileid,
159 tb_error_t (^completion)(xnuupcalls_xnuupcalls_sync__result_s)) {
160 return exclaves_storage_upcall_sync(fstag, op, fileid, completion);
161 },
162
163 .readdir = ^(const enum xnuupcalls_fstag_s fstag,
164 const uint64_t fileid, const uint64_t buf,
165 const uint32_t length,
166 tb_error_t (^completion)(xnuupcalls_xnuupcalls_readdir__result_s)) {
167 return exclaves_storage_upcall_readdir(fstag, fileid, buf, length, completion);
168 },
169
170 .getsize = ^(const enum xnuupcalls_fstag_s fstag, const uint64_t fileid,
171 tb_error_t (^completion)(xnuupcalls_xnuupcalls_getsize__result_s)) {
172 return exclaves_storage_upcall_getsize(fstag, fileid, completion);
173 },
174
175 .sealstate = ^(const enum xnuupcalls_fstag_s fstag,
176 tb_error_t (^completion)(xnuupcalls_xnuupcalls_sealstate__result_s)) {
177 return exclaves_storage_upcall_sealstate(fstag, completion);
178 },
179
180 .irq_register = ^(const uint64_t id, const int32_t index,
181 tb_error_t (^completion)(xnuupcalls_xnuupcalls_irq_register__result_s)) {
182 return exclaves_driverkit_upcall_irq_register(id, index, completion);
183 },
184
185 .irq_remove = ^(const uint64_t id, const int32_t index,
186 tb_error_t (^completion)(xnuupcalls_xnuupcalls_irq_remove__result_s)) {
187 return exclaves_driverkit_upcall_irq_remove(id, index, completion);
188 },
189
190 .irq_enable = ^(const uint64_t id, const int32_t index,
191 tb_error_t (^completion)(xnuupcalls_xnuupcalls_irq_enable__result_s)) {
192 return exclaves_driverkit_upcall_irq_enable(id, index, completion);
193 },
194
195 .irq_disable = ^(const uint64_t id, const int32_t index,
196 tb_error_t (^completion)(xnuupcalls_xnuupcalls_irq_disable__result_s)) {
197 return exclaves_driverkit_upcall_irq_disable(id, index, completion);
198 },
199
200 .timer_register = ^(const uint64_t id,
201 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_register__result_s)) {
202 return exclaves_driverkit_upcall_timer_register(id, completion);
203 },
204
205 .timer_remove = ^(const uint64_t id, const uint32_t timer_id,
206 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_remove__result_s)) {
207 return exclaves_driverkit_upcall_timer_remove(id, timer_id, completion);
208 },
209
210 .timer_enable = ^(const uint64_t id, const uint32_t timer_id,
211 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_enable__result_s)) {
212 return exclaves_driverkit_upcall_timer_enable(id, timer_id, completion);
213 },
214
215 .timer_disable = ^(const uint64_t id, const uint32_t timer_id,
216 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_disable__result_s)) {
217 return exclaves_driverkit_upcall_timer_disable(id, timer_id, completion);
218 },
219
220 .timer_set_timeout = ^(const uint64_t id, const uint32_t timer_id,
221 const struct xnuupcalls_drivertimerspecification_s *duration,
222 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_set_timeout__result_s)) {
223 return exclaves_driverkit_upcall_timer_set_timeout(id, timer_id, duration, completion);
224 },
225
226 .timer_cancel_timeout = ^(const uint64_t id, const uint32_t timer_id,
227 tb_error_t (^completion)(xnuupcalls_xnuupcalls_timer_cancel_timeout__result_s)) {
228 return exclaves_driverkit_upcall_timer_cancel_timeout(id, timer_id, completion);
229 },
230
231 .lock_wl = ^(const uint64_t id,
232 tb_error_t (^completion)(xnuupcalls_xnuupcalls_lock_wl__result_s)) {
233 return exclaves_driverkit_upcall_lock_wl(id, completion);
234 },
235
236 .unlock_wl = ^(const uint64_t id,
237 tb_error_t (^completion)(xnuupcalls_xnuupcalls_unlock_wl__result_s)) {
238 return exclaves_driverkit_upcall_unlock_wl(id, completion);
239 },
240
241 .async_notification_signal = ^(const uint64_t id,
242 const uint32_t notificationID,
243 tb_error_t (^completion)(xnuupcalls_xnuupcalls_async_notification_signal__result_s)) {
244 return exclaves_driverkit_upcall_async_notification_signal(id,
245 notificationID, completion);
246 },
247
248 .mapper_activate = ^(const uint64_t id, const uint32_t mapperIndex,
249 tb_error_t (^completion)(xnuupcalls_xnuupcalls_mapper_activate__result_s)) {
250 return exclaves_driverkit_upcall_mapper_activate(id,
251 mapperIndex, completion);
252 },
253
254 .mapper_deactivate = ^(const uint64_t id, const uint32_t mapperIndex,
255 tb_error_t (^completion)(xnuupcalls_xnuupcalls_mapper_deactivate__result_s)) {
256 return exclaves_driverkit_upcall_mapper_deactivate(id,
257 mapperIndex, completion);
258 },
259
260 .notification_signal = ^(const uint64_t id, const uint32_t mask,
261 tb_error_t (^completion)(xnuupcalls_xnuupcalls_notification_signal__result_s)) {
262 return exclaves_driverkit_upcall_notification_signal(id, mask,
263 completion);
264 },
265
266 .ane_setpowerstate = ^(const uint64_t id, const uint32_t desiredState,
267 tb_error_t (^completion)(xnuupcalls_xnuupcalls_ane_setpowerstate__result_s)) {
268 return exclaves_driverkit_upcall_ane_setpowerstate(id, desiredState,
269 completion);
270 },
271
272 .ane_worksubmit = ^(const uint64_t id, const uint64_t requestID,
273 const uint32_t taskDescriptorCount, const uint64_t submitTimestamp,
274 tb_error_t (^completion)(xnuupcalls_xnuupcalls_ane_worksubmit__result_s)) {
275 return exclaves_driverkit_upcall_ane_worksubmit(id, requestID,
276 taskDescriptorCount, submitTimestamp, completion);
277 },
278
279 .ane_workbegin = ^(const uint64_t id, const uint64_t requestID,
280 const uint64_t beginTimestamp,
281 tb_error_t (^completion)(xnuupcalls_xnuupcalls_ane_workbegin__result_s)) {
282 return exclaves_driverkit_upcall_ane_workbegin(id, requestID,
283 beginTimestamp, completion);
284 },
285
286 .ane_workend = ^(const uint64_t id, const uint64_t requestID,
287 tb_error_t (^completion)(xnuupcalls_xnuupcalls_ane_workend__result_s)) {
288 return exclaves_driverkit_upcall_ane_workend(id, requestID, completion);
289 },
290
291 .conclave_suspend = ^(const uint32_t flags,
292 tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_suspend__result_s)) {
293 return exclaves_conclave_upcall_suspend(flags, completion);
294 },
295
296 .conclave_stop = ^(const uint32_t flags,
297 tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_stop__result_s)) {
298 return exclaves_conclave_upcall_stop(flags, completion);
299 },
300 .conclave_crash_info = ^(const xnuupcalls_conclavesharedbuffer_s *shared_buf,
301 const uint32_t length,
302 tb_error_t (^completion)(xnuupcalls_xnuupcalls_conclave_crash_info__result_s)) {
303 return exclaves_conclave_upcall_crash_info(shared_buf, length, completion);
304 },
305 /* END IGNORE CODESTYLE */
306};
307
308kern_return_t
309exclaves_register_upcall_handler(exclaves_id_t upcall_id, void *upcall_context,
310 exclaves_upcall_handler_t upcall_handler)
311{
312 assert3u(upcall_id, <, NUM_XNUPROXY_UPCALLS);
313 assert(upcall_handler != NULL);
314
315 lck_mtx_assert(&exclaves_boot_lock, LCK_MTX_ASSERT_OWNED);
316 assert(exclaves_upcall_handlers[upcall_id].handler == NULL);
317
318 exclaves_upcall_handlers[upcall_id] =
319 (exclaves_upcall_handler_registration_t){
320 .handler = upcall_handler,
321 .context = upcall_context,
322 };
323
324 return KERN_SUCCESS;
325}
326
327kern_return_t
328exclaves_upcall_early_init(void)
329{
330 lck_mtx_assert(&exclaves_boot_lock, LCK_MTX_ASSERT_OWNED);
331
332#if DEVELOPMENT || DEBUG
333 kern_return_t kr;
334 kr = exclaves_register_upcall_handler(
335 XNUPROXY_UPCALL_HELLOUPCALL, NULL,
336 exclaves_test_hello_upcall_handler);
337 assert3u(kr, ==, KERN_SUCCESS);
338#endif /* DEVELOPMENT || DEBUG */
339
340 tb_endpoint_t tb_upcall_ep = tb_endpoint_create_with_value(
341 TB_TRANSPORT_TYPE_XNU, EXCLAVES_ID_TIGHTBEAM_UPCALL,
342 TB_ENDPOINT_OPTIONS_NONE);
343#pragma clang diagnostic push
344#pragma clang diagnostic ignored "-Wcast-qual" /* FIXME: rdar://103647654 */
345 tb_error_t error = xnuupcalls_xnuupcalls__server_start(tb_upcall_ep,
346 (xnuupcalls_xnuupcalls__server_s *)&exclaves_tightbeam_upcalls);
347#pragma clang diagnostic pop
348
349 return error == TB_ERROR_SUCCESS ? KERN_SUCCESS : KERN_FAILURE;
350}
351
352static kern_return_t
353exclaves_upcall_init(void)
354{
355 lck_mtx_assert(&exclaves_boot_lock, LCK_MTX_ASSERT_OWNED);
356
357#if XNUPROXY_MSG_VERSION >= 2
358 kern_return_t kkr;
359 xnuproxy_msg_t msg = {
360 .cmd = XNUPROXY_CMD_UPCALL_READY,
361 };
362 kkr = exclaves_xnu_proxy_send(&msg, NULL);
363 assert3u(kkr, ==, KERN_SUCCESS);
364#endif /* XNUPROXY_MSG_VERSION >= 2 */
365
366 return kkr;
367}
368
369EXCLAVES_BOOT_TASK(exclaves_upcall_init, EXCLAVES_BOOT_RANK_FIRST);
370
371OS_NOINLINE
372kern_return_t
373exclaves_call_upcall_handler(exclaves_id_t upcall_id)
374{
375 kern_return_t kr = KERN_INVALID_CAPABILITY;
376
377 __assert_only thread_t thread = current_thread();
378 assert3u(thread->th_exclaves_state & TH_EXCLAVES_UPCALL, !=, 0);
379
380 exclaves_tag_t tag = Exclaves_L4_GetMessageTag();
381
382 Exclaves_L4_IpcBuffer_t *ipcb = Exclaves_L4_IpcBuffer();
383 exclaves_badge_t badge = XNUPROXY_CR_UPCALL_BADGE(ipcb);
384
385 exclaves_upcall_handler_registration_t upcall_handler = {};
386
387 if (upcall_id < NUM_XNUPROXY_UPCALLS) {
388 upcall_handler = exclaves_upcall_handlers[upcall_id];
389 }
390 if (upcall_handler.handler) {
391 kr = upcall_handler.handler(upcall_handler.context, &tag, badge);
392 Exclaves_L4_SetMessageTag(tag);
393 }
394
395 return kr;
396}
397
398/* -------------------------------------------------------------------------- */
399#pragma mark Testing
400
401
402static tb_error_t
403exclaves_helloupcall(const uint64_t arg, tb_error_t (^completion)(uint64_t))
404{
405#if DEVELOPMENT || DEBUG
406 exclaves_debug_printf(show_test_output,
407 "%s: Hello Tightbeam Upcall!\n", __func__);
408 tb_error_t ret = completion(~arg);
409 STACKSHOT_TESTPOINT(TP_UPCALL);
410 /* Emit kdebug event for kperf sampling testing */
411 KDBG(BSDDBG_CODE(DBG_BSD_KDEBUG_TEST, 0xaa));
412 return ret;
413#else
414 (void)arg;
415 (void)completion;
416
417 return TB_ERROR_SUCCESS;
418#endif /* DEVELOPMENT || DEBUG */
419}
420
421#if DEVELOPMENT || DEBUG
422
423static kern_return_t
424exclaves_test_upcall_handler(void *context, exclaves_tag_t *tag,
425 exclaves_badge_t badge)
426{
427#pragma unused(context, badge)
428 Exclaves_L4_IpcBuffer_t *ipcb = Exclaves_L4_IpcBuffer();
429 assert(ipcb != NULL);
430
431 Exclaves_L4_Word_t mrs = Exclaves_L4_MessageTag_Mrs(*tag);
432 assert(mrs < Exclaves_L4_IpcBuffer_Mrs);
433 Exclaves_L4_Word_t crs = Exclaves_L4_MessageTag_Crs(*tag);
434 assert(crs == 0);
435 Exclaves_L4_Word_t label = Exclaves_L4_MessageTag_Label(*tag);
436
437 /* setup test reply message */
438 *tag = Exclaves_L4_MessageTag(mrs, 0, ~label, Exclaves_L4_False);
439 for (int i = 0; i < mrs; i++) {
440 Exclaves_L4_SetMessageMr(i, ~Exclaves_L4_GetMessageMr(i));
441 }
442
443 return KERN_SUCCESS;
444}
445
446static int
447exclaves_hello_upcall_test(__unused int64_t in, int64_t *out)
448{
449 kern_return_t kr = KERN_SUCCESS;
450
451 if (exclaves_get_status() != EXCLAVES_STATUS_AVAILABLE) {
452 exclaves_debug_printf(show_test_output,
453 "%s: SKIPPED: Exclaves not available\n", __func__);
454 *out = -1;
455 return 0;
456 }
457
458 exclaves_debug_printf(show_test_output, "%s: STARTING\n", __func__);
459
460 Exclaves_L4_IpcBuffer_t *ipcb;
461 kr = exclaves_allocate_ipc_buffer((void**)&ipcb);
462 assert(kr == KERN_SUCCESS);
463 assert(ipcb != NULL);
464
465 const Exclaves_L4_Word_t request = 0xdecafbadfeedfaceul;
466 Exclaves_L4_SetMessageMr(0, request);
467 exclaves_tag_t tag = Exclaves_L4_MessageTag(1, 0, 0x1330ul,
468 Exclaves_L4_False);
469
470 exclaves_debug_printf(show_test_output,
471 "exclaves: exclaves_endpoint_call() sending request 0x%lx, "
472 "tag 0x%llx, label 0x%lx\n", request, tag,
473 Exclaves_L4_MessageTag_Label(tag));
474
475 exclaves_error_t error;
476 kr = exclaves_endpoint_call(IPC_PORT_NULL, EXCLAVES_ID_HELLO_EXCLAVE_EP,
477 &tag, &error);
478 assert(kr == KERN_SUCCESS);
479
480 Exclaves_L4_Word_t reply = Exclaves_L4_GetMessageMr(0);
481 exclaves_debug_printf(show_test_output,
482 "exclaves: exclaves_endpoint_call() returned reply 0x%lx, "
483 "tag 0x%llx, label 0x%lx, error 0x%llx\n", reply, tag,
484 Exclaves_L4_MessageTag_Label(tag), error);
485
486 assert(error == Exclaves_L4_Success);
487 assert(Exclaves_L4_MessageTag_Mrs(tag) == 1);
488 assert(reply == ((request >> 32) | (request << 32)));
489 assert((uint16_t)Exclaves_L4_MessageTag_Label(tag) == (uint16_t)0x1331ul);
490
491 kr = exclaves_free_ipc_buffer();
492 assert(kr == KERN_SUCCESS);
493
494 exclaves_debug_printf(show_test_output, "%s: SUCCESS\n", __func__);
495 *out = 1;
496
497 return 0;
498}
499SYSCTL_TEST_REGISTER(exclaves_hello_upcall_test, exclaves_hello_upcall_test);
500
501static kern_return_t
502exclaves_test_hello_upcall_handler(void *context, exclaves_tag_t *tag,
503 exclaves_badge_t badge)
504{
505 /* HelloUpcall test handler */
506 assert(context == NULL);
507 exclaves_debug_printf(show_test_output, "%s: Hello Upcall!\n", __func__);
508 task_stop_conclave_upcall();
509 STACKSHOT_TESTPOINT(TP_UPCALL);
510 /* Emit kdebug event for kperf sampling testing */
511 KDBG(BSDDBG_CODE(DBG_BSD_KDEBUG_TEST, 0xaa));
512 return exclaves_test_upcall_handler(context, tag, badge);
513}
514#endif /* DEVELOPMENT || DEBUG */
515
516#endif /* __has_include(<Tightbeam/tightbeam.h>) */
517
518#else /* CONFIG_EXCLAVES */
519
520kern_return_t
521exclaves_call_upcall_handler(exclaves_id_t upcall_id)
522{
523 (void)upcall_id;
524 return KERN_NOT_SUPPORTED;
525}
526
527kern_return_t
528exclaves_register_upcall_handler(exclaves_id_t upcall_id, void *upcall_context,
529 exclaves_upcall_handler_t upcall_handler)
530{
531 (void)upcall_id;
532 (void)upcall_context;
533 (void)upcall_handler;
534
535 return KERN_NOT_SUPPORTED;
536}
537
538#endif /* CONFIG_EXCLAVES */
539