]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/netboot.c
xnu-344.23.tar.gz
[apple/xnu.git] / bsd / kern / netboot.c
CommitLineData
9bccf70c
A
1/*
2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
de355530
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
9bccf70c 11 *
de355530
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
9bccf70c
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
de355530
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
9bccf70c
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23/*
24 * History:
25 * 14 December, 2001 Dieter Siegmund (dieter@apple.com)
26 * - created
27 */
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/conf.h>
32#include <sys/ioctl.h>
33#include <sys/proc.h>
34#include <sys/mount.h>
35#include <sys/mbuf.h>
36#include <sys/filedesc.h>
37#include <sys/vnode.h>
38#include <sys/malloc.h>
39#include <sys/socket.h>
40#include <sys/reboot.h>
41#include <net/if.h>
42#include <net/if_dl.h>
43#include <net/if_types.h>
44#include <net/route.h>
45#include <netinet/in.h>
46#include <netinet/if_ether.h>
47#include <netinet/dhcp_options.h>
48
49
50//#include <libkern/libkern.h>
51
52extern dev_t rootdev; /* device of the root */
53extern struct filedesc filedesc0;
54
55extern char * strchr(const char *str, int ch);
56
57extern int nfs_mountroot(); /* nfs_vfsops.c */
58extern int (*mountroot)(void);
59
60extern unsigned char rootdevice[];
61
62static int S_netboot = 0;
63static struct netboot_info * S_netboot_info_p;
64
65void *
66IOBSDRegistryEntryForDeviceTree(char * path);
67
68void
69IOBSDRegistryEntryRelease(void * entry);
70
71const void *
72IOBSDRegistryEntryGetData(void * entry, char * property_name,
73 int * packet_length);
74
75extern int vndevice_root_image(const char * path, char devname[],
76 dev_t * dev_p);
77extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
78
79
80static boolean_t path_getfile __P((char * image_path,
81 struct sockaddr_in * sin_p,
82 char * serv_name, char * pathname));
83
84#define BOOTP_RESPONSE "bootp-response"
85#define BSDP_RESPONSE "bsdp-response"
86#define DHCP_RESPONSE "dhcp-response"
87
88extern int
89bootp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_retry,
90 struct in_addr * netmask_p, struct in_addr * router_p,
91 struct proc * procp);
92
93#define IP_FORMAT "%d.%d.%d.%d"
94#define IP_CH(ip) ((u_char *)ip)
95#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
96
97#define kNetBootRootPathPrefixNFS "nfs:"
98#define kNetBootRootPathPrefixHTTP "http:"
99
100typedef enum {
101 kNetBootImageTypeUnknown = 0,
102 kNetBootImageTypeNFS = 1,
103 kNetBootImageTypeHTTP = 2,
104} NetBootImageType;
105
106struct netboot_info {
107 struct in_addr client_ip;
108 struct in_addr server_ip;
109 char * server_name;
110 int server_name_length;
111 char * mount_point;
112 int mount_point_length;
113 char * image_path;
114 int image_path_length;
115 NetBootImageType image_type;
116 boolean_t use_hdix;
117};
118
119int
120inet_aton(char * cp, struct in_addr * pin)
121{
122 u_char * b = (char *)pin;
123 int i;
124 char * p;
125
126 for (p = cp, i = 0; i < 4; i++) {
127 u_long l = strtoul(p, 0, 0);
128 if (l > 255)
129 return (FALSE);
130 b[i] = l;
131 p = strchr(p, '.');
132 if (i < 3 && p == NULL)
133 return (FALSE);
134 p++;
135 }
136 return (TRUE);
137}
138
139/*
140 * Function: parse_booter_path
141 * Purpose:
142 * Parse a string of the form:
143 * "<IP>:<host>:<mount>[:<image_path>]"
144 * into the given ip address, host, mount point, and optionally, image_path.
145 *
146 * Note:
147 * The passed in string is modified i.e. ':' is replaced by '\0'.
148 * Example:
149 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
150 */
151static __inline__ boolean_t
152parse_booter_path(char * path, struct in_addr * iaddr_p, char * * host,
153 char * * mount_dir, char * * image_path)
154{
155 char * start;
156 char * colon;
157
158 /* IP address */
159 start = path;
160 colon = strchr(start, ':');
161 if (colon == NULL) {
162 return (FALSE);
163 }
164 *colon = '\0';
165 if (inet_aton(start, iaddr_p) != 1) {
166 return (FALSE);
167 }
168
169 /* host */
170 start = colon + 1;
171 colon = strchr(start, ':');
172 if (colon == NULL) {
173 return (FALSE);
174 }
175 *colon = '\0';
176 *host = start;
177
178 /* mount */
179 start = colon + 1;
180 colon = strchr(start, ':');
181 *mount_dir = start;
182 if (colon == NULL) {
183 *image_path = NULL;
184 }
185 else {
186 /* image path */
187 *colon = '\0';
188 start = colon + 1;
189 *image_path = start;
190 }
191 return (TRUE);
192}
193
194/*
195 * Function: find_colon
196 * Purpose:
197 * Find the next unescaped instance of the colon character.
198 * If a colon is escaped (preceded by a backslash '\' character),
199 * shift the string over by one character to overwrite the backslash.
200 */
201static __inline__ char *
202find_colon(char * str)
203{
204 char * start = str;
205 char * colon;
206
207 while ((colon = strchr(start, ':')) != NULL) {
208 char * dst;
209 char * src;
210
211 if (colon == start) {
212 break;
213 }
214 if (colon[-1] != '\\')
215 break;
216 for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) {
217 *dst = *src;
218 }
219 start = colon;
220 }
221 return (colon);
222}
223
224/*
225 * Function: parse_netboot_path
226 * Purpose:
227 * Parse a string of the form:
228 * "nfs:<IP>:<mount>[:<image_path>]"
229 * into the given ip address, host, mount point, and optionally, image_path.
230 * Notes:
231 * - the passed in string is modified i.e. ':' is replaced by '\0'
232 * - literal colons must be escaped with a backslash
233 *
234 * Examples:
235 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
236 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
237 */
238static __inline__ boolean_t
239parse_netboot_path(char * path, struct in_addr * iaddr_p, char * * host,
240 char * * mount_dir, char * * image_path)
241{
242 char * start;
243 char * colon;
244
245 if (strncmp(path, kNetBootRootPathPrefixNFS,
246 strlen(kNetBootRootPathPrefixNFS)) != 0) {
247 return (FALSE);
248 }
249
250 /* IP address */
251 start = path + strlen(kNetBootRootPathPrefixNFS);
252 colon = strchr(start, ':');
253 if (colon == NULL) {
254 return (FALSE);
255 }
256 *colon = '\0';
257 if (inet_aton(start, iaddr_p) != 1) {
258 return (FALSE);
259 }
260
261 /* mount point */
262 start = colon + 1;
263 colon = find_colon(start);
264 *mount_dir = start;
265 if (colon == NULL) {
266 *image_path = NULL;
267 }
268 else {
269 /* image path */
270 *colon = '\0';
271 start = colon + 1;
272 (void)find_colon(start);
273 *image_path = start;
274 }
275 *host = inet_ntoa(*iaddr_p);
276 return (TRUE);
277}
278
279static boolean_t
280parse_image_path(char * path, struct in_addr * iaddr_p, char * * host,
281 char * * mount_dir, char * * image_path)
282{
283 if (path[0] >= '0' && path[0] <= '9') {
284 return (parse_booter_path(path, iaddr_p, host, mount_dir,
285 image_path));
286 }
287 return (parse_netboot_path(path, iaddr_p, host, mount_dir,
288 image_path));
289}
290
291static boolean_t
292get_root_path(char * root_path)
293{
294 void * entry;
295 boolean_t found = FALSE;
296 const void * pkt;
297 int pkt_len;
298
299 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
300 if (entry == NULL) {
301 return (FALSE);
302 }
303 pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
304 if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
305 printf("netboot: retrieving root path from BSDP response\n");
306 }
307 else {
308 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE,
309 &pkt_len);
310 if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
311 printf("netboot: retrieving root path from BOOTP response\n");
312 }
313 }
314 if (pkt != NULL) {
315 int len;
316 dhcpol_t options;
317 char * path;
318 struct dhcp * reply;
319
320 reply = (struct dhcp *)pkt;
321 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
322
323 path = (char *)dhcpol_find(&options,
324 dhcptag_root_path_e, &len, NULL);
325 if (path) {
326 bcopy(path, root_path, len);
327 root_path[len] = '\0';
328 found = TRUE;
329 }
330 }
331 IOBSDRegistryEntryRelease(entry);
332 return (found);
333
334}
335
336static struct netboot_info *
337netboot_info_init(struct in_addr iaddr)
338{
339 struct netboot_info * info;
340 char * root_path = NULL;
341 boolean_t use_hdix = TRUE;
342 char * vndevice = NULL;
343
344 MALLOC_ZONE(vndevice, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
345 if (PE_parse_boot_arg("vndevice", vndevice) == TRUE) {
346 use_hdix = FALSE;
347 }
348 _FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
349
350 info = (struct netboot_info *)kalloc(sizeof(*info));
351 bzero(info, sizeof(*info));
352 info->client_ip = iaddr;
353 info->image_type = kNetBootImageTypeUnknown;
354 info->use_hdix = use_hdix;
355
356 /* check for a booter-specified path then a NetBoot path */
357 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
358 if (PE_parse_boot_arg("rp", root_path) == TRUE
359 || PE_parse_boot_arg("rootpath", root_path) == TRUE
360 || get_root_path(root_path) == TRUE) {
361 char * server_name = NULL;
362 char * mount_point = NULL;
363 char * image_path = NULL;
364 struct in_addr server_ip;
365
366 if (parse_image_path(root_path, &server_ip, &server_name,
367 &mount_point, &image_path)) {
368 info->image_type = kNetBootImageTypeNFS;
369 info->server_ip = server_ip;
370 info->server_name_length = strlen(server_name) + 1;
371 info->server_name = (char *)kalloc(info->server_name_length);
372 info->mount_point_length = strlen(mount_point) + 1;
373 info->mount_point = (char *)kalloc(info->mount_point_length);
374 strcpy(info->server_name, server_name);
375 strcpy(info->mount_point, mount_point);
376
377 printf("Server %s Mount %s",
378 server_name, info->mount_point);
379 if (image_path != NULL) {
380 boolean_t needs_slash;
381
382 info->image_path_length = strlen(image_path) + 1;
383 if (image_path[0] != '/') {
384 needs_slash = TRUE;
385 info->image_path_length++;
386 }
387 info->image_path = (char *)kalloc(info->image_path_length);
388 if (needs_slash) {
389 info->image_path[0] = '/';
390 strcpy(info->image_path + 1, image_path);
391 }
392 else {
393 strcpy(info->image_path, image_path);
394 }
395 printf(" Image %s", info->image_path);
396 }
397 printf("\n");
398 }
399 else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
400 strlen(kNetBootRootPathPrefixHTTP)) == 0) {
401 /* only HDIX supports HTTP */
402 info->image_type = kNetBootImageTypeHTTP;
403 info->use_hdix = TRUE;
404 info->image_path_length = strlen(root_path) + 1;
405 info->image_path = (char *)kalloc(info->image_path_length);
406 strcpy(info->image_path, root_path);
407 }
408 else {
409 printf("netboot: root path uses unrecognized format\n");
410 }
411 }
412 _FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
413 return (info);
414}
415
416static void
417netboot_info_free(struct netboot_info * * info_p)
418{
419 struct netboot_info * info = *info_p;
420
421 if (info) {
422 if (info->mount_point) {
423 kfree(info->mount_point, info->mount_point_length);
424 }
425 if (info->server_name) {
426 kfree(info->server_name, info->server_name_length);
427 }
428 if (info->image_path) {
429 kfree(info->image_path, info->image_path_length);
430 }
431 kfree(info, sizeof(*info));
432 }
433 *info_p = NULL;
434 return;
435}
436
437boolean_t
438netboot_iaddr(struct in_addr * iaddr_p)
439{
440 if (S_netboot_info_p == NULL)
441 return (FALSE);
442
443 *iaddr_p = S_netboot_info_p->client_ip;
444 return (TRUE);
445}
446
447boolean_t
448netboot_rootpath(struct in_addr * server_ip,
449 char * name, int name_len,
450 char * path, int path_len)
451{
452 if (S_netboot_info_p == NULL)
453 return (FALSE);
454
455 name[0] = '\0';
456 path[0] = '\0';
457
458 if (S_netboot_info_p->mount_point_length == 0) {
459 return (FALSE);
460 }
461 if (path_len < S_netboot_info_p->mount_point_length) {
462 printf("netboot: path too small %d < %d\n",
463 path_len, S_netboot_info_p->mount_point_length);
464 return (FALSE);
465 }
466 strcpy(path, S_netboot_info_p->mount_point);
467 strncpy(name, S_netboot_info_p->server_name, name_len);
468 *server_ip = S_netboot_info_p->server_ip;
469 return (TRUE);
470}
471
472
473static boolean_t
474get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
475 struct in_addr * router_p)
476{
477 void * entry;
478 const void * pkt;
479 int pkt_len;
480
481
482 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
483 if (entry == NULL) {
484 return (FALSE);
485 }
486 pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
487 if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
488 printf("netboot: retrieving IP information from DHCP response\n");
489 }
490 else {
491 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len);
492 if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
493 printf("netboot: retrieving IP information from BOOTP response\n");
494 }
495 }
496 if (pkt != NULL) {
497 struct in_addr * ip;
498 int len;
499 dhcpol_t options;
500 struct dhcp * reply;
501
502 reply = (struct dhcp *)pkt;
503 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
504 *iaddr_p = reply->dp_yiaddr;
505 ip = (struct in_addr *)
506 dhcpol_find(&options,
507 dhcptag_subnet_mask_e, &len, NULL);
508 if (ip) {
509 *netmask_p = *ip;
510 }
511 ip = (struct in_addr *)
512 dhcpol_find(&options, dhcptag_router_e, &len, NULL);
513 if (ip) {
514 *router_p = *ip;
515 }
516 }
517 IOBSDRegistryEntryRelease(entry);
518 return (pkt != NULL);
519}
520
521static int
522inet_aifaddr(struct socket * so, char * name, const struct in_addr * addr,
523 const struct in_addr * mask,
524 const struct in_addr * broadcast)
525{
526 struct sockaddr blank_sin = { sizeof(blank_sin), AF_INET };
527 struct ifaliasreq ifra;
528
529 bzero(&ifra, sizeof(ifra));
530 strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
531 if (addr) {
532 ifra.ifra_addr = blank_sin;
533 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr = *addr;
534 }
535 if (mask) {
536 ifra.ifra_mask = blank_sin;
537 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr = *mask;
538 }
539 if (broadcast) {
540 ifra.ifra_broadaddr = blank_sin;
541 ((struct sockaddr_in *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
542 }
543 return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()));
544}
545
546static int
547default_route_add(struct in_addr router, boolean_t proxy_arp)
548{
549 struct sockaddr_in dst;
550 u_long flags = RTF_UP | RTF_STATIC;
551 struct sockaddr_in gw;
552 struct sockaddr_in mask;
553
554 if (proxy_arp == FALSE) {
555 flags |= RTF_GATEWAY;
556 }
557
558 /* dest 0.0.0.0 */
559 bzero((caddr_t)&dst, sizeof(dst));
560 dst.sin_len = sizeof(dst);
561 dst.sin_family = AF_INET;
562
563 /* gateway */
564 bzero((caddr_t)&gw, sizeof(gw));
565 gw.sin_len = sizeof(gw);
566 gw.sin_family = AF_INET;
567 gw.sin_addr = router;
568
569 /* mask 0.0.0.0 */
570 bzero(&mask, sizeof(mask));
571 mask.sin_len = sizeof(mask);
572 mask.sin_family = AF_INET;
573
574 printf("netboot: adding default route " IP_FORMAT "\n",
575 IP_LIST(&router));
576
577 return (rtrequest(RTM_ADD, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
578 (struct sockaddr *)&mask, flags, NULL));
579}
580
581static struct ifnet *
582find_interface()
583{
584 struct ifnet * ifp = NULL;
585
586 if (rootdevice[0]) {
587 ifp = ifunit(rootdevice);
588 }
589 if (ifp == NULL) {
590 TAILQ_FOREACH(ifp, &ifnet, if_link)
591 if ((ifp->if_flags &
592 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
593 break;
594 }
595 return (ifp);
596}
597
598int
599netboot_mountroot()
600{
601 int error = 0;
602 struct in_addr iaddr = { 0 };
603 struct ifreq ifr;
604 struct ifnet * ifp;
605 struct in_addr netmask = { 0 };
606 struct proc * procp = current_proc();
607 struct in_addr router = { 0 };
608 struct socket * so = NULL;
609
610 bzero(&ifr, sizeof(ifr));
611
612 thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
613
614 /* find the interface */
615 ifp = find_interface();
616 if (ifp == NULL) {
617 printf("netboot: no suitable interface\n");
618 error = ENXIO;
619 goto failed;
620 }
621 sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
622 printf("netboot: using network interface '%s'\n", ifr.ifr_name);
623
624 /* bring it up */
625 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) {
626 printf("netboot: socreate, error=%d\n", error);
627 goto failed;
628 }
629 ifr.ifr_flags = ifp->if_flags | IFF_UP;
630 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp);
631 if (error) {
632 printf("netboot: SIFFLAGS, error=%d\n", error);
633 goto failed;
634 }
635
636 /* grab information from the registry */
637 if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
638 /* use BOOTP to retrieve IP address, netmask and router */
639 error = bootp(ifp, &iaddr, 32, &netmask, &router, procp);
640 if (error) {
641 printf("netboot: BOOTP failed %d\n", error);
642 goto failed;
643 }
644 }
645 printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
646 if (netmask.s_addr) {
647 printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
648 }
649 if (router.s_addr) {
650 printf(" router " IP_FORMAT, IP_LIST(&router));
651 }
652 printf("\n");
653 error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL);
654 if (error) {
655 printf("netboot: inet_aifaddr failed, %d\n", error);
656 goto failed;
657 }
658 if (router.s_addr == 0) {
659 /* enable proxy arp if we don't have a router */
660 router.s_addr = iaddr.s_addr;
661 }
662 error = default_route_add(router, router.s_addr == iaddr.s_addr);
663 if (error) {
664 printf("netboot: default_route_add failed %d\n", error);
665 }
666
667 soclose(so);
668 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
669
670 S_netboot_info_p = netboot_info_init(iaddr);
671 switch (S_netboot_info_p->image_type) {
672 default:
673 case kNetBootImageTypeNFS:
674 error = nfs_mountroot();
675 break;
676 case kNetBootImageTypeHTTP:
677 error = netboot_setup(procp);
678 break;
679 }
680 if (error == 0) {
681 S_netboot = 1;
682 }
683 else {
684 S_netboot = 0;
685 }
686 return (error);
687failed:
688 if (so != NULL) {
689 soclose(so);
690 }
691 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
692 return (error);
693}
694
695int
696netboot_setup(struct proc * p)
697{
698 dev_t dev;
699 int error = 0;
700
701 if (S_netboot_info_p == NULL
702 || S_netboot_info_p->image_path == NULL) {
703 goto done;
704 }
705 if (S_netboot_info_p->use_hdix) {
706 printf("netboot_setup: calling di_root_image\n");
707 error = di_root_image(S_netboot_info_p->image_path,
708 rootdevice, &dev);
709 if (error) {
710 printf("netboot_setup: di_root_image: failed %d\n", error);
711 goto done;
712 }
713 }
714 else {
715 printf("netboot_setup: calling vndevice_root_image\n");
716 error = vndevice_root_image(S_netboot_info_p->image_path,
717 rootdevice, &dev);
718 if (error) {
719 printf("netboot_setup: vndevice_root_image: failed %d\n", error);
720 goto done;
721 }
722 }
723 rootdev = dev;
724 mountroot = NULL;
725 printf("netboot: root device 0x%x\n", rootdev);
726 error = vfs_mountroot();
727 if (error == 0 && rootvnode != NULL) {
728 struct vnode *tvp;
729 struct vnode *newdp;
730
731 /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
732 if (VFS_ROOT(mountlist.cqh_last, &newdp))
733 panic("netboot_setup: cannot find root vnode");
734 VREF(newdp);
735 tvp = rootvnode;
736 vrele(tvp);
737 filedesc0.fd_cdir = newdp;
738 rootvnode = newdp;
739 simple_lock(&mountlist_slock);
740 CIRCLEQ_REMOVE(&mountlist, CIRCLEQ_FIRST(&mountlist), mnt_list);
741 simple_unlock(&mountlist_slock);
742 VOP_UNLOCK(rootvnode, 0, p);
743 mountlist.cqh_first->mnt_flag |= MNT_ROOTFS;
744 }
745 done:
746 netboot_info_free(&S_netboot_info_p);
747 return (error);
748}
749
750int
751netboot_root()
752{
753 return (S_netboot);
754}