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 | |
49 | static const char *STORAGE_EXCLAVE_BUF_ID = "com.apple.named_buffer.6" ; |
50 | #define STORAGE_EXCLAVE_BUF_SIZE (4 * 1024 * 1024) |
51 | |
52 | static int |
53 | verify_string_length(const char *str, size_t size) |
54 | { |
55 | return (strnlen(str, size) < size) ? 0 : ERANGE; |
56 | } |
57 | |
58 | static int |
59 | verify_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 | |
77 | tb_error_t |
78 | exclaves_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 | |
108 | tb_error_t |
109 | exclaves_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 | |
140 | tb_error_t |
141 | exclaves_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 | |
164 | tb_error_t |
165 | exclaves_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 |
197 | extern bool bsd_rooted_ramdisk(void); |
198 | |
199 | static bool |
200 | is_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 | |
207 | static exclaves_resource_t *storage_resource = NULL; |
208 | static kern_return_t |
209 | exclaves_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 | } |
230 | EXCLAVES_BOOT_TASK(exclaves_storage_init, EXCLAVES_BOOT_RANK_SECOND); |
231 | |
232 | tb_error_t |
233 | exclaves_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 | |
280 | tb_error_t |
281 | exclaves_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 | |
329 | tb_error_t |
330 | exclaves_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 | |
359 | tb_error_t |
360 | exclaves_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 | |
384 | tb_error_t |
385 | exclaves_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 | |
443 | tb_error_t |
444 | exclaves_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 | |
470 | tb_error_t |
471 | exclaves_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 | |