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