1 /*
2 * Copyright (c) 2024 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 #if CONFIG_EXCLAVES
30
31 #include <vm/pmap.h>
32
33 #include <vm/vm_page_internal.h>
34 #include <vm/vm_object_xnu.h>
35 #include <vm/vm_pageout_xnu.h>
36 #include <vm/vm_kern_xnu.h>
37 #include <vm/vm_map_xnu.h>
38 #include <vm/vm_memory_entry_xnu.h>
39 #include <vm/vm_protos.h>
40
41 #include <mach/mach_vm.h>
42 #include <mach/mach_host.h>
43
44 #include <device/device_port.h>
45
46 #include <kern/ipc_kobject.h>
47
48 #include <libkern/coreanalytics/coreanalytics.h>
49 #include <kern/ledger.h>
50
51 #include "exclaves_memory.h"
52
53 /* -------------------------------------------------------------------------- */
54 #pragma mark Accounting
55
56 typedef struct {
57 _Atomic uint64_t pages_alloced;
58 _Atomic uint64_t pages_freed;
59 _Atomic uint64_t time_allocating;
60 _Atomic uint64_t max_alloc_latency;
61 _Atomic uint64_t alloc_latency_byhighbit[16];// highbit(MCT end - MCT start)/4
62 } exclaves_allocation_statistics_t;
63
64 exclaves_allocation_statistics_t exclaves_allocation_statistics;
65
66 CA_EVENT(ca_exclaves_allocation_statistics,
67 CA_INT, pages_alloced,
68 CA_INT, pages_freed,
69 CA_INT, time_allocating,
70 CA_INT, max_alloc_latency,
71 CA_INT, alloc_latency_highbit0,
72 CA_INT, alloc_latency_highbit1,
73 CA_INT, alloc_latency_highbit2,
74 CA_INT, alloc_latency_highbit3,
75 CA_INT, alloc_latency_highbit4,
76 CA_INT, alloc_latency_highbit5,
77 CA_INT, alloc_latency_highbit6,
78 CA_INT, alloc_latency_highbit7,
79 CA_INT, alloc_latency_highbit8,
80 CA_INT, alloc_latency_highbit9,
81 CA_INT, alloc_latency_highbit10,
82 CA_INT, alloc_latency_highbit11,
83 CA_INT, alloc_latency_highbit12,
84 CA_INT, alloc_latency_highbit13,
85 CA_INT, alloc_latency_highbit14,
86 CA_INT, alloc_latency_highbit15);
87
88 void
exclaves_memory_report_accounting(void)89 exclaves_memory_report_accounting(void)
90 {
91 ca_event_t event = CA_EVENT_ALLOCATE(ca_exclaves_allocation_statistics);
92 CA_EVENT_TYPE(ca_exclaves_allocation_statistics) * e = event->data;
93
94 e->pages_alloced = os_atomic_load(&exclaves_allocation_statistics.pages_alloced, relaxed);
95 e->pages_freed = os_atomic_load(&exclaves_allocation_statistics.pages_freed, relaxed);
96 e->time_allocating = os_atomic_load(&exclaves_allocation_statistics.time_allocating, relaxed);
97 e->max_alloc_latency = os_atomic_load(&exclaves_allocation_statistics.max_alloc_latency, relaxed);
98 e->alloc_latency_highbit0 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[0], relaxed);
99 e->alloc_latency_highbit1 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[1], relaxed);
100 e->alloc_latency_highbit2 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[2], relaxed);
101 e->alloc_latency_highbit3 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[3], relaxed);
102 e->alloc_latency_highbit4 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[4], relaxed);
103 e->alloc_latency_highbit5 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[5], relaxed);
104 e->alloc_latency_highbit6 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[6], relaxed);
105 e->alloc_latency_highbit7 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[7], relaxed);
106 e->alloc_latency_highbit8 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[8], relaxed);
107 e->alloc_latency_highbit9 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[9], relaxed);
108 e->alloc_latency_highbit10 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[10], relaxed);
109 e->alloc_latency_highbit11 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[11], relaxed);
110 e->alloc_latency_highbit12 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[12], relaxed);
111 e->alloc_latency_highbit13 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[13], relaxed);
112 e->alloc_latency_highbit14 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[14], relaxed);
113 e->alloc_latency_highbit15 = os_atomic_load(&exclaves_allocation_statistics.alloc_latency_byhighbit[15], relaxed);
114
115 CA_EVENT_SEND(event);
116 }
117
118 static_assert(
119 (EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN == XNUUPCALLS_PAGEKIND_ROOTDOMAIN) &&
120 (EXCLAVES_MEMORY_PAGEKIND_CONCLAVE == XNUUPCALLS_PAGEKIND_CONCLAVE),
121 "xnuupcalls_pagekind_s mismatch");
122 static_assert(
123 (EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN == XNUUPCALLSV2_PAGEKIND_ROOTDOMAIN) &&
124 (EXCLAVES_MEMORY_PAGEKIND_CONCLAVE == XNUUPCALLSV2_PAGEKIND_CONCLAVE),
125 "xnuupcallsv2_pagekind_s mismatch");
126
127 static ledger_t
get_conclave_mem_ledger(exclaves_memory_pagekind_t kind)128 get_conclave_mem_ledger(exclaves_memory_pagekind_t kind)
129 {
130 ledger_t ledger;
131 switch (kind) {
132 case EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN:
133 ledger = kernel_task->ledger;
134 break;
135 case EXCLAVES_MEMORY_PAGEKIND_CONCLAVE:
136 if (current_thread()->conclave_stop_task != NULL) {
137 ledger = current_thread()->conclave_stop_task->ledger;
138 } else {
139 ledger = current_task()->ledger;
140 }
141 break;
142 default:
143 panic("Conclave Memory ledger doesn't recognize pagekind");
144 break;
145 }
146 return ledger;
147 }
148
149
150 /* -------------------------------------------------------------------------- */
151 #pragma mark Allocation/Free
152
153 void
exclaves_memory_alloc(const uint32_t npages,uint32_t * pages,const exclaves_memory_pagekind_t kind,const exclaves_memory_page_flags_t flags)154 exclaves_memory_alloc(const uint32_t npages, uint32_t *pages, const exclaves_memory_pagekind_t kind, const exclaves_memory_page_flags_t flags)
155 {
156 uint32_t pages_left = npages;
157 vm_page_t page_list = NULL;
158 vm_page_t sequestered = NULL;
159 unsigned p = 0;
160
161 uint64_t start_time = mach_continuous_approximate_time();
162 kma_flags_t kma_flags = KMA_ZERO | KMA_NOFAIL;
163 vm_object_t vm_obj = exclaves_object;
164
165 (void)flags;
166
167 while (pages_left) {
168 vm_page_t next;
169 vm_page_alloc_list(pages_left, kma_flags, &page_list);
170
171 vm_object_lock(vm_obj);
172 for (vm_page_t mem = page_list; mem != VM_PAGE_NULL; mem = next) {
173 next = mem->vmp_snext;
174 if (!vm_page_in_array(mem)) {
175 // avoid ml_static_mfree() pages due to 117505258
176 mem->vmp_snext = sequestered;
177 sequestered = mem;
178 continue;
179 }
180 mem->vmp_snext = NULL;
181
182 vm_page_lock_queues();
183 vm_page_wire(mem, VM_KERN_MEMORY_EXCLAVES, FALSE);
184 vm_page_unlock_queues();
185 /* Insert the page into the exclaves object */
186 vm_page_insert_wired(mem, vm_obj,
187 ptoa(VM_PAGE_GET_PHYS_PAGE(mem)),
188 VM_KERN_MEMORY_EXCLAVES);
189
190 /* Retype via SPTM to SK owned */
191 sptm_retype_params_t retype_params = {
192 .raw = SPTM_RETYPE_PARAMS_NULL
193 };
194 sptm_retype(ptoa(VM_PAGE_GET_PHYS_PAGE(mem)),
195 XNU_DEFAULT, SK_DEFAULT, retype_params);
196
197 pages[p++] = VM_PAGE_GET_PHYS_PAGE(mem);
198 pages_left--;
199 }
200 vm_object_unlock(vm_obj);
201 }
202
203 vm_page_free_list(sequestered, FALSE);
204
205 uint64_t elapsed_time = mach_continuous_approximate_time() - start_time;
206
207 os_atomic_add(&exclaves_allocation_statistics.pages_alloced, npages, relaxed);
208 os_atomic_add(&exclaves_allocation_statistics.time_allocating, elapsed_time, relaxed);
209 os_atomic_max(&exclaves_allocation_statistics.max_alloc_latency, elapsed_time, relaxed);
210 os_atomic_add(&exclaves_allocation_statistics.alloc_latency_byhighbit[ffsll(elapsed_time) / 4], elapsed_time, relaxed);
211
212 ledger_t ledger = get_conclave_mem_ledger(kind);
213 kern_return_t ledger_ret = ledger_credit(ledger,
214 task_ledgers.conclave_mem,
215 (ledger_amount_t) (npages * PAGE_SIZE));
216 if (ledger_ret != KERN_SUCCESS) {
217 panic("Ledger credit failed. count %u error code %d",
218 npages,
219 ledger_ret);
220 }
221 }
222
223 void
exclaves_memory_free(const uint32_t npages,const uint32_t * pages,const exclaves_memory_pagekind_t kind,const exclaves_memory_page_flags_t flags)224 exclaves_memory_free(const uint32_t npages, const uint32_t *pages, const exclaves_memory_pagekind_t kind, const exclaves_memory_page_flags_t flags)
225 {
226 vm_object_t vm_obj = exclaves_object;
227 (void)flags;
228
229 vm_object_lock(vm_obj);
230 for (size_t p = 0; p < npages; p++) {
231 /* Find the page in the exclaves object. */
232 vm_page_t m;
233 m = vm_page_lookup(vm_obj, ptoa(pages[p]));
234
235 /* Assert we found the page */
236 assert(m != VM_PAGE_NULL);
237
238 /* Via SPTM, verify the page type is something ownable by xnu. */
239 assert3u(sptm_get_frame_type(ptoa(VM_PAGE_GET_PHYS_PAGE(m))),
240 ==, XNU_DEFAULT);
241
242
243 /* Free the page */
244 vm_page_lock_queues();
245 vm_page_free(m);
246 vm_page_unlock_queues();
247 }
248 vm_object_unlock(vm_obj);
249
250 os_atomic_add(&exclaves_allocation_statistics.pages_freed, npages, relaxed);
251
252 ledger_t ledger = get_conclave_mem_ledger(kind);
253 kern_return_t ledger_ret = ledger_debit(ledger,
254 task_ledgers.conclave_mem,
255 (ledger_amount_t) (npages * PAGE_SIZE));
256 if (ledger_ret != KERN_SUCCESS) {
257 panic("Ledger debit failed. count %u error code %d",
258 npages,
259 ledger_ret);
260 }
261 }
262
263 static void
validate_for_mapping(uint32_t page,vm_prot_t prot)264 validate_for_mapping(uint32_t page, vm_prot_t prot)
265 {
266 const sptm_frame_type_t type = sptm_get_frame_type(ptoa(page));
267
268 // Mapping RW and type is SK_SHARED_RW.
269 if (type == SK_SHARED_RW && (prot & VM_PROT_WRITE) != 0) {
270 return;
271 }
272
273 // Mapping RO and type is SK_SHARED_RW or SH_SHARED_RO
274 if ((type == SK_SHARED_RW || type == SK_SHARED_RO) &&
275 (prot & VM_PROT_WRITE) == 0) {
276 return;
277 }
278
279 // Mismatch of type and prot
280 panic("trying to map exclaves memory (prot: %u) "
281 "but memory is of the wrong type (%u)", prot, type);
282 }
283
284 kern_return_t
exclaves_memory_map(uint32_t npages,const uint32_t * pages,vm_prot_t prot,char ** address)285 exclaves_memory_map(uint32_t npages, const uint32_t *pages, vm_prot_t prot,
286 char **address)
287 {
288 assert3u(npages, >, 0);
289
290 kern_return_t kr = KERN_FAILURE;
291 const vm_map_kernel_flags_t vmk_flags = {
292 .vmf_fixed = false,
293 .vm_tag = VM_KERN_MEMORY_EXCLAVES_SHARED,
294 };
295 const vm_size_t size = npages * PAGE_SIZE;
296
297 memory_object_t pager = device_pager_setup((memory_object_t)NULL,
298 (uintptr_t)NULL, size, DEVICE_PAGER_COHERENT);
299 assert3p(pager, !=, NULL);
300
301 for (uint32_t i = 0; i < npages; i++) {
302 validate_for_mapping(pages[i], prot);
303
304 kr = device_pager_populate_object(pager, ptoa(i), pages[i],
305 PAGE_SIZE);
306 if (kr != KERN_SUCCESS) {
307 device_pager_deallocate(pager);
308 return kr;
309 }
310 }
311
312 ipc_port_t entry = IPC_PORT_NULL;
313 kr = mach_memory_object_memory_entry_64((host_t)1, false, size,
314 prot, pager, &entry);
315 if (kr != KERN_SUCCESS) {
316 device_pager_deallocate(pager);
317 return kr;
318 }
319
320 kr = mach_vm_map_kernel(kernel_map, (mach_vm_offset_ut *)address, size, 0, vmk_flags, entry,
321 0, FALSE, prot, prot, VM_INHERIT_DEFAULT);
322
323 mach_memory_entry_port_release(entry);
324
325 if (kr != KERN_SUCCESS) {
326 device_pager_deallocate(pager);
327 return kr;
328 }
329
330 device_pager_deallocate(pager);
331
332 /*
333 * Wire the memory so that it's paged-in up-front. This memory is
334 * already wired via exclaves_memory_alloc.
335 */
336 const vm_map_offset_ut start = *(vm_map_offset_ut *)address;
337 kr = vm_map_wire_kernel(kernel_map, start, start + size, prot,
338 VM_KERN_MEMORY_EXCLAVES_SHARED, false);
339 if (kr != KERN_SUCCESS) {
340 mach_vm_deallocate(kernel_map, start, size);
341 return kr;
342 }
343
344 return KERN_SUCCESS;
345 }
346
347 kern_return_t
exclaves_memory_unmap(char * address,size_t size)348 exclaves_memory_unmap(char *address, size_t size)
349 {
350 kern_return_t kr = KERN_FAILURE;
351
352 const vm_map_offset_ut start = (vm_map_offset_ut)address;
353 kr = vm_map_unwire(kernel_map, start, start + size, false);
354 if (kr != KERN_SUCCESS) {
355 return kr;
356 }
357
358 kr = mach_vm_deallocate(kernel_map, (mach_vm_address_t)address, size);
359 if (kr != KERN_SUCCESS) {
360 return kr;
361 }
362
363 return KERN_SUCCESS;
364 }
365
366 /* -------------------------------------------------------------------------- */
367 #pragma mark Upcalls
368
369 /* Legacy upcall handlers */
370
371 tb_error_t
372 exclaves_memory_upcall_legacy_alloc(uint32_t npages, xnuupcalls_pagekind_s kind,
373 tb_error_t (^completion)(xnuupcalls_pagelist_s))
374 {
375 xnuupcalls_pagelist_s pagelist = {};
376
377 assert3u(npages, <=, ARRAY_COUNT(pagelist.pages));
378 if (npages > ARRAY_COUNT(pagelist.pages)) {
379 panic("npages");
380 }
381
382 exclaves_memory_alloc(npages, pagelist.pages,
383 (exclaves_memory_pagekind_t) kind,
384 EXCLAVES_MEMORY_PAGE_FLAGS_NONE);
385 return completion(pagelist);
386 }
387
388 tb_error_t
389 exclaves_memory_upcall_legacy_alloc_ext(uint32_t npages, xnuupcalls_pageallocflags_s flags,
390 tb_error_t (^completion)(xnuupcalls_pagelist_s))
391 {
392 xnuupcalls_pagelist_s pagelist = {};
393 exclaves_memory_pagekind_t kind = EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN;
394 exclaves_memory_page_flags_t alloc_flags = EXCLAVES_MEMORY_PAGE_FLAGS_NONE;
395
396 assert3u(npages, <=, ARRAY_COUNT(pagelist.pages));
397 if (npages > ARRAY_COUNT(pagelist.pages)) {
398 panic("npages");
399 }
400
401 if (flags & XNUUPCALLS_PAGEALLOCFLAGS_CONCLAVE) {
402 kind = EXCLAVES_MEMORY_PAGEKIND_CONCLAVE;
403 }
404 exclaves_memory_alloc(npages, pagelist.pages, kind, alloc_flags);
405 return completion(pagelist);
406 }
407
408
409 tb_error_t
410 exclaves_memory_upcall_legacy_free(const uint32_t pages[EXCLAVES_MEMORY_MAX_REQUEST],
411 uint32_t npages, const xnuupcalls_pagekind_s kind,
412 tb_error_t (^completion)(void))
413 {
414 /* Get pointer for page list paddr */
415 assert(npages <= EXCLAVES_MEMORY_MAX_REQUEST);
416 if (npages > EXCLAVES_MEMORY_MAX_REQUEST) {
417 panic("npages");
418 }
419
420 exclaves_memory_free(npages, pages, (exclaves_memory_pagekind_t) kind, EXCLAVES_MEMORY_PAGE_FLAGS_NONE);
421
422 return completion();
423 }
424
425 tb_error_t
426 exclaves_memory_upcall_legacy_free_ext(const uint32_t pages[EXCLAVES_MEMORY_MAX_REQUEST],
427 uint32_t npages, const xnuupcalls_pagefreeflags_s flags,
428 tb_error_t (^completion)(void))
429 {
430 exclaves_memory_pagekind_t kind = EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN;
431 exclaves_memory_page_flags_t free_flags = EXCLAVES_MEMORY_PAGE_FLAGS_NONE;
432 /* Get pointer for page list paddr */
433 assert(npages <= EXCLAVES_MEMORY_MAX_REQUEST);
434 if (npages > EXCLAVES_MEMORY_MAX_REQUEST) {
435 panic("npages");
436 }
437 if (flags & XNUUPCALLS_PAGEALLOCFLAGS_CONCLAVE) {
438 kind = EXCLAVES_MEMORY_PAGEKIND_CONCLAVE;
439 }
440
441 exclaves_memory_free(npages, pages, kind, free_flags);
442
443 return completion();
444 }
445
446 /* Upcall handlers */
447
448 tb_error_t
449 exclaves_memory_upcall_alloc(uint32_t npages, xnuupcallsv2_pagekind_s kind,
450 tb_error_t (^completion)(xnuupcallsv2_pagelist_s))
451 {
452 uint32_t pages[EXCLAVES_MEMORY_MAX_REQUEST];
453 xnuupcallsv2_pagelist_s pagelist = {};
454
455 assert3u(npages, <=, EXCLAVES_MEMORY_MAX_REQUEST);
456 if (npages > EXCLAVES_MEMORY_MAX_REQUEST) {
457 panic("npages");
458 }
459
460 exclaves_memory_alloc(npages, pages,
461 (exclaves_memory_pagekind_t) kind,
462 EXCLAVES_MEMORY_PAGE_FLAGS_NONE);
463
464 u32__v_assign_unowned(&pagelist, pages, npages);
465
466 return completion(pagelist);
467 }
468
469 tb_error_t
470 exclaves_memory_upcall_alloc_ext(uint32_t npages, xnuupcallsv2_pageallocflagsv2_s flags,
471 tb_error_t (^completion)(xnuupcallsv2_pagelist_s))
472 {
473 uint32_t pages[EXCLAVES_MEMORY_MAX_REQUEST];
474 xnuupcallsv2_pagelist_s pagelist = {};
475 exclaves_memory_pagekind_t kind = EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN;
476 exclaves_memory_page_flags_t alloc_flags = EXCLAVES_MEMORY_PAGE_FLAGS_NONE;
477
478 assert3u(npages, <=, EXCLAVES_MEMORY_MAX_REQUEST);
479 if (npages > EXCLAVES_MEMORY_MAX_REQUEST) {
480 panic("npages");
481 }
482
483 if (flags & XNUUPCALLSV2_PAGEALLOCFLAGSV2_CONCLAVE) {
484 kind = EXCLAVES_MEMORY_PAGEKIND_CONCLAVE;
485 }
486
487 exclaves_memory_alloc(npages, pages, kind, alloc_flags);
488
489 u32__v_assign_unowned(&pagelist, pages, npages);
490
491 return completion(pagelist);
492 }
493
494
495 tb_error_t
496 exclaves_memory_upcall_free(const xnuupcallsv2_pagelist_s pages,
497 const xnuupcallsv2_pagekind_s kind, tb_error_t (^completion)(void))
498 {
499 uint32_t _pages[EXCLAVES_MEMORY_MAX_REQUEST];
500 uint32_t *pages_ptr = _pages;
501 uint32_t __block npages = 0;
502
503 u32__v_visit(&pages, ^(size_t i, const uint32_t page) {
504 if (++npages > EXCLAVES_MEMORY_MAX_REQUEST) {
505 panic("npages");
506 }
507 pages_ptr[i] = page;
508 });
509
510 exclaves_memory_free(npages, _pages, (exclaves_memory_pagekind_t) kind, EXCLAVES_MEMORY_PAGE_FLAGS_NONE);
511
512 return completion();
513 }
514
515 tb_error_t
516 exclaves_memory_upcall_free_ext(const xnuupcallsv2_pagelist_s pages,
517 const xnuupcallsv2_pagefreeflagsv2_s flags, tb_error_t (^completion)(void))
518 {
519 uint32_t _pages[EXCLAVES_MEMORY_MAX_REQUEST];
520 uint32_t *pages_ptr = _pages;
521 uint32_t __block npages = 0;
522 exclaves_memory_pagekind_t kind = EXCLAVES_MEMORY_PAGEKIND_ROOTDOMAIN;
523 exclaves_memory_page_flags_t free_flags = EXCLAVES_MEMORY_PAGE_FLAGS_NONE;
524
525 u32__v_visit(&pages, ^(size_t i, const uint32_t page) {
526 if (++npages > EXCLAVES_MEMORY_MAX_REQUEST) {
527 panic("npages");
528 }
529 pages_ptr[i] = page;
530 });
531
532 if (flags & XNUUPCALLSV2_PAGEFREEFLAGSV2_CONCLAVE) {
533 kind = EXCLAVES_MEMORY_PAGEKIND_CONCLAVE;
534 }
535
536 exclaves_memory_free(npages, _pages, kind, free_flags);
537
538 return completion();
539 }
540
541 #endif /* CONFIG_EXCLAVES */
542