# PXE boot, diskless and NFS



## olivier (Nov 11, 2009)

Hi all,

I'm trying to boot a PC with FreeBSD 8.0-rc2 that can't use the PATA controller  neither the USB drivers (8.0 regression).
Then my only choice are to boot this workstation using PXE.

I've followed the advice given here and I can PXE boot and load the kernel.
But just after loading the kernel when it try to mount the root filesystem, I've got this error:


```
Trying to mount root from nfs:10.0.0.1/usr/tftpboot
nfs_diskless: no interface
ROOT MOUNT ERROR:
If you have invalid mount options, reboot, and first try the following from
the loader prompt:

	set vfs.root.mountfrom.options=rw

and then remove invalid mount options from /etc/fstab.

Loader variables:
vfs.root.mountfrom=nfs:10.0.0.1:/usr/tftpboot
vfs.root.mountfrom.options=rw

(...)

mountroot>
```

Is the message *nfs_diskless: no interface* an important clue about my problem ?


----------



## crsd (Nov 11, 2009)

Quoting http://www.nber.org/sys-admin/FreeBSD-diskless.html :


> Regardless of PXE support in the motherboard firmware, your FreeBSD kernel may not support the onboard ethernet, in which case the kernel will stop with a "nfs_diskless: No interface" prompt.


----------



## olivier (Nov 12, 2009)

But my NIC drivers (nfe) is compiled in the generic kernel that I'm using: then FreeBSD should support the NIC.


----------



## wlohman (Nov 13, 2009)

&quot said:
			
		

> But my NIC drivers (nfe) is compiled in the generic kernel that I'm using: then FreeBSD should support the NIC.


You would think that, wouldn't you?

I'm new to FreeBSD internals, but I'm sure the mechanisms involved should be quite similar to Linux. I've never been able to get root over NFS without using an initrd*) as mediator. The problem is this:

Booting over PXE is always a two-stage rocket. First the motherboard of the machine calls out (over the NIC) for 1) DHCP and 2) a configuration pointing to the kernel, it's parameters and perhaps an image to load. 
The server, if configured properly (as I think it is, in your case), will gladly hand out a DHCP lease and corresponding information/configuration (IP-address, DNS, gateway) and will readily transfer the kernel and image over TFTP per request. 



The client now loads the kernel, and the second stage of the process begins. The kernel loads, probes the hardware and mounts root. Boom. Stops here. Why?
Well, normally I mount the initrd as root. The initrd begins to run it's boot sequence and therein initiates the NIC and rerequests DHCP from the server. Without this step, the NIC, although perhaps recognized by the kernel, remains dead. The configuration the server initially gave the client (IP-address, DNS, gateway) does not survive the transference of control from the BIOS to the loaded kernel.

Have you read this: Handbook (chapter Diskless operation)
I suppose you can skip most of it and look at: "31.7.2.6 Building a Diskless Kernel"

I have no first hand experience with those options, but it seems a good thing to try if you haven't already. Anyway, what I think the problem is is that the NIC doesn't get initialized and thus doesn't request for DHCP, as the kernel isn't used for this normally... normally that happens later in the boot sequence.

So the task is to find a way to get the client to activate the NIC and rerequest DHCP. I normally use an initrd for that, but from the link I understand the BSD kernel can be persuaded to do this on it's own.


*) Initial Ram Disk, or put more plainly, an image. This term seems to me in this short while to be more of a Linux thingy although I'm positive FreeBSD has either the same thing or something similar.


----------



## olivier (Nov 16, 2009)

Thanks a lot for your long reply wlohman!

I've read the file /usr/src/sys/nfsclient/nfs_diskless.c and found this information:


```
/*
 * Populate the essential fields in the nfsv3_diskless structure.
 *
 * The loader is expected to export the following environment variables:
 *
 * boot.netif.name              name of boot interface
 * 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
 * boot.nfsroot.options         NFS options for the root filesystem
```

This means that the FreeBSD loader (pxeboot in my case) doesn't export these variables.
And the pxeboot(8) have this information too:


```
pxeboot recognizes next-server and option root-path 
directives as the server and path to NFS mount for file 
requests, respectively, or the server to make TFTP requests to.
Note that pxeboot expects to fetch /boot/loader.rc from the 
specified server before loading any other files.
```

I will try to play with the loader.rc file to check if I can force some variables (I want to avoid using MFS_root file)...


----------



## olivier (Nov 18, 2009)

After tunning the file loader.rc, I've seen that the pxe loader populate correctly lot's of the needed variables (boot.netif.ip, boot.netif.netmask, boot.netif.gateway, etc...) but with the only expection of "boot.netif.name".

Then I've checked the /usr/src/sys/nfsclient/nfs_diskless.c, 
and I believe that the problem is on this part:


```
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(LLADDR(sdl),
                                          LLADDR(&ourdl),
                                          sdl->sdl_alen)) {
                                    IFNET_RUNLOCK();
                                    goto match_done;
                                }
                        }
                }
        }
        IFNET_RUNLOCK();
        printf("nfs_diskless: no interface\n");
        return; /* no matching interface */
match_done:
        setenv("boot.netif.name", ifp->if_xname);
```

But I don't know the language C and don't understand the meaning of the TAILQ_FOREACH function (Witch should go to match_done label).


----------



## olivier (Jan 7, 2010)

My configurations works with FreeBSD 7.2: This problem came from a regression bug in FreeBSD 8.0 regarding ACPI and my motherboard.
PR open:
kern/142263: [acpi] ACPI regression on Asus K8N7-E deluxe motherboard with 8-stable [regression].


----------

