3. Diskless installation

Creating an embedded system with no mass memory offers several benefits:

but there are also some drawbacks:

So let's get to the configuration! We need to set up a boot server, on which most of the installation will take place; all we need from the embedded device is its MAC address. To get it, you just have to attach to the console and power it up:

# cu -s 19200 -l cua00

comBIOS ver. 1.26a  20040819  Copyright (C) 2000-2004 Soekris Engineering.

net45xx

0064 Mbyte Memory                        CPU 80486 133 Mhz 


Slot   Vend Dev  ClassRev Cmd  Stat CL LT HT  Base1    Base2   Int 
-------------------------------------------------------------------
0:00:0 1022 3000 06000000 0006 2280 00 00 00 00000000 00000000 
0:17:0 104C AC51 06070000 0107 0210 10 3F 82 A0000000 020000A0 10
0:17:1 104C AC51 06070000 0107 0210 10 3F 82 A0001000 020000A0 10
0:18:0 100B 0020 02000000 0107 0290 00 3F 00 0000E101 A0002000 11
0:19:0 100B 0020 02000000 0107 0290 00 3F 00 0000E201 A0003000 05

 1 Seconds to automatic boot.   Press Ctrl-P for entering Monitor.

NSC DP83815/DP83816 Fast Ethernet UNDI, v1.03                                  
Copyright (C) 2002, 2003 National Semiconductor Corporation
All rights reserved.

Pre-boot eXecution Environment  PXE-2.0 (build 082)
Copyright (C) 1997-2000  Intel Corporation


CLIENT MAC ADDR: 00 00 24 C3 C1 B0  
[...]

We will now take a look at how to compile a diskless kernel, and then step through the system boot process to understand which network services we will need to set up on the boot server.

3.1 Building a custom kernel

Everything we have seen before about kernel configuration and compiling still applies; just make sure you specify, in the configuration file, that the system must look for the root and swap filesystems on NFS:

/usr/src/sys/arch/i386/conf/NET4521
[...]
config		bsd root on nfs swap on nfs
[...]

3.2 rarpd(8)

On boot, the device first tries to configure its network settings. Since it only knows its MAC address, it generates a RARP request to get an IP address. Therefore, we must enable the rarpd(8) daemon in the boot server's /etc/rc.conf.local(8) file:

/etc/rc.conf.local
rarpd_flags="-a"

If you don't want the daemon to listen on all the interfaces, just replace the "-a" parameter with the name of the interface to listen on. To honour RARP requests, the daemon uses two files:

If the requesting host does not exist in both files, the daemon won't be able to send a reply.

3.3 dhcpd(8)

Now that it has got its own IP address, the embedded device will look for the boot file. To get the file name, it will send a DHCP request, to which our server will be glad to reply. Therefore, we need to enable the dhcpd(8) daemon in the boot server's /etc/rc.conf.local(8) file:

/etc/rc.conf.local
dhcpd_flags=""

and configure it:

/etc/dhcpd.conf
[...]
# Diskless devices group
group {
	filename "pxeboot";		# Boot file
	#next-server  pxe-server;	# PXE server (if different from the DHCP server)

	host net4521 { hardware ethernet 00:00:24:c3:c1:b0; }
}
[...]

3.4 tftpd(8)

Ok, now that it knows the name of the boot file, the diskless device will attempt to download it, via tftp(1), from the server in the "next-server" parameter or from the DHCP server itself. To enable tftpd(8) on our boot server, we need to uncomment the following line in /etc/inetd.conf(8):

/etc/inetd.conf
tftp		dgram	udp	wait	root	/usr/libexec/tftpd	tftpd -s /tftpboot

create the /tftpboot directory and populate it with the appropriate files: pxeboot(8) (the second-stage PXE boot loader), bsd (the custom kernel) and /tftpboot/etc/boot.conf(8), which contains the boot parameters:

/tftpboot/etc/boot.conf
set tty com0
stty com0 19200

3.5 bootparamd(8)

Now the system will boot, until it needs to mount the NFS filesystems. To find them out, it will broadcast a BOOTPARAMS request, waiting for some rpc.bootparamd(8) daemon to tell it the parameters of the NFS filesystems to mount. Therefore, we need to start the bootparamd(8) daemon on our server. Once again, we have to edit a couple of variables in /etc/rc.conf.local(8):

/etc/rc.conf.local
bootparamd_flags=""
portmap="YES"

As you can see, to make bootparamd(8) work, we need to start the portmap(8) daemon too, which converts RPC program numbers into DARPA protocol port numbers. bootparamd(8) has its own configuration file, /etc/bootparams(5), which must contain an entry for each client, specifying the pathnames for its root and (optionally) swap areas (fields are delimited with blank or tab, and entries may span across multiple lines using a back-slash):

/etc/bootparams
net4521	root=boot-srv:/exports/net4521/root/ \
	swap=boot-srv:/exports/net4521/swap

3.6 nfs

The last step to complete the boot process is to mount the NFS filesystems. Therefore, we must set up the NFS server; let's edit the /etc/rc.conf.local(8) file once again to set a couple of variables:

/etc/rc.conf.local
nfs_server="YES"
nfsd_flags="-tun 4"

and set up the filesystems to mount:

On the NFS server, the /etc/exports(5) file lists the exported filesystems and sets the hosts and export options for each one:

/etc/exports
/usr -ro 172.16.0.10
/export/net4521 -maproot=root -alldirs 172.16.0.10

The client filesystem table, /etc/fstab(5) (which, to be precise, resides on the server, in /exports/net4521/root/etc/fstab), will look like:

/etc/fstab
boot-srv:/exports/net4521/root	/	nfs	rw	0 0
boot-srv:/usr			/usr	nfs	rw	0 0

Now we only have to power up the device and, if some champagne remained from the previous chapter, now is time to go get it and finish it.