diff --git a/sys/amd64/amd64/autoconf.c b/sys/amd64/amd64/autoconf.c index 61d0abbcf068..b209065027d6 100644 --- a/sys/amd64/amd64/autoconf.c +++ b/sys/amd64/amd64/autoconf.c @@ -90,6 +90,10 @@ static void configure_final __P((void *)); static void setroot __P((void)); #endif +#if defined(NFS) && defined(NFS_ROOT) +static void pxe_setup_nfsdiskless(void); +#endif + SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); @@ -232,6 +236,7 @@ cpu_rootconf() #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) + pxe_setup_nfsdiskless(); if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; @@ -322,3 +327,172 @@ setroot() sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif + +#if defined(NFS) && defined(NFS_ROOT) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct nfs_diskless nfs_diskless; + +static int +inaddr_to_sockaddr(char *ev, struct sockaddr_in *sa) +{ + u_int32_t a[4]; + char *cp; + + bzero(sa, sizeof(*sa)); + sa->sin_len = sizeof(*sa); + sa->sin_family = AF_INET; + + if ((cp = getenv(ev)) == NULL) + return(1); + if (sscanf(cp, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]) != 4) + return(1); + /* XXX is this ordering correct? */ + sa->sin_addr.s_addr = (a[3] << 24) + (a[2] << 16) + (a[1] << 8) + a[0]; + return(0); +} + +static int +hwaddr_to_sockaddr(char *ev, struct sockaddr_dl *sa) +{ + char *cp; + u_int32_t a[6]; + + bzero(sa, sizeof(*sa)); + sa->sdl_len = sizeof(*sa); + sa->sdl_family = AF_LINK; + sa->sdl_type = IFT_ETHER; + sa->sdl_alen = ETHER_ADDR_LEN; + if ((cp = getenv(ev)) == NULL) + return(1); + if (sscanf(cp, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) + return(1); + sa->sdl_data[0] = a[0]; + sa->sdl_data[1] = a[1]; + sa->sdl_data[2] = a[2]; + sa->sdl_data[3] = a[3]; + sa->sdl_data[4] = a[4]; + sa->sdl_data[5] = a[5]; + return(0); +} + +static int +decode_nfshandle(char *ev, u_char *fh) +{ + u_char *cp; + int len, val; + + if (((cp = getenv(ev)) == NULL) || (strlen(cp) < 2) || (*cp != 'X')) + return(0); + len = 0; + cp++; + for (;;) { + if (*cp == 'X') + return(len); + if ((sscanf(cp, "%2x", &val) != 1) || (val > 0xff)) + return(0); + *(fh++) = val; + len++; + cp += 2; + if (len > NFSX_V2FH) + return(0); + } +} + +/* + * Populate the essential fields in the nfsv3_diskless structure. + * + * The loader is expected to export the following environment variables: + * + * boot.netif.ip IP address on boot interface + * boot.netif.netmask netmask on boot interface + * boot.netif.gateway default gateway (optional) + * boot.netif.hwaddr hardware address of boot interface + * boot.nfsroot.server IP address of root filesystem server + * boot.nfsroot.path path of the root filesystem on server + * boot.nfsroot.nfshandle NFS handle for root filesystem on server + */ +static void +pxe_setup_nfsdiskless() +{ + struct nfs_diskless *nd = &nfs_diskless; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_dl *sdl, ourdl; + struct sockaddr_in myaddr, netmask; + char *cp; + + /* set up interface */ + if (inaddr_to_sockaddr("boot.netif.ip", &myaddr)) + return; + if (inaddr_to_sockaddr("boot.netif.netmask", &netmask)) { + printf("PXE: no netmask\n"); + return; + } + bcopy(&myaddr, &nd->myif.ifra_addr, sizeof(myaddr)); + bcopy(&myaddr, &nd->myif.ifra_broadaddr, sizeof(myaddr)); + ((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr = + myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; + bcopy(&netmask, &nd->myif.ifra_mask, sizeof(netmask)); + + if (hwaddr_to_sockaddr("boot.netif.hwaddr", &ourdl)) { + printf("PXE: no hardware address\n"); + return; + } + ifa = NULL; + ifp = TAILQ_FIRST(&ifnet); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if ((ifa->ifa_addr->sa_family == AF_LINK) && + (sdl = ((struct sockaddr_dl *)ifa->ifa_addr))) { + if ((sdl->sdl_type == ourdl.sdl_type) && + (sdl->sdl_alen == ourdl.sdl_alen) && + !bcmp(sdl->sdl_data + sdl->sdl_nlen, + ourdl.sdl_data + ourdl.sdl_nlen, + sdl->sdl_alen)) + goto match_done; + } + } + } + printf("PXE: no interface\n"); + return; /* no matching interface */ +match_done: + sprintf(nd->myif.ifra_name, "%s%d", ifp->if_name, ifp->if_unit); + + + /* set up gateway */ + inaddr_to_sockaddr("boot.netif.gateway", &nd->mygateway); + + /* XXX set up swap? */ + + /* set up root mount */ + nd->root_args.rsize = 8192; /* XXX tunable? */ + nd->root_args.wsize = 8192; + nd->root_args.sotype = SOCK_DGRAM; + nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT); + if (inaddr_to_sockaddr("boot.nfsroot.server", &nd->root_saddr)) { + printf("PXE: no server\n"); + return; + } + nd->root_saddr.sin_port = htons(NFS_PORT); + if (decode_nfshandle("boot.nfsroot.nfshandle", &nd->root_fh[0]) == 0) { + printf("PXE: no NFS handle\n"); + return; + } + if ((cp = getenv("boot.nfsroot.path")) != NULL) + strncpy(nd->root_hostnam, cp, MNAMELEN - 1); + + nfs_diskless_valid = 1; +} +#endif diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c index 61d0abbcf068..b209065027d6 100644 --- a/sys/i386/i386/autoconf.c +++ b/sys/i386/i386/autoconf.c @@ -90,6 +90,10 @@ static void configure_final __P((void *)); static void setroot __P((void)); #endif +#if defined(NFS) && defined(NFS_ROOT) +static void pxe_setup_nfsdiskless(void); +#endif + SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); @@ -232,6 +236,7 @@ cpu_rootconf() #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) + pxe_setup_nfsdiskless(); if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; @@ -322,3 +327,172 @@ setroot() sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif + +#if defined(NFS) && defined(NFS_ROOT) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct nfs_diskless nfs_diskless; + +static int +inaddr_to_sockaddr(char *ev, struct sockaddr_in *sa) +{ + u_int32_t a[4]; + char *cp; + + bzero(sa, sizeof(*sa)); + sa->sin_len = sizeof(*sa); + sa->sin_family = AF_INET; + + if ((cp = getenv(ev)) == NULL) + return(1); + if (sscanf(cp, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]) != 4) + return(1); + /* XXX is this ordering correct? */ + sa->sin_addr.s_addr = (a[3] << 24) + (a[2] << 16) + (a[1] << 8) + a[0]; + return(0); +} + +static int +hwaddr_to_sockaddr(char *ev, struct sockaddr_dl *sa) +{ + char *cp; + u_int32_t a[6]; + + bzero(sa, sizeof(*sa)); + sa->sdl_len = sizeof(*sa); + sa->sdl_family = AF_LINK; + sa->sdl_type = IFT_ETHER; + sa->sdl_alen = ETHER_ADDR_LEN; + if ((cp = getenv(ev)) == NULL) + return(1); + if (sscanf(cp, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) + return(1); + sa->sdl_data[0] = a[0]; + sa->sdl_data[1] = a[1]; + sa->sdl_data[2] = a[2]; + sa->sdl_data[3] = a[3]; + sa->sdl_data[4] = a[4]; + sa->sdl_data[5] = a[5]; + return(0); +} + +static int +decode_nfshandle(char *ev, u_char *fh) +{ + u_char *cp; + int len, val; + + if (((cp = getenv(ev)) == NULL) || (strlen(cp) < 2) || (*cp != 'X')) + return(0); + len = 0; + cp++; + for (;;) { + if (*cp == 'X') + return(len); + if ((sscanf(cp, "%2x", &val) != 1) || (val > 0xff)) + return(0); + *(fh++) = val; + len++; + cp += 2; + if (len > NFSX_V2FH) + return(0); + } +} + +/* + * Populate the essential fields in the nfsv3_diskless structure. + * + * The loader is expected to export the following environment variables: + * + * boot.netif.ip IP address on boot interface + * boot.netif.netmask netmask on boot interface + * boot.netif.gateway default gateway (optional) + * boot.netif.hwaddr hardware address of boot interface + * boot.nfsroot.server IP address of root filesystem server + * boot.nfsroot.path path of the root filesystem on server + * boot.nfsroot.nfshandle NFS handle for root filesystem on server + */ +static void +pxe_setup_nfsdiskless() +{ + struct nfs_diskless *nd = &nfs_diskless; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_dl *sdl, ourdl; + struct sockaddr_in myaddr, netmask; + char *cp; + + /* set up interface */ + if (inaddr_to_sockaddr("boot.netif.ip", &myaddr)) + return; + if (inaddr_to_sockaddr("boot.netif.netmask", &netmask)) { + printf("PXE: no netmask\n"); + return; + } + bcopy(&myaddr, &nd->myif.ifra_addr, sizeof(myaddr)); + bcopy(&myaddr, &nd->myif.ifra_broadaddr, sizeof(myaddr)); + ((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr = + myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; + bcopy(&netmask, &nd->myif.ifra_mask, sizeof(netmask)); + + if (hwaddr_to_sockaddr("boot.netif.hwaddr", &ourdl)) { + printf("PXE: no hardware address\n"); + return; + } + ifa = NULL; + ifp = TAILQ_FIRST(&ifnet); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if ((ifa->ifa_addr->sa_family == AF_LINK) && + (sdl = ((struct sockaddr_dl *)ifa->ifa_addr))) { + if ((sdl->sdl_type == ourdl.sdl_type) && + (sdl->sdl_alen == ourdl.sdl_alen) && + !bcmp(sdl->sdl_data + sdl->sdl_nlen, + ourdl.sdl_data + ourdl.sdl_nlen, + sdl->sdl_alen)) + goto match_done; + } + } + } + printf("PXE: no interface\n"); + return; /* no matching interface */ +match_done: + sprintf(nd->myif.ifra_name, "%s%d", ifp->if_name, ifp->if_unit); + + + /* set up gateway */ + inaddr_to_sockaddr("boot.netif.gateway", &nd->mygateway); + + /* XXX set up swap? */ + + /* set up root mount */ + nd->root_args.rsize = 8192; /* XXX tunable? */ + nd->root_args.wsize = 8192; + nd->root_args.sotype = SOCK_DGRAM; + nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT); + if (inaddr_to_sockaddr("boot.nfsroot.server", &nd->root_saddr)) { + printf("PXE: no server\n"); + return; + } + nd->root_saddr.sin_port = htons(NFS_PORT); + if (decode_nfshandle("boot.nfsroot.nfshandle", &nd->root_fh[0]) == 0) { + printf("PXE: no NFS handle\n"); + return; + } + if ((cp = getenv("boot.nfsroot.path")) != NULL) + strncpy(nd->root_hostnam, cp, MNAMELEN - 1); + + nfs_diskless_valid = 1; +} +#endif