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#if CONFIG_EXCLAVES
30
31#if __has_include(<Tightbeam/tightbeam.h>)
32
33#include <stdint.h>
34#include <vm/pmap.h>
35
36#include <Tightbeam/tightbeam.h>
37#include <Tightbeam/tightbeam_private.h>
38
39#include <mach/exclaves.h>
40#include <sys/errno.h>
41#include <vfs/vfs_exclave_fs.h>
42#include <kern/kalloc.h>
43
44#include "kern/exclaves.tightbeam.h"
45#include "exclaves_debug.h"
46#include "exclaves_storage.h"
47#include "exclaves_boot.h"
48
49static const char *STORAGE_EXCLAVE_BUF_ID = "com.apple.named_buffer.6";
50#define STORAGE_EXCLAVE_BUF_SIZE (4 * 1024 * 1024)
51
52static int
53verify_string_length(const char *str, size_t size)
54{
55 return (strnlen(str, size) < size) ? 0 : ERANGE;
56}
57
58static int
59verify_storage_buf_offset(uint64_t buf, uint64_t length)
60{
61 uint64_t off;
62 if (__builtin_add_overflow(buf, length, &off)) {
63 return ERANGE;
64 }
65
66 if (off > STORAGE_EXCLAVE_BUF_SIZE) {
67 return ERANGE;
68 }
69
70 return 0;
71}
72
73
74/* -------------------------------------------------------------------------- */
75#pragma mark Upcalls
76
77tb_error_t
78exclaves_storage_upcall_root(const uint8_t exclaveid[_Nonnull 32],
79 tb_error_t (^completion)(xnuupcalls_xnuupcalls_root__result_s))
80{
81 exclaves_debug_printf(show_storage_upcalls,
82 "[storage_upcalls_server] root %s\n", exclaveid);
83
84 int error;
85 uint64_t rootid;
86 xnuupcalls_xnuupcalls_root__result_s result = {};
87
88 if ((error = verify_string_length((const char *)&exclaveid[0], 32))) {
89 xnuupcalls_xnuupcalls_root__result_init_failure(&result, error);
90 return completion(result);
91 }
92 error = vfs_exclave_fs_root((const char *)&exclaveid[0], &rootid);
93 if (error) {
94 exclaves_debug_printf(show_errors,
95 "[storage_upcalls_server] vfs_exclave_fs_root failed with %d\n",
96 error);
97 xnuupcalls_xnuupcalls_root__result_init_failure(&result, error);
98 } else {
99 exclaves_debug_printf(show_storage_upcalls,
100 "[storage_upcalls_server] vfs_exclave_fs_root return "
101 "rootId %lld\n", rootid);
102 xnuupcalls_xnuupcalls_root__result_init_success(&result, rootid);
103 }
104
105 return completion(result);
106}
107
108tb_error_t
109exclaves_storage_upcall_open(const enum xnuupcalls_fstag_s fstag,
110 const uint64_t rootid, const uint8_t name[_Nonnull 256],
111 tb_error_t (^completion)(xnuupcalls_xnuupcalls_open__result_s))
112{
113 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
114 "open %d %lld %s\n", fstag, rootid, name);
115 int error;
116 uint64_t fileid;
117 xnuupcalls_xnuupcalls_open__result_s result = {};
118
119 if ((error = verify_string_length((const char *)&name[0], 256))) {
120 xnuupcalls_xnuupcalls_open__result_init_failure(&result, error);
121 return completion(result);
122 }
123 error = vfs_exclave_fs_open((uint32_t)fstag, rootid,
124 (const char *)&name[0], &fileid);
125 if (error) {
126 exclaves_debug_printf(show_errors,
127 "[storage_upcalls_server] vfs_exclave_fs_open failed with %d\n",
128 error);
129 xnuupcalls_xnuupcalls_open__result_init_failure(&result, error);
130 } else {
131 exclaves_debug_printf(show_storage_upcalls,
132 "[storage_upcalls_server] vfs_exclave_fs_open return "
133 "fileId %lld\n", fileid);
134 xnuupcalls_xnuupcalls_open__result_init_success(&result, fileid);
135 }
136
137 return completion(result);
138}
139
140tb_error_t
141exclaves_storage_upcall_close(const enum xnuupcalls_fstag_s fstag,
142 const uint64_t fileid, tb_error_t (^completion)(xnuupcalls_xnuupcalls_close__result_s))
143{
144 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
145 "close %d %lld\n", fstag, fileid);
146 int error;
147 xnuupcalls_xnuupcalls_close__result_s result = {};
148
149 error = vfs_exclave_fs_close((uint32_t)fstag, fileid);
150 if (error) {
151 exclaves_debug_printf(show_errors,
152 "[storage_upcalls_server] vfs_exclave_fs_close failed with "
153 "%d\n", error);
154 xnuupcalls_xnuupcalls_close__result_init_failure(&result, error);
155 } else {
156 exclaves_debug_printf(show_storage_upcalls,
157 "[storage_upcalls_server] vfs_exclave_fs_close succeeded\n");
158 xnuupcalls_xnuupcalls_close__result_init_success(&result);
159 }
160
161 return completion(result);
162}
163
164tb_error_t
165exclaves_storage_upcall_create(const enum xnuupcalls_fstag_s fstag,
166 const uint64_t rootid, const uint8_t name[_Nonnull 256],
167 tb_error_t (^completion)(xnuupcalls_xnuupcalls_create__result_s))
168{
169 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server]"
170 " create %d %lld %s\n", fstag, rootid, name);
171 int error;
172 uint64_t fileid;
173 xnuupcalls_xnuupcalls_create__result_s result = {};
174
175 if ((error = verify_string_length((const char *)&name[0], 256))) {
176 xnuupcalls_xnuupcalls_create__result_init_failure(&result, error);
177 return completion(result);
178 }
179 error = vfs_exclave_fs_create((uint32_t)fstag, rootid,
180 (const char *)&name[0], &fileid);
181 if (error) {
182 exclaves_debug_printf(show_errors,
183 "[storage_upcalls_server] vfs_exclave_fs_create failed with"
184 " %d\n", error);
185 xnuupcalls_xnuupcalls_create__result_init_failure(&result, error);
186 } else {
187 exclaves_debug_printf(show_storage_upcalls,
188 "[storage_upcalls_server] vfs_exclave_fs_create return "
189 "fileId %lld\n", fileid);
190 xnuupcalls_xnuupcalls_create__result_init_success(&result, fileid);
191 }
192
193 return completion(result);
194}
195
196// Borrowed from bsd_init.c
197extern bool bsd_rooted_ramdisk(void);
198
199static bool
200is_restore(void)
201{
202 bool is_restore = false;
203 (void) PE_parse_boot_argn("-restore", &is_restore, sizeof(is_restore));
204 return is_restore;
205}
206
207static exclaves_resource_t *storage_resource = NULL;
208static kern_return_t
209exclaves_storage_init(void)
210{
211 kern_return_t kr = exclaves_named_buffer_map(
212 EXCLAVES_DOMAIN_KERNEL, STORAGE_EXCLAVE_BUF_ID,
213 STORAGE_EXCLAVE_BUF_SIZE,
214 EXCLAVES_BUFFER_PERM_READ | EXCLAVES_BUFFER_PERM_WRITE,
215 &storage_resource);
216 if (kr != KERN_SUCCESS) {
217 exclaves_debug_printf(show_errors,
218 "[storage_upcalls] exclaves_named_buffer_map failed with %d\n", kr);
219 if (is_restore() || bsd_rooted_ramdisk()) {
220 // Don't fail boot here. Fail the upcalls that try to use the sharemem buffer instead.
221 // This is to prevent panic during boot-time when xnu-proxy was initialized before StorageExclave
222 // This can be reverted once Storage switched to V2
223 storage_resource = NULL;
224 } else {
225 return kr;
226 }
227 }
228 return KERN_SUCCESS;
229}
230EXCLAVES_BOOT_TASK(exclaves_storage_init, EXCLAVES_BOOT_RANK_SECOND);
231
232tb_error_t
233exclaves_storage_upcall_read(const enum xnuupcalls_fstag_s fstag,
234 const uint64_t fileid, const struct xnuupcalls_iodesc_s *descriptor,
235 tb_error_t (^completion)(xnuupcalls_xnuupcalls_read__result_s))
236{
237 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
238 "read %d %lld %lld %lld %lld\n", fstag, fileid, descriptor->buf,
239 descriptor->fileoffset, descriptor->length);
240 int error;
241
242 xnuupcalls_xnuupcalls_read__result_s result = {};
243
244 if (!storage_resource) {
245 exclaves_debug_printf(show_errors,
246 "[storage_upcalls] shared memory buffer not initialized\n");
247 xnuupcalls_xnuupcalls_read__result_init_failure(&result, ENOMEM);
248 return completion(result);
249 }
250
251 error = verify_storage_buf_offset(descriptor->buf, descriptor->length);
252 if (error != 0) {
253 xnuupcalls_xnuupcalls_read__result_init_failure(&result, error);
254 return completion(result);
255 }
256
257 __block uint64_t off = descriptor->fileoffset;
258 error = exclaves_named_buffer_io(storage_resource, descriptor->buf,
259 descriptor->length, ^(char *buffer, size_t size) {
260 int ret = vfs_exclave_fs_read((uint32_t)fstag,
261 fileid, off, size, buffer);
262 off += size;
263 return ret;
264 });
265
266 if (error) {
267 exclaves_debug_printf(show_errors,
268 "[storage_upcalls_server] vfs_exclave_fs_read "
269 "failed with %d\n", error);
270 xnuupcalls_xnuupcalls_read__result_init_failure(&result, error);
271 } else {
272 exclaves_debug_printf(show_storage_upcalls,
273 "[storage_upcalls_server] vfs_exclave_fs_read succeeded\n");
274 xnuupcalls_xnuupcalls_read__result_init_success(&result);
275 }
276
277 return completion(result);
278}
279
280tb_error_t
281exclaves_storage_upcall_write(const enum xnuupcalls_fstag_s fstag,
282 const uint64_t fileid, const struct xnuupcalls_iodesc_s *descriptor,
283 tb_error_t (^completion)(xnuupcalls_xnuupcalls_write__result_s))
284{
285 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
286 "write %d %lld %lld %lld %lld\n", fstag, fileid, descriptor->buf,
287 descriptor->fileoffset, descriptor->length);
288 int error;
289
290 xnuupcalls_xnuupcalls_write__result_s result = {};
291
292 if (!storage_resource) {
293 exclaves_debug_printf(show_errors,
294 "[storage_upcalls] shared memory buffer not initialized\n");
295 xnuupcalls_xnuupcalls_write__result_init_failure(&result, ENOMEM);
296 return completion(result);
297 }
298
299 error = verify_storage_buf_offset(descriptor->buf, descriptor->length);
300 if (error != 0) {
301 xnuupcalls_xnuupcalls_write__result_init_failure(&result, error);
302 return completion(result);
303 }
304
305
306 __block uint64_t off = descriptor->fileoffset;
307 error = exclaves_named_buffer_io(storage_resource, descriptor->buf,
308 descriptor->length, ^(char *buffer, size_t size) {
309 int ret = vfs_exclave_fs_write((uint32_t)fstag,
310 fileid, off, size, buffer);
311 off += size;
312 return ret;
313 });
314
315 if (error) {
316 exclaves_debug_printf(show_errors,
317 "[storage_upcalls_server] vfs_exclave_fs_write "
318 "failed with %d\n", error);
319 xnuupcalls_xnuupcalls_write__result_init_failure(&result, error);
320 } else {
321 exclaves_debug_printf(show_storage_upcalls,
322 "[storage_upcalls_server] vfs_exclave_fs_write succeeded\n");
323 xnuupcalls_xnuupcalls_write__result_init_success(&result);
324 }
325
326 return completion(result);
327}
328
329tb_error_t
330exclaves_storage_upcall_remove(const enum xnuupcalls_fstag_s fstag,
331 const uint64_t rootid, const uint8_t name[_Nonnull 256],
332 tb_error_t (^completion)(xnuupcalls_xnuupcalls_remove__result_s))
333{
334 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
335 "remove %d %lld %s\n", fstag, rootid, name);
336 int error;
337 xnuupcalls_xnuupcalls_remove__result_s result = {};
338
339 if ((error = verify_string_length((const char *)&name[0], 256))) {
340 xnuupcalls_xnuupcalls_remove__result_init_failure(&result, error);
341 return completion(result);
342 }
343 error = vfs_exclave_fs_remove((uint32_t)fstag, rootid,
344 (const char *)&name[0]);
345 if (error) {
346 exclaves_debug_printf(show_errors,
347 "[storage_upcalls_server] vfs_exclave_fs_remove failed with "
348 "%d\n", error);
349 xnuupcalls_xnuupcalls_remove__result_init_failure(&result, error);
350 } else {
351 exclaves_debug_printf(show_storage_upcalls,
352 "[storage_upcalls_server] vfs_exclave_fs_remove succeeded\n");
353 xnuupcalls_xnuupcalls_remove__result_init_success(&result);
354 }
355
356 return completion(result);
357}
358
359tb_error_t
360exclaves_storage_upcall_sync(const enum xnuupcalls_fstag_s fstag,
361 const enum xnuupcalls_syncop_s op,
362 const uint64_t fileid, tb_error_t (^completion)(xnuupcalls_xnuupcalls_sync__result_s))
363{
364 exclaves_debug_printf(show_storage_upcalls, "[storage_upcalls_server] "
365 "sync %d %lld %d\n", fstag, fileid, (int)op);
366 int error;
367 xnuupcalls_xnuupcalls_sync__result_s result = {};
368
369 error = vfs_exclave_fs_sync((uint32_t)fstag, fileid, op);
370 if (error) {
371 exclaves_debug_printf(show_errors,
372 "[storage_upcalls_server] vfs_exclave_fs_sync failed with %d\n",
373 error);
374 xnuupcalls_xnuupcalls_sync__result_init_failure(&result, error);
375 } else {
376 exclaves_debug_printf(show_storage_upcalls,
377 "[storage_upcalls_server] vfs_exclave_fs_sync succeeded\n");
378 xnuupcalls_xnuupcalls_sync__result_init_success(&result);
379 }
380
381 return completion(result);
382}
383
384tb_error_t
385exclaves_storage_upcall_readdir(const enum xnuupcalls_fstag_s fstag,
386 const uint64_t fileid, const uint64_t buf,
387 const uint32_t length, tb_error_t (^completion)(xnuupcalls_xnuupcalls_readdir__result_s))
388{
389 exclaves_debug_printf(show_storage_upcalls,
390 "[storage_upcalls_server] readdir %d %lld %lld %d\n",
391 fstag, fileid, buf, length);
392 int error;
393 int32_t count;
394
395 xnuupcalls_xnuupcalls_readdir__result_s result = {};
396
397 if (!storage_resource) {
398 exclaves_debug_printf(show_errors,
399 "[storage_upcalls] shared memory buffer not initialized\n");
400 xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, ENOMEM);
401 return completion(result);
402 }
403
404 if ((error = verify_storage_buf_offset(buf, length))) {
405 xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, error);
406 return completion(result);
407 }
408
409 /*
410 * When we are able to map exclaves shared memory into kernel
411 * virtual address space, this temporary buffer will no longer
412 * be required.
413 */
414 char *tmpbuf = kalloc_data(length, Z_WAITOK | Z_ZERO | Z_NOFAIL);
415 error = vfs_exclave_fs_readdir((uint32_t)fstag, fileid,
416 tmpbuf, length, &count);
417 assert3u(error, ==, 0);
418
419 __block char *p = tmpbuf;
420 error = exclaves_named_buffer_io(storage_resource, buf, length,
421 ^(char *buffer, size_t size) {
422 memcpy(buffer, p, size);
423 p += size;
424 return 0;
425 });
426 assert3u(error, ==, 0);
427 kfree_data(tmpbuf, length);
428
429 if (error) {
430 exclaves_debug_printf(show_errors,
431 "[storage_upcalls_server] vfs_exclave_fs_readdir "
432 "failed with %d\n", error);
433 xnuupcalls_xnuupcalls_readdir__result_init_failure(&result, error);
434 } else {
435 exclaves_debug_printf(show_storage_upcalls,
436 "[storage_upcalls_server] vfs_exclave_fs_readdir succeeded\n");
437 xnuupcalls_xnuupcalls_readdir__result_init_success(&result, count);
438 }
439
440 return completion(result);
441}
442
443tb_error_t
444exclaves_storage_upcall_getsize(const enum xnuupcalls_fstag_s fstag,
445 const uint64_t fileid,
446 tb_error_t (^completion)(xnuupcalls_xnuupcalls_getsize__result_s result))
447{
448 exclaves_debug_printf(show_storage_upcalls,
449 "[storage_upcalls_server] getsize %d %lld\n",
450 fstag, fileid);
451 int error;
452 uint64_t size;
453 xnuupcalls_xnuupcalls_getsize__result_s result = {};
454
455 error = vfs_exclave_fs_getsize((uint32_t)fstag, fileid, &size);
456 if (error) {
457 exclaves_debug_printf(show_errors,
458 "[storage_upcalls_server] vfs_exclave_fs_getsize(%d, %lld) "
459 "failed with %d\n", fstag, fileid, error);
460 xnuupcalls_xnuupcalls_getsize__result_init_failure(&result, error);
461 } else {
462 exclaves_debug_printf(show_storage_upcalls,
463 "[storage_upcalls_server] vfs_exclave_fs_getsize succeeded\n");
464 xnuupcalls_xnuupcalls_getsize__result_init_success(&result, size);
465 }
466
467 return completion(result);
468}
469
470tb_error_t
471exclaves_storage_upcall_sealstate(const enum xnuupcalls_fstag_s fstag,
472 tb_error_t (^completion)(xnuupcalls_xnuupcalls_sealstate__result_s result))
473{
474 exclaves_debug_printf(show_storage_upcalls,
475 "[storage_upcalls_server] sealstate %d\n",
476 fstag);
477 int error;
478 bool sealed;
479 xnuupcalls_xnuupcalls_sealstate__result_s result = {};
480
481 error = vfs_exclave_fs_sealstate((uint32_t)fstag, &sealed);
482 if (error) {
483 exclaves_debug_printf(show_errors,
484 "[storage_upcalls_server] vfs_exclave_fs_sealstate(%d) "
485 "failed with %d\n", fstag, error);
486 xnuupcalls_xnuupcalls_sealstate__result_init_failure(&result, error);
487 } else {
488 exclaves_debug_printf(show_storage_upcalls,
489 "[storage_upcalls_server] vfs_exclave_fs_sealstate succeeded\n");
490 xnuupcalls_xnuupcalls_sealstate__result_init_success(&result, sealed);
491 }
492
493 return completion(result);
494}
495
496#endif /* __has_include(<Tightbeam/tightbeam.h>) */
497
498#endif /* CONFIG_EXCLAVES */
499