2 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
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
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.
23 * @APPLE_LICENSE_HEADER_END@
28 * 14 December, 2001 Dieter Siegmund (dieter@apple.com)
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
35 #include <sys/ioctl.h>
37 #include <sys/mount.h>
39 #include <sys/filedesc.h>
40 #include <sys/vnode.h>
41 #include <sys/malloc.h>
42 #include <sys/socket.h>
43 #include <sys/reboot.h>
45 #include <net/if_dl.h>
46 #include <net/if_types.h>
47 #include <net/route.h>
48 #include <netinet/in.h>
49 #include <netinet/if_ether.h>
50 #include <netinet/dhcp_options.h>
53 //#include <libkern/libkern.h>
55 extern dev_t rootdev
; /* device of the root */
56 extern struct filedesc filedesc0
;
58 extern char * strchr(const char *str
, int ch
);
60 extern int nfs_mountroot(); /* nfs_vfsops.c */
61 extern int (*mountroot
)(void);
63 extern unsigned char rootdevice
[];
65 static int S_netboot
= 0;
66 static struct netboot_info
* S_netboot_info_p
;
69 IOBSDRegistryEntryForDeviceTree(char * path
);
72 IOBSDRegistryEntryRelease(void * entry
);
75 IOBSDRegistryEntryGetData(void * entry
, char * property_name
,
78 extern int vndevice_root_image(const char * path
, char devname
[],
80 extern int di_root_image(const char *path
, char devname
[], dev_t
*dev_p
);
83 static boolean_t path_getfile
__P((char * image_path
,
84 struct sockaddr_in
* sin_p
,
85 char * serv_name
, char * pathname
));
87 #define BOOTP_RESPONSE "bootp-response"
88 #define BSDP_RESPONSE "bsdp-response"
89 #define DHCP_RESPONSE "dhcp-response"
92 bootp(struct ifnet
* ifp
, struct in_addr
* iaddr_p
, int max_retry
,
93 struct in_addr
* netmask_p
, struct in_addr
* router_p
,
96 #define IP_FORMAT "%d.%d.%d.%d"
97 #define IP_CH(ip) ((u_char *)ip)
98 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
100 #define kNetBootRootPathPrefixNFS "nfs:"
101 #define kNetBootRootPathPrefixHTTP "http:"
104 kNetBootImageTypeUnknown
= 0,
105 kNetBootImageTypeNFS
= 1,
106 kNetBootImageTypeHTTP
= 2,
109 struct netboot_info
{
110 struct in_addr client_ip
;
111 struct in_addr server_ip
;
113 int server_name_length
;
115 int mount_point_length
;
117 int image_path_length
;
118 NetBootImageType image_type
;
123 inet_aton(char * cp
, struct in_addr
* pin
)
125 u_char
* b
= (char *)pin
;
129 for (p
= cp
, i
= 0; i
< 4; i
++) {
130 u_long l
= strtoul(p
, 0, 0);
135 if (i
< 3 && p
== NULL
)
143 * Function: parse_booter_path
145 * Parse a string of the form:
146 * "<IP>:<host>:<mount>[:<image_path>]"
147 * into the given ip address, host, mount point, and optionally, image_path.
150 * The passed in string is modified i.e. ':' is replaced by '\0'.
152 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
154 static __inline__ boolean_t
155 parse_booter_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
156 char * * mount_dir
, char * * image_path
)
163 colon
= strchr(start
, ':');
168 if (inet_aton(start
, iaddr_p
) != 1) {
174 colon
= strchr(start
, ':');
183 colon
= strchr(start
, ':');
198 * Function: find_colon
200 * Find the next unescaped instance of the colon character.
201 * If a colon is escaped (preceded by a backslash '\' character),
202 * shift the string over by one character to overwrite the backslash.
204 static __inline__
char *
205 find_colon(char * str
)
210 while ((colon
= strchr(start
, ':')) != NULL
) {
214 if (colon
== start
) {
217 if (colon
[-1] != '\\')
219 for (dst
= colon
- 1, src
= colon
; *dst
!= '\0'; dst
++, src
++) {
228 * Function: parse_netboot_path
230 * Parse a string of the form:
231 * "nfs:<IP>:<mount>[:<image_path>]"
232 * into the given ip address, host, mount point, and optionally, image_path.
234 * - the passed in string is modified i.e. ':' is replaced by '\0'
235 * - literal colons must be escaped with a backslash
238 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
239 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
241 static __inline__ boolean_t
242 parse_netboot_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
243 char * * mount_dir
, char * * image_path
)
248 if (strncmp(path
, kNetBootRootPathPrefixNFS
,
249 strlen(kNetBootRootPathPrefixNFS
)) != 0) {
254 start
= path
+ strlen(kNetBootRootPathPrefixNFS
);
255 colon
= strchr(start
, ':');
260 if (inet_aton(start
, iaddr_p
) != 1) {
266 colon
= find_colon(start
);
275 (void)find_colon(start
);
278 *host
= inet_ntoa(*iaddr_p
);
283 parse_image_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
284 char * * mount_dir
, char * * image_path
)
286 if (path
[0] >= '0' && path
[0] <= '9') {
287 return (parse_booter_path(path
, iaddr_p
, host
, mount_dir
,
290 return (parse_netboot_path(path
, iaddr_p
, host
, mount_dir
,
295 get_root_path(char * root_path
)
298 boolean_t found
= FALSE
;
302 entry
= IOBSDRegistryEntryForDeviceTree("/chosen");
306 pkt
= IOBSDRegistryEntryGetData(entry
, BSDP_RESPONSE
, &pkt_len
);
307 if (pkt
!= NULL
&& pkt_len
>= sizeof(struct dhcp
)) {
308 printf("netboot: retrieving root path from BSDP response\n");
311 pkt
= IOBSDRegistryEntryGetData(entry
, BOOTP_RESPONSE
,
313 if (pkt
!= NULL
&& pkt_len
>= sizeof(struct dhcp
)) {
314 printf("netboot: retrieving root path from BOOTP response\n");
323 reply
= (struct dhcp
*)pkt
;
324 (void)dhcpol_parse_packet(&options
, reply
, pkt_len
, NULL
);
326 path
= (char *)dhcpol_find(&options
,
327 dhcptag_root_path_e
, &len
, NULL
);
329 bcopy(path
, root_path
, len
);
330 root_path
[len
] = '\0';
334 IOBSDRegistryEntryRelease(entry
);
339 static struct netboot_info
*
340 netboot_info_init(struct in_addr iaddr
)
342 struct netboot_info
* info
;
343 char * root_path
= NULL
;
344 boolean_t use_hdix
= TRUE
;
345 char * vndevice
= NULL
;
347 MALLOC_ZONE(vndevice
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
348 if (PE_parse_boot_arg("vndevice", vndevice
) == TRUE
) {
351 _FREE_ZONE(vndevice
, MAXPATHLEN
, M_NAMEI
);
353 info
= (struct netboot_info
*)kalloc(sizeof(*info
));
354 bzero(info
, sizeof(*info
));
355 info
->client_ip
= iaddr
;
356 info
->image_type
= kNetBootImageTypeUnknown
;
357 info
->use_hdix
= use_hdix
;
359 /* check for a booter-specified path then a NetBoot path */
360 MALLOC_ZONE(root_path
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
361 if (PE_parse_boot_arg("rp", root_path
) == TRUE
362 || PE_parse_boot_arg("rootpath", root_path
) == TRUE
363 || get_root_path(root_path
) == TRUE
) {
364 char * server_name
= NULL
;
365 char * mount_point
= NULL
;
366 char * image_path
= NULL
;
367 struct in_addr server_ip
;
369 if (parse_image_path(root_path
, &server_ip
, &server_name
,
370 &mount_point
, &image_path
)) {
371 info
->image_type
= kNetBootImageTypeNFS
;
372 info
->server_ip
= server_ip
;
373 info
->server_name_length
= strlen(server_name
) + 1;
374 info
->server_name
= (char *)kalloc(info
->server_name_length
);
375 info
->mount_point_length
= strlen(mount_point
) + 1;
376 info
->mount_point
= (char *)kalloc(info
->mount_point_length
);
377 strcpy(info
->server_name
, server_name
);
378 strcpy(info
->mount_point
, mount_point
);
380 printf("Server %s Mount %s",
381 server_name
, info
->mount_point
);
382 if (image_path
!= NULL
) {
383 boolean_t needs_slash
;
385 info
->image_path_length
= strlen(image_path
) + 1;
386 if (image_path
[0] != '/') {
388 info
->image_path_length
++;
390 info
->image_path
= (char *)kalloc(info
->image_path_length
);
392 info
->image_path
[0] = '/';
393 strcpy(info
->image_path
+ 1, image_path
);
396 strcpy(info
->image_path
, image_path
);
398 printf(" Image %s", info
->image_path
);
402 else if (strncmp(root_path
, kNetBootRootPathPrefixHTTP
,
403 strlen(kNetBootRootPathPrefixHTTP
)) == 0) {
404 /* only HDIX supports HTTP */
405 info
->image_type
= kNetBootImageTypeHTTP
;
406 info
->use_hdix
= TRUE
;
407 info
->image_path_length
= strlen(root_path
) + 1;
408 info
->image_path
= (char *)kalloc(info
->image_path_length
);
409 strcpy(info
->image_path
, root_path
);
412 printf("netboot: root path uses unrecognized format\n");
415 _FREE_ZONE(root_path
, MAXPATHLEN
, M_NAMEI
);
420 netboot_info_free(struct netboot_info
* * info_p
)
422 struct netboot_info
* info
= *info_p
;
425 if (info
->mount_point
) {
426 kfree(info
->mount_point
, info
->mount_point_length
);
428 if (info
->server_name
) {
429 kfree(info
->server_name
, info
->server_name_length
);
431 if (info
->image_path
) {
432 kfree(info
->image_path
, info
->image_path_length
);
434 kfree(info
, sizeof(*info
));
441 netboot_iaddr(struct in_addr
* iaddr_p
)
443 if (S_netboot_info_p
== NULL
)
446 *iaddr_p
= S_netboot_info_p
->client_ip
;
451 netboot_rootpath(struct in_addr
* server_ip
,
452 char * name
, int name_len
,
453 char * path
, int path_len
)
455 if (S_netboot_info_p
== NULL
)
461 if (S_netboot_info_p
->mount_point_length
== 0) {
464 if (path_len
< S_netboot_info_p
->mount_point_length
) {
465 printf("netboot: path too small %d < %d\n",
466 path_len
, S_netboot_info_p
->mount_point_length
);
469 strcpy(path
, S_netboot_info_p
->mount_point
);
470 strncpy(name
, S_netboot_info_p
->server_name
, name_len
);
471 *server_ip
= S_netboot_info_p
->server_ip
;
477 get_ip_parameters(struct in_addr
* iaddr_p
, struct in_addr
* netmask_p
,
478 struct in_addr
* router_p
)
485 entry
= IOBSDRegistryEntryForDeviceTree("/chosen");
489 pkt
= IOBSDRegistryEntryGetData(entry
, DHCP_RESPONSE
, &pkt_len
);
490 if (pkt
!= NULL
&& pkt_len
>= sizeof(struct dhcp
)) {
491 printf("netboot: retrieving IP information from DHCP response\n");
494 pkt
= IOBSDRegistryEntryGetData(entry
, BOOTP_RESPONSE
, &pkt_len
);
495 if (pkt
!= NULL
&& pkt_len
>= sizeof(struct dhcp
)) {
496 printf("netboot: retrieving IP information from BOOTP response\n");
505 reply
= (struct dhcp
*)pkt
;
506 (void)dhcpol_parse_packet(&options
, reply
, pkt_len
, NULL
);
507 *iaddr_p
= reply
->dp_yiaddr
;
508 ip
= (struct in_addr
*)
509 dhcpol_find(&options
,
510 dhcptag_subnet_mask_e
, &len
, NULL
);
514 ip
= (struct in_addr
*)
515 dhcpol_find(&options
, dhcptag_router_e
, &len
, NULL
);
520 IOBSDRegistryEntryRelease(entry
);
521 return (pkt
!= NULL
);
525 inet_aifaddr(struct socket
* so
, char * name
, const struct in_addr
* addr
,
526 const struct in_addr
* mask
,
527 const struct in_addr
* broadcast
)
529 struct sockaddr blank_sin
= { sizeof(blank_sin
), AF_INET
};
530 struct ifaliasreq ifra
;
532 bzero(&ifra
, sizeof(ifra
));
533 strncpy(ifra
.ifra_name
, name
, sizeof(ifra
.ifra_name
));
535 ifra
.ifra_addr
= blank_sin
;
536 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
= *addr
;
539 ifra
.ifra_mask
= blank_sin
;
540 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
= *mask
;
543 ifra
.ifra_broadaddr
= blank_sin
;
544 ((struct sockaddr_in
*)&ifra
.ifra_broadaddr
)->sin_addr
= *broadcast
;
546 return (ifioctl(so
, SIOCAIFADDR
, (caddr_t
)&ifra
, current_proc()));
550 default_route_add(struct in_addr router
, boolean_t proxy_arp
)
552 struct sockaddr_in dst
;
553 u_long flags
= RTF_UP
| RTF_STATIC
;
554 struct sockaddr_in gw
;
555 struct sockaddr_in mask
;
557 if (proxy_arp
== FALSE
) {
558 flags
|= RTF_GATEWAY
;
562 bzero((caddr_t
)&dst
, sizeof(dst
));
563 dst
.sin_len
= sizeof(dst
);
564 dst
.sin_family
= AF_INET
;
567 bzero((caddr_t
)&gw
, sizeof(gw
));
568 gw
.sin_len
= sizeof(gw
);
569 gw
.sin_family
= AF_INET
;
570 gw
.sin_addr
= router
;
573 bzero(&mask
, sizeof(mask
));
574 mask
.sin_len
= sizeof(mask
);
575 mask
.sin_family
= AF_INET
;
577 printf("netboot: adding default route " IP_FORMAT
"\n",
580 return (rtrequest(RTM_ADD
, (struct sockaddr
*)&dst
, (struct sockaddr
*)&gw
,
581 (struct sockaddr
*)&mask
, flags
, NULL
));
584 static struct ifnet
*
587 struct ifnet
* ifp
= NULL
;
590 ifp
= ifunit(rootdevice
);
593 TAILQ_FOREACH(ifp
, &ifnet
, if_link
)
595 (IFF_LOOPBACK
|IFF_POINTOPOINT
)) == 0)
605 struct in_addr iaddr
= { 0 };
608 struct in_addr netmask
= { 0 };
609 struct proc
* procp
= current_proc();
610 struct in_addr router
= { 0 };
611 struct socket
* so
= NULL
;
613 bzero(&ifr
, sizeof(ifr
));
615 thread_funnel_switch(KERNEL_FUNNEL
, NETWORK_FUNNEL
);
617 /* find the interface */
618 ifp
= find_interface();
620 printf("netboot: no suitable interface\n");
624 sprintf(ifr
.ifr_name
, "%s%d", ifp
->if_name
, ifp
->if_unit
);
625 printf("netboot: using network interface '%s'\n", ifr
.ifr_name
);
628 if ((error
= socreate(AF_INET
, &so
, SOCK_DGRAM
, 0)) != 0) {
629 printf("netboot: socreate, error=%d\n", error
);
632 ifr
.ifr_flags
= ifp
->if_flags
| IFF_UP
;
633 error
= ifioctl(so
, SIOCSIFFLAGS
, (caddr_t
)&ifr
, procp
);
635 printf("netboot: SIFFLAGS, error=%d\n", error
);
639 /* grab information from the registry */
640 if (get_ip_parameters(&iaddr
, &netmask
, &router
) == FALSE
) {
641 /* use BOOTP to retrieve IP address, netmask and router */
642 error
= bootp(ifp
, &iaddr
, 32, &netmask
, &router
, procp
);
644 printf("netboot: BOOTP failed %d\n", error
);
648 printf("netboot: IP address " IP_FORMAT
, IP_LIST(&iaddr
));
649 if (netmask
.s_addr
) {
650 printf(" netmask " IP_FORMAT
, IP_LIST(&netmask
));
653 printf(" router " IP_FORMAT
, IP_LIST(&router
));
656 error
= inet_aifaddr(so
, ifr
.ifr_name
, &iaddr
, &netmask
, NULL
);
658 printf("netboot: inet_aifaddr failed, %d\n", error
);
661 if (router
.s_addr
== 0) {
662 /* enable proxy arp if we don't have a router */
663 router
.s_addr
= iaddr
.s_addr
;
665 error
= default_route_add(router
, router
.s_addr
== iaddr
.s_addr
);
667 printf("netboot: default_route_add failed %d\n", error
);
671 thread_funnel_switch(NETWORK_FUNNEL
, KERNEL_FUNNEL
);
673 S_netboot_info_p
= netboot_info_init(iaddr
);
674 switch (S_netboot_info_p
->image_type
) {
676 case kNetBootImageTypeNFS
:
677 error
= nfs_mountroot();
679 case kNetBootImageTypeHTTP
:
680 error
= netboot_setup(procp
);
694 thread_funnel_switch(NETWORK_FUNNEL
, KERNEL_FUNNEL
);
699 netboot_setup(struct proc
* p
)
704 if (S_netboot_info_p
== NULL
705 || S_netboot_info_p
->image_path
== NULL
) {
708 if (S_netboot_info_p
->use_hdix
) {
709 printf("netboot_setup: calling di_root_image\n");
710 error
= di_root_image(S_netboot_info_p
->image_path
,
713 printf("netboot_setup: di_root_image: failed %d\n", error
);
718 printf("netboot_setup: calling vndevice_root_image\n");
719 error
= vndevice_root_image(S_netboot_info_p
->image_path
,
722 printf("netboot_setup: vndevice_root_image: failed %d\n", error
);
728 printf("netboot: root device 0x%x\n", rootdev
);
729 error
= vfs_mountroot();
730 if (error
== 0 && rootvnode
!= NULL
) {
734 /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
735 if (VFS_ROOT(mountlist
.cqh_last
, &newdp
))
736 panic("netboot_setup: cannot find root vnode");
740 filedesc0
.fd_cdir
= newdp
;
742 simple_lock(&mountlist_slock
);
743 CIRCLEQ_REMOVE(&mountlist
, CIRCLEQ_FIRST(&mountlist
), mnt_list
);
744 simple_unlock(&mountlist_slock
);
745 VOP_UNLOCK(rootvnode
, 0, p
);
746 mountlist
.cqh_first
->mnt_flag
|= MNT_ROOTFS
;
749 netboot_info_free(&S_netboot_info_p
);