]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/imageboot.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
1 /*
2 * Copyright (c) 2006 Apple Computer, 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 <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/proc_internal.h>
32 #include <sys/systm.h>
33 #include <sys/systm.h>
34 #include <sys/mount_internal.h>
35 #include <sys/filedesc.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/imageboot.h>
38 #include <kern/assert.h>
39
40 #include <sys/namei.h>
41 #include <sys/fcntl.h>
42 #include <sys/vnode.h>
43 #include <sys/sysproto.h>
44 #include <sys/csr.h>
45 #include <miscfs/devfs/devfsdefs.h>
46 #include <libkern/crypto/sha2.h>
47 #include <libkern/crypto/rsa.h>
48 #include <libkern/OSKextLibPrivate.h>
49
50 #if CONFIG_IMAGEBOOT_IMG4
51 #include <libkern/img4/interface.h>
52 #include <img4/img4.h>
53 #endif
54
55 #include <kern/kalloc.h>
56
57 #include <pexpert/pexpert.h>
58 #include <kern/chunklist.h>
59
60 extern struct filedesc filedesc0;
61
62 extern int (*mountroot)(void);
63 extern char rootdevice[DEVMAXNAMESIZE];
64
65 #if CONFIG_LOCKERBOOT
66 typedef struct _locker_mount_args {
67 char lmnt_path[PATH_MAX];
68 uint16_t lmnt_preferred_hash;
69 } locker_mount_args_t;
70 #endif
71
72 #define DEBUG_IMAGEBOOT 0
73
74 #if DEBUG_IMAGEBOOT
75 #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__)
76 #else
77 #define DBG_TRACE(...) do {} while(0)
78 #endif
79
80 #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
81 #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
82 #define kfree_safe(x) do { if ((x)) { kfree_addr((x)); (x) = NULL; } } while (0)
83
84 extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
85 extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
86
87 static boolean_t imageboot_setup_new(imageboot_type_t type);
88
89 vnode_t imgboot_get_image_file(const char *path, off_t *fsize, int *errp); /* may be required by chunklist.c */
90 int read_file(const char *path, void **bufp, size_t *bufszp); /* may be required by chunklist.c */
91
92 #define kIBFilePrefix "file://"
93
94 __private_extern__ int
95 imageboot_format_is_valid(const char *root_path)
96 {
97 return strncmp(root_path, kIBFilePrefix,
98 strlen(kIBFilePrefix)) == 0;
99 }
100
101 static void
102 vnode_get_and_drop_always(vnode_t vp)
103 {
104 vnode_getalways(vp);
105 vnode_rele(vp);
106 vnode_put(vp);
107 }
108
109 __private_extern__ imageboot_type_t
110 imageboot_needed(void)
111 {
112 imageboot_type_t result = IMAGEBOOT_NONE;
113 char *root_path = NULL;
114
115 DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__);
116
117 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
118 if (root_path == NULL) {
119 panic("%s: M_NAMEI zone exhausted", __FUNCTION__);
120 }
121
122 #if CONFIG_LOCKERBOOT
123 if (PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) {
124 result = IMAGEBOOT_LOCKER;
125 goto out;
126 }
127 #endif
128
129 /* Check for first layer */
130 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
131 #if CONFIG_IMAGEBOOT_IMG4
132 PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) ||
133 #endif
134 PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
135 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
136 PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
137 goto out;
138 }
139
140 /* Sanity-check first layer */
141 if (imageboot_format_is_valid(root_path)) {
142 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
143 } else {
144 goto out;
145 }
146
147 result = IMAGEBOOT_DMG;
148
149 /* Check for second layer */
150 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
151 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
152 goto out;
153 }
154
155 /* Sanity-check second layer */
156 if (imageboot_format_is_valid(root_path)) {
157 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
158 } else {
159 panic("%s: Invalid URL scheme for %s\n",
160 __FUNCTION__, root_path);
161 }
162
163 out:
164 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
165
166 return result;
167 }
168
169
170 /*
171 * Swaps in new root filesystem based on image path.
172 * Current root filesystem is removed from mount list and
173 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
174 * "rootvnode" is reset. Root vnode of currentroot filesystem
175 * is returned with usecount (no iocount).
176 */
177 __private_extern__ int
178 imageboot_mount_image(const char *root_path, int height, imageboot_type_t type)
179 {
180 dev_t dev;
181 int error;
182 /*
183 * Need to stash this here since we may do a kernel_mount() on /, which will
184 * automatically update the rootvnode global. Note that vfs_mountroot() does
185 * not update that global, which is a bit weird.
186 */
187 vnode_t old_rootvnode = rootvnode;
188 vnode_t newdp;
189 mount_t new_rootfs;
190 boolean_t update_rootvnode = FALSE;
191
192 if (type == IMAGEBOOT_DMG) {
193 error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
194 if (error) {
195 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
196 }
197
198 rootdev = dev;
199 mountroot = NULL;
200 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
201 error = vfs_mountroot();
202 if (error != 0) {
203 panic("vfs_mountroot() failed.\n");
204 }
205
206 update_rootvnode = TRUE;
207 }
208 #if CONFIG_LOCKERBOOT
209 else if (type == IMAGEBOOT_LOCKER) {
210 locker_mount_args_t *mntargs = kalloc(sizeof(*mntargs));
211 if (!mntargs) {
212 panic("could not alloc mount args");
213 }
214
215 strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path));
216 mntargs->lmnt_preferred_hash = 0;
217
218 DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path);
219 error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/",
220 mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel());
221 if (error) {
222 panic("failed to mount locker: %d", error);
223 }
224 kfree(mntargs, sizeof(*mntargs));
225
226 /* Clear the old mount association. */
227 old_rootvnode->v_mountedhere = NULL;
228 rootvnode->v_mount->mnt_vnodecovered = NULL;
229 }
230 #endif
231 else {
232 panic("invalid imageboot type: %d", type);
233 }
234
235 /*
236 * Get the vnode for '/'.
237 * Set fdp->fd_fd.fd_cdir to reference it.
238 */
239 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
240 panic("%s: cannot find root vnode", __FUNCTION__);
241 }
242 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name);
243
244 if (old_rootvnode != NULL) {
245 /* remember the old rootvnode, but remove it from mountlist */
246 mount_t old_rootfs = old_rootvnode->v_mount;
247
248 mount_list_remove(old_rootfs);
249 mount_lock(old_rootfs);
250 #ifdef CONFIG_IMGSRC_ACCESS
251 old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
252 #endif /* CONFIG_IMGSRC_ACCESS */
253 old_rootfs->mnt_flag &= ~MNT_ROOTFS;
254 mount_unlock(old_rootfs);
255 }
256
257 /* switch to the new rootvnode */
258 if (update_rootvnode) {
259 rootvnode = newdp;
260 }
261
262 new_rootfs = rootvnode->v_mount;
263 mount_lock(new_rootfs);
264 new_rootfs->mnt_flag |= MNT_ROOTFS;
265 mount_unlock(new_rootfs);
266
267 vnode_ref(newdp);
268 vnode_put(newdp);
269 filedesc0.fd_cdir = newdp;
270 DBG_TRACE("%s: root switched\n", __FUNCTION__);
271
272 if (old_rootvnode != NULL) {
273 #ifdef CONFIG_IMGSRC_ACCESS
274 if (height >= 0 && PE_imgsrc_mount_supported()) {
275 imgsrc_rootvnodes[height] = old_rootvnode;
276 } else {
277 vnode_get_and_drop_always(old_rootvnode);
278 }
279 #else
280 #pragma unused(height)
281 vnode_get_and_drop_always(old_rootvnode);
282 #endif /* CONFIG_IMGSRC_ACCESS */
283 }
284 return 0;
285 }
286
287 int
288 read_file(const char *path, void **bufp, size_t *bufszp)
289 {
290 int err = 0;
291 struct nameidata ndp = {};
292 struct vnode *vp = NULL;
293 off_t fsize = 0;
294 int resid = 0;
295 char *buf = NULL;
296 bool doclose = false;
297
298 vfs_context_t ctx = vfs_context_kernel();
299 proc_t p = vfs_context_proc(ctx);
300 kauth_cred_t kerncred = vfs_context_ucred(ctx);
301
302 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
303 if ((err = namei(&ndp)) != 0) {
304 AUTHPRNT("namei failed (%s) - %d", path, err);
305 goto out;
306 }
307 nameidone(&ndp);
308 vp = ndp.ni_vp;
309
310 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
311 AUTHPRNT("failed to get vnode size of %s - %d", path, err);
312 goto out;
313 }
314 if (fsize < 0) {
315 panic("negative file size");
316 }
317
318 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
319 AUTHPRNT("failed to open %s - %d", path, err);
320 goto out;
321 }
322 doclose = true;
323
324 /* if bufsz is non-zero, cap the read at bufsz bytes */
325 if (*bufszp && *bufszp < (size_t)fsize) {
326 fsize = *bufszp;
327 }
328
329 buf = kalloc(fsize);
330 if (buf == NULL) {
331 err = ENOMEM;
332 goto out;
333 }
334
335 if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, fsize, 0, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) {
336 AUTHPRNT("Cannot read %d bytes from %s - %d", (int)fsize, path, err);
337 goto out;
338 }
339
340 if (resid) {
341 /* didnt get everything we wanted */
342 AUTHPRNT("Short read of %d bytes from %s - %d", (int)fsize, path, resid);
343 err = EINVAL;
344 goto out;
345 }
346
347 out:
348 if (doclose) {
349 VNOP_CLOSE(vp, FREAD, ctx);
350 }
351 if (vp) {
352 vnode_put(vp);
353 vp = NULL;
354 }
355
356 if (err) {
357 kfree_safe(buf);
358 } else {
359 *bufp = buf;
360 *bufszp = fsize;
361 }
362
363 return err;
364 }
365
366 #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
367 vnode_t
368 imgboot_get_image_file(const char *path, off_t *fsize, int *errp)
369 {
370 struct nameidata ndp = {};
371 vnode_t vp = NULL;
372 vfs_context_t ctx = vfs_context_kernel();
373 int err;
374
375 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
376 if ((err = namei(&ndp)) != 0) {
377 AUTHPRNT("Cannot find %s - error %d", path, err);
378 } else {
379 nameidone(&ndp);
380 vp = ndp.ni_vp;
381
382 if (vp->v_type != VREG) {
383 err = EINVAL;
384 AUTHPRNT("%s it not a regular file", path);
385 } else if (fsize) {
386 if ((err = vnode_size(vp, fsize, ctx)) != 0) {
387 AUTHPRNT("Cannot get file size of %s - error %d", path, err);
388 }
389 }
390 }
391
392 if (err) {
393 *errp = err;
394 vp = NULL;
395 }
396 return vp;
397 }
398 #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
399
400 #if CONFIG_IMAGEBOOT_IMG4
401
402 #define APTICKET_NAME "apticket.der"
403
404 static char *
405 imgboot_get_apticket_path(const char *rootpath)
406 {
407 size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME);
408 char *path = kalloc(plen);
409
410 if (path) {
411 char *slash;
412
413 strlcpy(path, rootpath, plen);
414 slash = strrchr(path, '/');
415 if (slash == NULL) {
416 slash = path;
417 } else {
418 slash++;
419 }
420 strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1);
421 }
422 return path;
423 }
424
425 static int
426 authenticate_root_with_img4(const char *rootpath)
427 {
428 errno_t rv;
429 img4_t i4;
430 img4_payload_t i4pl;
431 vnode_t vp;
432 char *ticket_path;
433 size_t tcksz = 0;
434 void *tckbuf = NULL;
435
436 DBG_TRACE("Check %s\n", rootpath);
437
438 if (img4if == NULL) {
439 AUTHPRNT("AppleImage4 is not ready");
440 return EAGAIN;
441 }
442
443 ticket_path = imgboot_get_apticket_path(rootpath);
444 if (ticket_path == NULL) {
445 AUTHPRNT("Cannot construct ticket path - out of memory");
446 return ENOMEM;
447 }
448
449 rv = read_file(ticket_path, &tckbuf, &tcksz);
450 if (rv) {
451 AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv);
452 goto out_with_ticket_path;
453 }
454
455 DBG_TRACE("Got %d bytes of manifest from %s\n", (int)tcksz, ticket_path);
456
457 rv = img4_init(&i4, 0, tckbuf, tcksz, NULL);
458 if (rv) {
459 AUTHPRNT("Cannot initialise verification handle - error %d", rv);
460 goto out_with_ticket_bytes;
461 }
462
463 vp = imgboot_get_image_file(rootpath, NULL, &rv);
464 if (vp == NULL) {
465 /* Error message had been printed already */
466 goto out;
467 }
468
469 rv = img4_payload_init_with_vnode_4xnu(&i4pl, 'rosi', vp, I4PLF_UNWRAPPED);
470 if (rv) {
471 AUTHPRNT("failed to init payload: %d", rv);
472 goto out;
473 }
474
475 rv = img4_get_trusted_external_payload(&i4, &i4pl, IMG4_ENVIRONMENT_PPL, NULL, NULL);
476 if (rv) {
477 AUTHPRNT("failed to validate root image %s: %d", rootpath, rv);
478 }
479
480 img4_payload_destroy(&i4pl);
481 out:
482 img4_destroy(&i4);
483 out_with_ticket_bytes:
484 kfree_safe(tckbuf);
485 out_with_ticket_path:
486 kfree_safe(ticket_path);
487 return rv;
488 }
489 #endif /* CONFIG_IMAGEBOOT_IMG4 */
490
491
492 /*
493 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
494 * All existing mounts are first umounted.
495 */
496 static int
497 imageboot_mount_ramdisk(const char *path)
498 {
499 int err = 0;
500 size_t bufsz = 0;
501 void *buf = NULL;
502 dev_t dev;
503 vnode_t newdp;
504 mount_t new_rootfs;
505
506 /* Read our target image from disk */
507 err = read_file(path, &buf, &bufsz);
508 if (err) {
509 printf("%s: failed: read_file() = %d\n", __func__, err);
510 goto out;
511 }
512 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
513
514 #if CONFIG_IMGSRC_ACCESS
515 /* Re-add all root mounts to the mount list in the correct order... */
516 mount_list_remove(rootvnode->v_mount);
517 for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
518 struct vnode *vn = imgsrc_rootvnodes[i];
519 if (vn) {
520 vnode_getalways(vn);
521 imgsrc_rootvnodes[i] = NULLVP;
522
523 mount_t mnt = vn->v_mount;
524 mount_lock(mnt);
525 mnt->mnt_flag |= MNT_ROOTFS;
526 mount_list_add(mnt);
527 mount_unlock(mnt);
528
529 vnode_rele(vn);
530 vnode_put(vn);
531 }
532 }
533 mount_list_add(rootvnode->v_mount);
534 #endif
535
536 /* ... and unmount everything */
537 vnode_get_and_drop_always(rootvnode);
538 filedesc0.fd_cdir = NULL;
539 rootvnode = NULL;
540 vfs_unmountall();
541
542 /* Attach the ramfs image ... */
543 err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
544 if (err) {
545 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
546 goto out;
547 }
548
549 /* ... and mount it */
550 rootdev = dev;
551 mountroot = NULL;
552 err = vfs_mountroot();
553 if (err) {
554 printf("%s: failed: vfs_mountroot() = %d\n", __func__, err);
555 goto out;
556 }
557
558 /* Switch to new root vnode */
559 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
560 panic("%s: cannot find root vnode", __func__);
561 }
562 rootvnode = newdp;
563 rootvnode->v_flag |= VROOT;
564 new_rootfs = rootvnode->v_mount;
565 mount_lock(new_rootfs);
566 new_rootfs->mnt_flag |= MNT_ROOTFS;
567 mount_unlock(new_rootfs);
568
569 vnode_ref(newdp);
570 vnode_put(newdp);
571 filedesc0.fd_cdir = newdp;
572
573 DBG_TRACE("%s: root switched\n", __func__);
574
575 out:
576 if (err) {
577 kfree_safe(buf);
578 }
579 return err;
580 }
581
582 /*
583 * If the path is in <file://> URL format then we allocate memory and decode it,
584 * otherwise return the same pointer.
585 *
586 * Caller is expected to check if the pointers are different.
587 */
588 static char *
589 url_to_path(char *url_path)
590 {
591 char *path = url_path;
592 size_t len = strlen(kIBFilePrefix);
593
594 if (strncmp(kIBFilePrefix, url_path, len) == 0) {
595 /* its a URL - remove the file:// prefix and percent-decode */
596 url_path += len;
597
598 len = strlen(url_path);
599 if (len) {
600 /* Make a copy of the path to URL-decode */
601 path = kalloc(len + 1);
602 if (path == NULL) {
603 panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len);
604 }
605
606 strlcpy(path, url_path, len + 1);
607 url_decode(path);
608 } else {
609 panic("Bogus imageboot path URL - missing path\n");
610 }
611
612 DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path);
613 }
614
615 return path;
616 }
617
618 static boolean_t
619 imageboot_setup_new(imageboot_type_t type)
620 {
621 int error;
622 char *root_path = NULL;
623 int height = 0;
624 boolean_t done = FALSE;
625 boolean_t auth_root = TRUE;
626 boolean_t ramdisk_root = FALSE;
627
628 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
629 assert(root_path != NULL);
630
631 #if CONFIG_LOCKERBOOT
632 if (type == IMAGEBOOT_LOCKER) {
633 if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) {
634 panic("locker boot with no locker given");
635 }
636
637 DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name);
638
639 /*
640 * The locker path is a path, not a URL, so just pass it directly to
641 * imageboot_mount_image().
642 */
643 error = imageboot_mount_image(root_path, 0, type);
644 if (error) {
645 panic("failed to mount system locker: %d", error);
646 }
647
648 done = TRUE;
649 goto out;
650 }
651 #endif /* CONFIG_LOCKERBOOT */
652
653 unsigned imgboot_arg;
654 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
655 ramdisk_root = TRUE;
656 }
657
658 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
659 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
660 error = imageboot_mount_image(root_path, height, type);
661 if (error != 0) {
662 panic("Failed to mount container image.");
663 }
664
665 height++;
666 }
667
668 if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE &&
669 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
670 if (height > 0) {
671 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG);
672 }
673 goto out;
674 }
675
676 printf("%s: root image URL is '%s'\n", __func__, root_path);
677
678 #if CONFIG_CSR
679 if (auth_root && (csr_check(CSR_ALLOW_ANY_RECOVERY_OS) == 0)) {
680 AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, skipping root image authentication");
681 auth_root = FALSE;
682 }
683 #endif
684
685 /* Make a copy of the path to URL-decode */
686 char *path = url_to_path(root_path);
687 assert(path);
688
689 #if CONFIG_IMAGEBOOT_CHUNKLIST
690 if (auth_root) {
691 AUTHDBG("authenticating root image at %s", path);
692 error = authenticate_root_with_chunklist(path);
693 if (error) {
694 panic("root image authentication failed (err = %d)\n", error);
695 }
696 AUTHDBG("successfully authenticated %s", path);
697 }
698 #endif
699
700 if (ramdisk_root) {
701 error = imageboot_mount_ramdisk(path);
702 } else {
703 error = imageboot_mount_image(root_path, height, type);
704 }
705
706 if (path != root_path) {
707 kfree_safe(path);
708 }
709
710 if (error) {
711 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
712 error, auth_root, ramdisk_root);
713 }
714
715 #if CONFIG_IMAGEBOOT_CHUNKLIST
716 if (auth_root) {
717 /* check that the image version matches the running kernel */
718 AUTHDBG("checking root image version");
719 error = authenticate_root_version_check();
720 if (error) {
721 panic("root image version check failed");
722 } else {
723 AUTHDBG("root image version matches kernel");
724 }
725 }
726 #endif
727
728 done = TRUE;
729
730 out:
731 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
732 return done;
733 }
734
735 __private_extern__ void
736 imageboot_setup(imageboot_type_t type)
737 {
738 int error = 0;
739 char *root_path = NULL;
740
741 DBG_TRACE("%s: entry\n", __FUNCTION__);
742
743 if (rootvnode == NULL) {
744 panic("imageboot_setup: rootvnode is NULL.");
745 }
746
747 /*
748 * New boot-arg scheme:
749 * root-dmg : the dmg that will be the root filesystem, authenticated by default.
750 * auth-root-dmg : same as root-dmg.
751 * container-dmg : an optional dmg that contains the root-dmg.
752 * locker : the locker that will be the root filesystem -- mutually
753 * exclusive with any other boot-arg.
754 */
755 if (imageboot_setup_new(type)) {
756 return;
757 }
758
759 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
760 assert(root_path != NULL);
761
762 /*
763 * Look for outermost disk image to root from. If we're doing a nested boot,
764 * there's some sense in which the outer image never needs to be the root filesystem,
765 * but it does need very similar treatment: it must not be unmounted, needs a fake
766 * device vnode created for it, and should not show up in getfsstat() until exposed
767 * with MNT_IMGSRC. We just make it the temporary root.
768 */
769 #if CONFIG_IMAGEBOOT_IMG4
770 if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) {
771 char *path = url_to_path(root_path);
772
773 assert(path);
774
775 if (authenticate_root_with_img4(path)) {
776 panic("Root image %s does not match the manifest\n", root_path);
777 }
778 if (path != root_path) {
779 kfree_safe(path);
780 }
781 } else
782 #endif /* CONFIG_IMAGEBOOT_IMG4 */
783 if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
784 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
785 panic("%s: no valid path to image.\n", __FUNCTION__);
786 }
787
788 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path);
789
790 error = imageboot_mount_image(root_path, 0, type);
791 if (error) {
792 panic("Failed on first stage of imageboot.");
793 }
794
795 /*
796 * See if we are rooting from a nested image
797 */
798 if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
799 goto done;
800 }
801
802 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
803
804 /*
805 * If we fail to set up second image, it's not a given that we
806 * can safely root off the first.
807 */
808 error = imageboot_mount_image(root_path, 1, type);
809 if (error) {
810 panic("Failed on second stage of imageboot.");
811 }
812
813 done:
814 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
815
816 DBG_TRACE("%s: exit\n", __FUNCTION__);
817
818 return;
819 }