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