Index: sys/boot/common/boot.c =================================================================== --- sys/boot/common/boot.c +++ sys/boot/common/boot.c @@ -31,11 +31,14 @@ * Loading modules, booting the system */ +#include + #include #include #include "bootstrap.h" +static int announce_boot_intent(void); static char *getbootfile(int try); static int loadakernel(int try, int argc, char* argv[]); @@ -55,6 +58,11 @@ struct preloaded_file *fp; /* + * Inform the device that we're about to boot. + */ + announce_boot_intent(); + + /* * See if the user has specified an explicit kernel to boot. */ if ((argc > 1) && (argv[1][0] != '-')) { @@ -162,6 +170,7 @@ int c, yes; char *argv[2], *cp, *ep; char *kernelname; + char *kernel_other_nfs; #ifdef BOOT_PROMPT_123 const char *seq = "123", *p = seq; #endif @@ -178,6 +187,25 @@ } } + /* See if we are asked to swap NFS handle early. */ + kernel_other_nfs = getenv("kernel_other_nfs"); + if (kernel_other_nfs != NULL) { + announce_boot_intent(); + + /* + * If we were triggered to do this. Try to re-start, which we + * cannot easily, thus just re-read the config file(s) from the + * new location and cross our fingers. + */ + /* + * Read our default configuration + */ + if (include("/boot/loader.rc") != CMD_OK) + include("/boot/boot.conf"); + printf("\n"); + + } + kernelname = getenv("kernelname"); if (kernelname == NULL) { argv[0] = NULL; @@ -395,6 +423,31 @@ } static int +announce_boot_intent(void) +{ + struct devdesc *dev; + int result; + const char *currdev; + const char *rootpath; /* ignored */ + + /* + * Send a 'flush write cache' ioctl to the boot device. + * PXE will interpret this as "ensure your NFS handle matches + * 'vfs.root.mountfrom', if one is set". + */ + + currdev = getenv("currdev"); + if (currdev == NULL) + return (0); + + result = archsw.arch_getdev((void **)&dev, currdev, &rootpath); + if (result) + return (result); + + return dev->d_dev->dv_ioctl(NULL, DIOCGFLUSH, NULL); +} + +static int loadakernel(int try, int argc, char* argv[]) { char *cp; Index: sys/boot/i386/libi386/pxe.c =================================================================== --- sys/boot/i386/libi386/pxe.c +++ sys/boot/i386/libi386/pxe.c @@ -29,6 +29,9 @@ #include __FBSDID("$FreeBSD$"); +#include + +#include #include #include #include @@ -75,6 +78,7 @@ size_t size, char *buf, size_t *rsize); static int pxe_open(struct open_file *f, ...); static int pxe_close(struct open_file *f); +static int pxe_ioctl(struct open_file *f, u_long cmd, void *data); static void pxe_print(int verbose); static void pxe_cleanup(void); static void pxe_setnfshandle(char *rootpath); @@ -88,6 +92,14 @@ static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); static void pxe_netif_end(struct netif *nif); +#ifdef LOADER_NFS_SUPPORT +extern int rpc_port; +static int pxe_compareroot(const in_addr_t addr0, const char *path0, + const in_addr_t addr1, const char *path1); +static int pxe_pivot_nfsroot(const char *mountspec); +static void pxe_rpcmountcall(void); +#endif + #ifdef OLD_NFSV2 int nfs_getrootfh(struct iodesc*, char*, u_char*); #else @@ -133,7 +145,7 @@ pxe_strategy, pxe_open, pxe_close, - noioctl, + pxe_ioctl, pxe_print, pxe_cleanup }; @@ -358,6 +370,121 @@ return (0); } +static int +pxe_ioctl(struct open_file *f, u_long cmd, void *data) +{ +#ifdef LOADER_NFS_SUPPORT + /* + * If we receive the 'flush your write cache' ioctl, check for a + * mismatch between boot.nfsroot.* and vfs.root.mountfrom. + * + * We may need to pivot from the NFS boot directory to a different + * NFS root directory. + */ + if (cmd == DIOCGFLUSH) + return pxe_pivot_nfsroot(getenv("vfs.root.mountfrom")); +#endif + + return (0); +} + +#ifdef LOADER_NFS_SUPPORT +static int +pxe_pivot_nfsroot(const char *mountspec) +{ + static const char NFS_PREFIX[] = "nfs:"; + + /* + * This function should only be called after all boot files have been + * loaded and the PXE TFTP stack has been shut down. + */ + assert(pxe_opens == 0); + assert(pxe_sock == -1); + + /* + * Ignore all mountfrom specifications that aren't NFS; the kernel + * can handle them without loader assistance. + */ + if (mountspec == NULL) + return (0); + + if (strncmp(mountspec, NFS_PREFIX, sizeof(NFS_PREFIX) - 1) != 0) { +#ifdef PXE_DEBUG + if (pxe_debug) + printf("pxe_pivot_nfsroot: ignoring non-NFS root '%s'\n", + mountspec); +#endif + return (0); + } + + /* Ignore the 'nfs:' prefix; we know that we're doing NFS. */ + mountspec += sizeof(NFS_PREFIX) - 1; + + + /* + * Compare the current root with vfs.root.mountfrom. + * If they're the same, there's no need to pivot. + */ + struct in_addr next_ip = { .s_addr = 0 }; + const char *next_path; + + for (int i = 0; i < FNAME_SIZE; i++) + if (mountspec[i] == ':') { + /* inet_addr() needs a null-terminated string. */ + char addr[i + 1]; + bcopy(mountspec, addr, i); + addr[i] = '\0'; + + next_ip.s_addr = inet_addr(addr); + next_path = mountspec + i + 1; + break; + } + + if (next_ip.s_addr == 0) { + printf("NFS MOUNT ERROR: NFS root '%s' does not contain ':'\n", + mountspec); + return (EINVAL); + } + + if (pxe_compareroot(rootip.s_addr, rootpath, + next_ip.s_addr, next_path) == 0) + return (0); + + + /* + * Do the pivot to the root filesystem. + */ + printf("pxe_pivot_nfsroot:\n"); + printf(" boot %s:%s\n", inet_ntoa(rootip), rootpath); + printf(" root %s:%s\n", inet_ntoa(next_ip), next_path); + + /* + * pxe_setnfshandle ignores its argument: it uses the global + * 'rootip' and 'rootpath' variables. + */ + rootip = next_ip; + strncpy(rootpath, next_path, FNAME_SIZE); + + pxe_sock = netif_open(NULL); + + pxe_rpcmountcall(); + pxe_setnfshandle(NULL); + setenv("boot.nfsroot.path", next_path, 1); + setenv("boot.nfsroot.server", inet_ntoa(next_ip), 1); + setenv("dhcp.root-path", mountspec, 1); + + netif_close(pxe_sock); + pxe_sock = -1; + +#ifdef PXE_DEBUG + if (pxe_debug) + pxe_print(0); +#endif + + return (0); +} +#endif /* LOADER_NFS_SUPPORT */ + static void pxe_print(int verbose) { @@ -417,7 +544,6 @@ /* structure truncated here */ }; extern struct nfs_iodesc nfs_root_node; -extern int rpc_port; static void pxe_rpcmountcall() @@ -469,7 +595,6 @@ /* structure truncated */ }; extern struct nfs_iodesc nfs_root_node; -extern int rpc_port; static void pxe_rpcmountcall() @@ -477,8 +602,10 @@ struct iodesc *d; int error; - if (!(d = socktodesc(pxe_sock))) + if (!(d = socktodesc(pxe_sock))) { + printf("NFS MOUNT ERROR: no PXE socket %d\n", pxe_sock); return; + } d->myport = htons(--rpc_port); d->destip = rootip; if ((error = nfs_getrootfh(d, rootpath, &nfs_root_node.fhsize, @@ -515,6 +642,19 @@ setenv("boot.nfsroot.nfshandlelen", buf, 1); } #endif /* OLD_NFSV2 */ + + +static int +pxe_compareroot(const in_addr_t addr0, const char *path0, + const in_addr_t addr1, const char *path1) +{ + in_addr_t diff = addr0 - addr1; + + if (diff != 0) + return diff; + + return strncmp(path0, path1, FNAME_SIZE); +} #endif /* LOADER_NFS_SUPPORT */ void