]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/netboot.c
565da712801cdda3ca001b2fc17271f527fcaaf8
[apple/xnu.git] / bsd / kern / netboot.c
1 /*
2 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * History:
26 * 14 December, 2001 Dieter Siegmund (dieter@apple.com)
27 * - created
28 */
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/conf.h>
33 #include <sys/ioctl.h>
34 #include <sys/proc_internal.h>
35 #include <sys/mount_internal.h>
36 #include <sys/mbuf.h>
37 #include <sys/filedesc.h>
38 #include <sys/vnode_internal.h>
39 #include <sys/malloc.h>
40 #include <sys/socket.h>
41 #include <sys/socketvar.h>
42 #include <sys/reboot.h>
43 #include <net/if.h>
44 #include <net/if_dl.h>
45 #include <net/if_types.h>
46 #include <net/route.h>
47 #include <netinet/in.h>
48 #include <netinet/if_ether.h>
49 #include <netinet/dhcp_options.h>
50 #include <pexpert/pexpert.h>
51
52 #include <kern/kern_types.h>
53 #include <kern/kalloc.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 static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */
257 char * start;
258 char * colon;
259
260 if (strncmp(path, kNetBootRootPathPrefixNFS,
261 strlen(kNetBootRootPathPrefixNFS)) != 0) {
262 return (FALSE);
263 }
264
265 /* IP address */
266 start = path + strlen(kNetBootRootPathPrefixNFS);
267 colon = strchr(start, ':');
268 if (colon == NULL) {
269 return (FALSE);
270 }
271 *colon = '\0';
272 if (inet_aton(start, iaddr_p) != 1) {
273 return (FALSE);
274 }
275
276 /* mount point */
277 start = colon + 1;
278 colon = find_colon(start);
279 *mount_dir = start;
280 if (colon == NULL) {
281 *image_path = NULL;
282 }
283 else {
284 /* image path */
285 *colon = '\0';
286 start = colon + 1;
287 (void)find_colon(start);
288 *image_path = start;
289 }
290 *host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp));
291 return (TRUE);
292 }
293
294 static boolean_t
295 parse_image_path(char * path, struct in_addr * iaddr_p, char * * host,
296 char * * mount_dir, char * * image_path)
297 {
298 if (path[0] >= '0' && path[0] <= '9') {
299 return (parse_booter_path(path, iaddr_p, host, mount_dir,
300 image_path));
301 }
302 return (parse_netboot_path(path, iaddr_p, host, mount_dir,
303 image_path));
304 }
305
306 static boolean_t
307 get_root_path(char * root_path)
308 {
309 void * entry;
310 boolean_t found = FALSE;
311 const void * pkt;
312 int pkt_len;
313
314 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
315 if (entry == NULL) {
316 return (FALSE);
317 }
318 pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
319 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
320 printf("netboot: retrieving root path from BSDP response\n");
321 }
322 else {
323 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE,
324 &pkt_len);
325 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
326 printf("netboot: retrieving root path from BOOTP response\n");
327 }
328 }
329 if (pkt != NULL) {
330 int len;
331 dhcpol_t options;
332 char * path;
333 struct dhcp * reply;
334
335 reply = (struct dhcp *)pkt;
336 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
337
338 path = (char *)dhcpol_find(&options,
339 dhcptag_root_path_e, &len, NULL);
340 if (path) {
341 bcopy(path, root_path, len);
342 root_path[len] = '\0';
343 found = TRUE;
344 }
345 }
346 IOBSDRegistryEntryRelease(entry);
347 return (found);
348
349 }
350
351 static struct netboot_info *
352 netboot_info_init(struct in_addr iaddr)
353 {
354 struct netboot_info * info;
355 char * root_path = NULL;
356 boolean_t use_hdix = TRUE;
357 char * vndevice = NULL;
358
359 MALLOC_ZONE(vndevice, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
360 if (vndevice == NULL)
361 panic("netboot_info_init: M_NAMEI zone exhausted");
362 if (PE_parse_boot_arg("vndevice", vndevice) == TRUE) {
363 use_hdix = FALSE;
364 }
365 FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
366
367 info = (struct netboot_info *)kalloc(sizeof(*info));
368 bzero(info, sizeof(*info));
369 info->client_ip = iaddr;
370 info->image_type = kNetBootImageTypeUnknown;
371 info->use_hdix = use_hdix;
372
373 /* check for a booter-specified path then a NetBoot path */
374 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
375 if (root_path == NULL)
376 panic("netboot_info_init: M_NAMEI zone exhausted");
377 if (PE_parse_boot_arg("rp", root_path) == TRUE
378 || PE_parse_boot_arg("rootpath", root_path) == TRUE
379 || get_root_path(root_path) == TRUE) {
380 char * server_name = NULL;
381 char * mount_point = NULL;
382 char * image_path = NULL;
383 struct in_addr server_ip;
384
385 if (parse_image_path(root_path, &server_ip, &server_name,
386 &mount_point, &image_path)) {
387 info->image_type = kNetBootImageTypeNFS;
388 info->server_ip = server_ip;
389 info->server_name_length = strlen(server_name) + 1;
390 info->server_name = (char *)kalloc(info->server_name_length);
391 info->mount_point_length = strlen(mount_point) + 1;
392 info->mount_point = (char *)kalloc(info->mount_point_length);
393 strcpy(info->server_name, server_name);
394 strcpy(info->mount_point, mount_point);
395
396 printf("Server %s Mount %s",
397 server_name, info->mount_point);
398 if (image_path != NULL) {
399 boolean_t needs_slash = FALSE;
400
401 info->image_path_length = strlen(image_path) + 1;
402 if (image_path[0] != '/') {
403 needs_slash = TRUE;
404 info->image_path_length++;
405 }
406 info->image_path = (char *)kalloc(info->image_path_length);
407 if (needs_slash) {
408 info->image_path[0] = '/';
409 strcpy(info->image_path + 1, image_path);
410 }
411 else {
412 strcpy(info->image_path, image_path);
413 }
414 printf(" Image %s", info->image_path);
415 }
416 printf("\n");
417 }
418 else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
419 strlen(kNetBootRootPathPrefixHTTP)) == 0) {
420 /* only HDIX supports HTTP */
421 info->image_type = kNetBootImageTypeHTTP;
422 info->use_hdix = TRUE;
423 info->image_path_length = strlen(root_path) + 1;
424 info->image_path = (char *)kalloc(info->image_path_length);
425 strcpy(info->image_path, root_path);
426 }
427 else {
428 printf("netboot: root path uses unrecognized format\n");
429 }
430 }
431 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
432 return (info);
433 }
434
435 static void
436 netboot_info_free(struct netboot_info * * info_p)
437 {
438 struct netboot_info * info = *info_p;
439
440 if (info) {
441 if (info->mount_point) {
442 kfree(info->mount_point, info->mount_point_length);
443 }
444 if (info->server_name) {
445 kfree(info->server_name, info->server_name_length);
446 }
447 if (info->image_path) {
448 kfree(info->image_path, info->image_path_length);
449 }
450 kfree(info, sizeof(*info));
451 }
452 *info_p = NULL;
453 return;
454 }
455
456 boolean_t
457 netboot_iaddr(struct in_addr * iaddr_p)
458 {
459 if (S_netboot_info_p == NULL)
460 return (FALSE);
461
462 *iaddr_p = S_netboot_info_p->client_ip;
463 return (TRUE);
464 }
465
466 boolean_t
467 netboot_rootpath(struct in_addr * server_ip,
468 char * name, int name_len,
469 char * path, int path_len)
470 {
471 if (S_netboot_info_p == NULL)
472 return (FALSE);
473
474 name[0] = '\0';
475 path[0] = '\0';
476
477 if (S_netboot_info_p->mount_point_length == 0) {
478 return (FALSE);
479 }
480 if (path_len < S_netboot_info_p->mount_point_length) {
481 printf("netboot: path too small %d < %d\n",
482 path_len, S_netboot_info_p->mount_point_length);
483 return (FALSE);
484 }
485 strcpy(path, S_netboot_info_p->mount_point);
486 strncpy(name, S_netboot_info_p->server_name, name_len);
487 *server_ip = S_netboot_info_p->server_ip;
488 return (TRUE);
489 }
490
491
492 static boolean_t
493 get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
494 struct in_addr * router_p)
495 {
496 void * entry;
497 const void * pkt;
498 int pkt_len;
499
500
501 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
502 if (entry == NULL) {
503 return (FALSE);
504 }
505 pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
506 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
507 printf("netboot: retrieving IP information from DHCP response\n");
508 }
509 else {
510 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len);
511 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
512 printf("netboot: retrieving IP information from BOOTP response\n");
513 }
514 }
515 if (pkt != NULL) {
516 struct in_addr * ip;
517 int len;
518 dhcpol_t options;
519 struct dhcp * reply;
520
521 reply = (struct dhcp *)pkt;
522 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
523 *iaddr_p = reply->dp_yiaddr;
524 ip = (struct in_addr *)
525 dhcpol_find(&options,
526 dhcptag_subnet_mask_e, &len, NULL);
527 if (ip) {
528 *netmask_p = *ip;
529 }
530 ip = (struct in_addr *)
531 dhcpol_find(&options, dhcptag_router_e, &len, NULL);
532 if (ip) {
533 *router_p = *ip;
534 }
535 }
536 IOBSDRegistryEntryRelease(entry);
537 return (pkt != NULL);
538 }
539
540 static int
541 inet_aifaddr(struct socket * so, char * name, const struct in_addr * addr,
542 const struct in_addr * mask,
543 const struct in_addr * broadcast)
544 {
545 struct sockaddr blank_sin;
546 struct ifaliasreq ifra;
547
548 bzero(&blank_sin, sizeof(blank_sin));
549 blank_sin.sa_len = sizeof(blank_sin);
550 blank_sin.sa_family = AF_INET;
551
552 bzero(&ifra, sizeof(ifra));
553 strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
554 if (addr) {
555 ifra.ifra_addr = blank_sin;
556 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr = *addr;
557 }
558 if (mask) {
559 ifra.ifra_mask = blank_sin;
560 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr = *mask;
561 }
562 if (broadcast) {
563 ifra.ifra_broadaddr = blank_sin;
564 ((struct sockaddr_in *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
565 }
566 return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()));
567 }
568
569 static int
570 route_cmd(int cmd, struct in_addr d, struct in_addr g,
571 struct in_addr m, u_long more_flags)
572 {
573 struct sockaddr_in dst;
574 u_long flags = RTF_UP | RTF_STATIC;
575 struct sockaddr_in gw;
576 struct sockaddr_in mask;
577
578 flags |= more_flags;
579
580 /* destination */
581 bzero((caddr_t)&dst, sizeof(dst));
582 dst.sin_len = sizeof(dst);
583 dst.sin_family = AF_INET;
584 dst.sin_addr = d;
585
586 /* gateway */
587 bzero((caddr_t)&gw, sizeof(gw));
588 gw.sin_len = sizeof(gw);
589 gw.sin_family = AF_INET;
590 gw.sin_addr = g;
591
592 /* mask */
593 bzero(&mask, sizeof(mask));
594 mask.sin_len = sizeof(mask);
595 mask.sin_family = AF_INET;
596 mask.sin_addr = m;
597
598 return (rtrequest(cmd, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
599 (struct sockaddr *)&mask, flags, NULL));
600 }
601
602 static int
603 default_route_add(struct in_addr router, boolean_t proxy_arp)
604 {
605 u_long flags = 0;
606 struct in_addr zeroes = { 0 };
607
608 if (proxy_arp == FALSE) {
609 flags |= RTF_GATEWAY;
610 }
611 return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags));
612 }
613
614 static int
615 host_route_delete(struct in_addr host)
616 {
617 struct in_addr zeroes = { 0 };
618
619 return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST));
620 }
621
622 static struct ifnet *
623 find_interface(void)
624 {
625 struct ifnet * ifp = NULL;
626
627 if (rootdevice[0]) {
628 ifp = ifunit(rootdevice);
629 }
630 if (ifp == NULL) {
631 ifnet_head_lock_shared();
632 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
633 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
634 break;
635 ifnet_head_done();
636 }
637 return (ifp);
638 }
639
640 int
641 netboot_mountroot(void)
642 {
643 int error = 0;
644 struct in_addr iaddr = { 0 };
645 struct ifreq ifr;
646 struct ifnet * ifp;
647 struct in_addr netmask = { 0 };
648 struct proc * procp = current_proc();
649 struct in_addr router = { 0 };
650 struct socket * so = NULL;
651 unsigned int try;
652
653 bzero(&ifr, sizeof(ifr));
654
655
656 /* find the interface */
657 ifp = find_interface();
658 if (ifp == NULL) {
659 printf("netboot: no suitable interface\n");
660 error = ENXIO;
661 goto failed;
662 }
663 sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
664 printf("netboot: using network interface '%s'\n", ifr.ifr_name);
665
666 /* bring it up */
667 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) {
668 printf("netboot: socreate, error=%d\n", error);
669 goto failed;
670 }
671 ifr.ifr_flags = ifp->if_flags | IFF_UP;
672 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp);
673 if (error) {
674 printf("netboot: SIFFLAGS, error=%d\n", error);
675 goto failed;
676 }
677
678 /* grab information from the registry */
679 if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
680 /* use BOOTP to retrieve IP address, netmask and router */
681 error = bootp(ifp, &iaddr, 32, &netmask, &router, procp);
682 if (error) {
683 printf("netboot: BOOTP failed %d\n", error);
684 goto failed;
685 }
686 }
687 printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
688 if (netmask.s_addr) {
689 printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
690 }
691 if (router.s_addr) {
692 printf(" router " IP_FORMAT, IP_LIST(&router));
693 }
694 printf("\n");
695 error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL);
696 if (error) {
697 printf("netboot: inet_aifaddr failed, %d\n", error);
698 goto failed;
699 }
700 if (router.s_addr == 0) {
701 /* enable proxy arp if we don't have a router */
702 router.s_addr = iaddr.s_addr;
703 }
704 printf("netboot: adding default route " IP_FORMAT "\n",
705 IP_LIST(&router));
706 error = default_route_add(router, router.s_addr == iaddr.s_addr);
707 if (error) {
708 printf("netboot: default_route_add failed %d\n", error);
709 }
710
711 soclose(so);
712
713 S_netboot_info_p = netboot_info_init(iaddr);
714 switch (S_netboot_info_p->image_type) {
715 default:
716 case kNetBootImageTypeNFS:
717 for (try = 1; TRUE; try++) {
718 error = nfs_mountroot();
719 if (error == 0) {
720 break;
721 }
722 printf("netboot: nfs_mountroot() attempt %u failed; "
723 "clearing ARP entry and trying again\n", try);
724 /*
725 * error is either EHOSTDOWN or EHOSTUNREACH, which likely means
726 * that the port we're plugged into has spanning tree enabled,
727 * and either the router or the server can't answer our ARP
728 * requests. Clear the incomplete ARP entry by removing the
729 * appropriate route, depending on the error code:
730 * EHOSTDOWN NFS server's route
731 * EHOSTUNREACH router's route
732 */
733 switch (error) {
734 default:
735 /* NOT REACHED */
736 case EHOSTDOWN:
737 /* remove the server's arp entry */
738 error = host_route_delete(S_netboot_info_p->server_ip);
739 if (error) {
740 printf("netboot: host_route_delete(" IP_FORMAT
741 ") failed %d\n",
742 IP_LIST(&S_netboot_info_p->server_ip), error);
743 }
744 break;
745 case EHOSTUNREACH:
746 error = host_route_delete(router);
747 if (error) {
748 printf("netboot: host_route_delete(" IP_FORMAT
749 ") failed %d\n", IP_LIST(&router), error);
750 }
751 break;
752 }
753 }
754 break;
755 case kNetBootImageTypeHTTP:
756 error = netboot_setup(procp);
757 break;
758 }
759 if (error == 0) {
760 S_netboot = 1;
761 }
762 else {
763 S_netboot = 0;
764 }
765 return (error);
766 failed:
767 if (so != NULL) {
768 soclose(so);
769 }
770 return (error);
771 }
772
773 int
774 netboot_setup(struct proc * p)
775 {
776 dev_t dev;
777 int error = 0;
778
779 if (S_netboot_info_p == NULL
780 || S_netboot_info_p->image_path == NULL) {
781 goto done;
782 }
783 if (S_netboot_info_p->use_hdix) {
784 printf("netboot_setup: calling di_root_image\n");
785 error = di_root_image(S_netboot_info_p->image_path,
786 rootdevice, &dev);
787 if (error) {
788 printf("netboot_setup: di_root_image: failed %d\n", error);
789 goto done;
790 }
791 }
792 else {
793 printf("netboot_setup: calling vndevice_root_image\n");
794 error = vndevice_root_image(S_netboot_info_p->image_path,
795 rootdevice, &dev);
796 if (error) {
797 printf("netboot_setup: vndevice_root_image: failed %d\n", error);
798 goto done;
799 }
800 }
801 rootdev = dev;
802 mountroot = NULL;
803 printf("netboot: root device 0x%x\n", rootdev);
804 error = vfs_mountroot();
805 if (error == 0 && rootvnode != NULL) {
806 struct vnode *tvp;
807 struct vnode *newdp;
808 struct vfs_context context;
809
810 context.vc_proc = p;
811 context.vc_ucred = proc_ucred(p); /* XXX kauth_cred_get() ??? proxy */
812
813 /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
814 if (VFS_ROOT(mountlist.tqh_last, &newdp, &context))
815 panic("netboot_setup: cannot find root vnode");
816 vnode_ref(newdp);
817 vnode_put(newdp);
818 tvp = rootvnode;
819 vnode_rele(tvp);
820 filedesc0.fd_cdir = newdp;
821 rootvnode = newdp;
822 mount_list_lock();
823 TAILQ_REMOVE(&mountlist, TAILQ_FIRST(&mountlist), mnt_list);
824 mount_list_unlock();
825 mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
826 }
827 done:
828 netboot_info_free(&S_netboot_info_p);
829 return (error);
830 }
831
832 int
833 netboot_root(void)
834 {
835 return (S_netboot);
836 }