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