]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/imageboot.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
CommitLineData
2d21ac55
A
1/*
2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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.
0a7de745 25 *
2d21ac55
A
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>
6d2010ae 38#include <kern/assert.h>
2d21ac55 39
813fb2f6
A
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>
5ba3f43e 45#include <miscfs/devfs/devfsdefs.h>
813fb2f6
A
46#include <libkern/crypto/sha2.h>
47#include <libkern/crypto/rsa.h>
48#include <libkern/OSKextLibPrivate.h>
49
cb323159
A
50#if CONFIG_IMAGEBOOT_IMG4
51#include <libkern/img4/interface.h>
52#include <img4/img4.h>
53#endif
54
813fb2f6
A
55#include <kern/kalloc.h>
56
2d21ac55 57#include <pexpert/pexpert.h>
813fb2f6 58#include <kern/chunklist.h>
2d21ac55
A
59
60extern struct filedesc filedesc0;
61
62extern int (*mountroot)(void);
5ba3f43e 63extern char rootdevice[DEVMAXNAMESIZE];
2d21ac55 64
cb323159
A
65#if CONFIG_LOCKERBOOT
66typedef struct _locker_mount_args {
67 char lmnt_path[PATH_MAX];
68 uint16_t lmnt_preferred_hash;
69} locker_mount_args_t;
70#endif
71
2d21ac55
A
72#define DEBUG_IMAGEBOOT 0
73
74#if DEBUG_IMAGEBOOT
cb323159 75#define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__)
2d21ac55
A
76#else
77#define DBG_TRACE(...) do {} while(0)
78#endif
79
cb323159
A
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
5ba3f43e
A
84extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
85extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
86
cb323159
A
87static boolean_t imageboot_setup_new(imageboot_type_t type);
88
89vnode_t imgboot_get_image_file(const char *path, off_t *fsize, int *errp); /* may be required by chunklist.c */
90int read_file(const char *path, void **bufp, size_t *bufszp); /* may be required by chunklist.c */
2d21ac55
A
91
92#define kIBFilePrefix "file://"
93
6d2010ae
A
94__private_extern__ int
95imageboot_format_is_valid(const char *root_path)
96{
0a7de745
A
97 return strncmp(root_path, kIBFilePrefix,
98 strlen(kIBFilePrefix)) == 0;
6d2010ae
A
99}
100
101static void
0a7de745 102vnode_get_and_drop_always(vnode_t vp)
6d2010ae
A
103{
104 vnode_getalways(vp);
105 vnode_rele(vp);
106 vnode_put(vp);
107}
108
cb323159 109__private_extern__ imageboot_type_t
2d21ac55
A
110imageboot_needed(void)
111{
cb323159 112 imageboot_type_t result = IMAGEBOOT_NONE;
2d21ac55 113 char *root_path = NULL;
0a7de745 114
2d21ac55
A
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);
0a7de745 118 if (root_path == NULL) {
2d21ac55 119 panic("%s: M_NAMEI zone exhausted", __FUNCTION__);
0a7de745 120 }
2d21ac55 121
cb323159
A
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
6d2010ae 129 /* Check for first layer */
813fb2f6 130 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
cb323159
A
131#if CONFIG_IMAGEBOOT_IMG4
132 PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) ||
133#endif
0a7de745
A
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))) {
6d2010ae
A
137 goto out;
138 }
813fb2f6 139
6d2010ae
A
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 }
2d21ac55 146
cb323159 147 result = IMAGEBOOT_DMG;
6d2010ae
A
148
149 /* Check for second layer */
150 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
0a7de745 151 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
6d2010ae
A
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",
0a7de745 160 __FUNCTION__, root_path);
2d21ac55 161 }
6d2010ae
A
162
163out:
2d21ac55
A
164 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
165
0a7de745 166 return result;
2d21ac55
A
167}
168
169
170/*
6d2010ae
A
171 * Swaps in new root filesystem based on image path.
172 * Current root filesystem is removed from mount list and
0a7de745
A
173 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
174 * "rootvnode" is reset. Root vnode of currentroot filesystem
6d2010ae 175 * is returned with usecount (no iocount).
2d21ac55 176 */
6d2010ae 177__private_extern__ int
cb323159 178imageboot_mount_image(const char *root_path, int height, imageboot_type_t type)
2d21ac55 179{
0a7de745
A
180 dev_t dev;
181 int error;
cb323159
A
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;
0a7de745
A
188 vnode_t newdp;
189 mount_t new_rootfs;
cb323159 190 boolean_t update_rootvnode = FALSE;
2d21ac55 191
cb323159
A
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;
2d21ac55 207 }
cb323159
A
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 }
2d21ac55 214
cb323159
A
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);
6d2010ae 233 }
2d21ac55 234
6d2010ae
A
235 /*
236 * Get the vnode for '/'.
237 * Set fdp->fd_fd.fd_cdir to reference it.
238 */
0a7de745 239 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
6d2010ae 240 panic("%s: cannot find root vnode", __FUNCTION__);
0a7de745 241 }
cb323159 242 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name);
2d21ac55 243
cb323159 244 if (old_rootvnode != NULL) {
6d2010ae 245 /* remember the old rootvnode, but remove it from mountlist */
cb323159 246 mount_t old_rootfs = old_rootvnode->v_mount;
0a7de745 247
b7266188 248 mount_list_remove(old_rootfs);
b7266188
A
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);
6d2010ae 255 }
b7266188 256
6d2010ae 257 /* switch to the new rootvnode */
cb323159
A
258 if (update_rootvnode) {
259 rootvnode = newdp;
260 }
b7266188 261
6d2010ae
A
262 new_rootfs = rootvnode->v_mount;
263 mount_lock(new_rootfs);
264 new_rootfs->mnt_flag |= MNT_ROOTFS;
265 mount_unlock(new_rootfs);
b7266188 266
6d2010ae
A
267 vnode_ref(newdp);
268 vnode_put(newdp);
269 filedesc0.fd_cdir = newdp;
270 DBG_TRACE("%s: root switched\n", __FUNCTION__);
b7266188 271
6d2010ae 272 if (old_rootvnode != NULL) {
b7266188 273#ifdef CONFIG_IMGSRC_ACCESS
0a7de745
A
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
cb323159 280#pragma unused(height)
6d2010ae 281 vnode_get_and_drop_always(old_rootvnode);
b7266188 282#endif /* CONFIG_IMGSRC_ACCESS */
6d2010ae
A
283 }
284 return 0;
285}
b7266188 286
cb323159 287int
813fb2f6
A
288read_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
5ba3f43e 302 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
813fb2f6 303 if ((err = namei(&ndp)) != 0) {
cb323159 304 AUTHPRNT("namei failed (%s) - %d", path, err);
813fb2f6
A
305 goto out;
306 }
307 nameidone(&ndp);
308 vp = ndp.ni_vp;
309
310 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
cb323159 311 AUTHPRNT("failed to get vnode size of %s - %d", path, err);
813fb2f6
A
312 goto out;
313 }
314 if (fsize < 0) {
315 panic("negative file size");
316 }
317
318 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
cb323159 319 AUTHPRNT("failed to open %s - %d", path, err);
813fb2f6
A
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) {
cb323159 336 AUTHPRNT("Cannot read %d bytes from %s - %d", (int)fsize, path, err);
813fb2f6
A
337 goto out;
338 }
339
340 if (resid) {
341 /* didnt get everything we wanted */
cb323159 342 AUTHPRNT("Short read of %d bytes from %s - %d", (int)fsize, path, resid);
813fb2f6
A
343 err = EINVAL;
344 goto out;
345 }
346
347out:
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
cb323159
A
366#if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
367vnode_t
368imgboot_get_image_file(const char *path, off_t *fsize, int *errp)
813fb2f6 369{
813fb2f6 370 struct nameidata ndp = {};
cb323159 371 vnode_t vp = NULL;
813fb2f6 372 vfs_context_t ctx = vfs_context_kernel();
cb323159 373 int err;
813fb2f6 374
cb323159 375 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
813fb2f6 376 if ((err = namei(&ndp)) != 0) {
cb323159
A
377 AUTHPRNT("Cannot find %s - error %d", path, err);
378 } else {
379 nameidone(&ndp);
380 vp = ndp.ni_vp;
813fb2f6 381
cb323159 382 if (vp->v_type != VREG) {
813fb2f6 383 err = EINVAL;
cb323159
A
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 }
813fb2f6 389 }
813fb2f6
A
390 }
391
cb323159
A
392 if (err) {
393 *errp = err;
813fb2f6
A
394 vp = NULL;
395 }
cb323159 396 return vp;
813fb2f6 397}
cb323159 398#endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
813fb2f6 399
cb323159 400#if CONFIG_IMAGEBOOT_IMG4
813fb2f6 401
cb323159 402#define APTICKET_NAME "apticket.der"
813fb2f6 403
cb323159
A
404static char *
405imgboot_get_apticket_path(const char *rootpath)
406{
407 size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME);
408 char *path = kalloc(plen);
813fb2f6 409
cb323159
A
410 if (path) {
411 char *slash;
813fb2f6 412
cb323159
A
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);
813fb2f6 421 }
cb323159 422 return path;
813fb2f6
A
423}
424
425static int
cb323159 426authenticate_root_with_img4(const char *rootpath)
813fb2f6 427{
cb323159
A
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;
813fb2f6 435
cb323159 436 DBG_TRACE("Check %s\n", rootpath);
813fb2f6 437
cb323159
A
438 if (img4if == NULL) {
439 AUTHPRNT("AppleImage4 is not ready");
440 return EAGAIN;
813fb2f6
A
441 }
442
cb323159
A
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;
813fb2f6
A
447 }
448
cb323159
A
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;
813fb2f6
A
453 }
454
cb323159 455 DBG_TRACE("Got %d bytes of manifest from %s\n", (int)tcksz, ticket_path);
813fb2f6 456
cb323159
A
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;
813fb2f6
A
461 }
462
cb323159
A
463 vp = imgboot_get_image_file(rootpath, NULL, &rv);
464 if (vp == NULL) {
465 /* Error message had been printed already */
813fb2f6
A
466 goto out;
467 }
468
cb323159
A
469 rv = img4_payload_init_with_vnode_4xnu(&i4pl, 'rosi', vp, I4PLF_UNWRAPPED);
470 if (rv) {
471 AUTHPRNT("failed to init payload: %d", rv);
813fb2f6
A
472 goto out;
473 }
474
cb323159
A
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);
813fb2f6
A
478 }
479
cb323159 480 img4_payload_destroy(&i4pl);
813fb2f6 481out:
cb323159
A
482 img4_destroy(&i4);
483out_with_ticket_bytes:
484 kfree_safe(tckbuf);
485out_with_ticket_path:
486 kfree_safe(ticket_path);
487 return rv;
813fb2f6 488}
cb323159 489#endif /* CONFIG_IMAGEBOOT_IMG4 */
813fb2f6 490
813fb2f6 491
5ba3f43e
A
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 */
496static int
497imageboot_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 */
0a7de745 559 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
5ba3f43e
A
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
575out:
576 if (err) {
577 kfree_safe(buf);
578 }
579 return err;
580}
581
cb323159
A
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 */
588static char *
589url_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
813fb2f6 618static boolean_t
cb323159 619imageboot_setup_new(imageboot_type_t type)
6d2010ae
A
620{
621 int error;
622 char *root_path = NULL;
623 int height = 0;
624 boolean_t done = FALSE;
cb323159 625 boolean_t auth_root = TRUE;
5ba3f43e 626 boolean_t ramdisk_root = FALSE;
6d2010ae
A
627
628 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
629 assert(root_path != NULL);
630
cb323159
A
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
5ba3f43e
A
653 unsigned imgboot_arg;
654 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
655 ramdisk_root = TRUE;
656 }
657
813fb2f6 658 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
6d2010ae 659 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
cb323159 660 error = imageboot_mount_image(root_path, height, type);
6d2010ae
A
661 if (error != 0) {
662 panic("Failed to mount container image.");
663 }
664
665 height++;
666 }
667
cb323159
A
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) {
6d2010ae 670 if (height > 0) {
cb323159 671 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG);
6d2010ae
A
672 }
673 goto out;
813fb2f6
A
674 }
675
cb323159 676 printf("%s: root image URL is '%s'\n", __func__, root_path);
b7266188 677
813fb2f6
A
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");
cb323159 681 auth_root = FALSE;
2d21ac55 682 }
813fb2f6
A
683#endif
684
5ba3f43e 685 /* Make a copy of the path to URL-decode */
cb323159
A
686 char *path = url_to_path(root_path);
687 assert(path);
6d2010ae 688
cb323159 689#if CONFIG_IMAGEBOOT_CHUNKLIST
5ba3f43e 690 if (auth_root) {
813fb2f6 691 AUTHDBG("authenticating root image at %s", path);
cb323159 692 error = authenticate_root_with_chunklist(path);
813fb2f6
A
693 if (error) {
694 panic("root image authentication failed (err = %d)\n", error);
695 }
696 AUTHDBG("successfully authenticated %s", path);
5ba3f43e 697 }
cb323159 698#endif
00867663 699
5ba3f43e
A
700 if (ramdisk_root) {
701 error = imageboot_mount_ramdisk(path);
702 } else {
cb323159 703 error = imageboot_mount_image(root_path, height, type);
813fb2f6 704 }
6d2010ae 705
cb323159
A
706 if (path != root_path) {
707 kfree_safe(path);
708 }
5ba3f43e
A
709
710 if (error) {
711 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
0a7de745 712 error, auth_root, ramdisk_root);
6d2010ae
A
713 }
714
cb323159 715#if CONFIG_IMAGEBOOT_CHUNKLIST
813fb2f6
A
716 if (auth_root) {
717 /* check that the image version matches the running kernel */
718 AUTHDBG("checking root image version");
cb323159 719 error = authenticate_root_version_check();
813fb2f6
A
720 if (error) {
721 panic("root image version check failed");
722 } else {
723 AUTHDBG("root image version matches kernel");
724 }
725 }
cb323159 726#endif
813fb2f6 727
6d2010ae
A
728 done = TRUE;
729
730out:
731 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
732 return done;
733}
734
735__private_extern__ void
cb323159 736imageboot_setup(imageboot_type_t type)
6d2010ae
A
737{
738 int error = 0;
739 char *root_path = NULL;
740
741 DBG_TRACE("%s: entry\n", __FUNCTION__);
742
813fb2f6 743 if (rootvnode == NULL) {
6d2010ae
A
744 panic("imageboot_setup: rootvnode is NULL.");
745 }
746
747 /*
748 * New boot-arg scheme:
cb323159
A
749 * root-dmg : the dmg that will be the root filesystem, authenticated by default.
750 * auth-root-dmg : same as root-dmg.
0a7de745 751 * container-dmg : an optional dmg that contains the root-dmg.
cb323159
A
752 * locker : the locker that will be the root filesystem -- mutually
753 * exclusive with any other boot-arg.
6d2010ae 754 */
cb323159 755 if (imageboot_setup_new(type)) {
6d2010ae
A
756 return;
757 }
813fb2f6 758
6d2010ae
A
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
0a7de745 766 * device vnode created for it, and should not show up in getfsstat() until exposed
6d2010ae
A
767 * with MNT_IMGSRC. We just make it the temporary root.
768 */
cb323159
A
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 */
0a7de745
A
783 if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
784 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
6d2010ae
A
785 panic("%s: no valid path to image.\n", __FUNCTION__);
786 }
787
cb323159 788 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path);
0a7de745 789
cb323159 790 error = imageboot_mount_image(root_path, 0, type);
6d2010ae
A
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 */
0a7de745 798 if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
6d2010ae
A
799 goto done;
800 }
0a7de745 801
6d2010ae
A
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
0a7de745 806 * can safely root off the first.
6d2010ae 807 */
cb323159 808 error = imageboot_mount_image(root_path, 1, type);
6d2010ae 809 if (error) {
0a7de745 810 panic("Failed on second stage of imageboot.");
6d2010ae
A
811 }
812
2d21ac55
A
813done:
814 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
815
816 DBG_TRACE("%s: exit\n", __FUNCTION__);
817
6d2010ae 818 return;
2d21ac55 819}