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