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