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.
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:
[...] config bsd root on nfs swap on nfs [...]
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:
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:
00:00:24:c3:c1:b0 net4521.kernel-panic.it
172.16.0.10 net4521.kernel-panic.it
If the requesting host does not exist in both files, the daemon won't be able to send a reply.
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:
dhcpd_flags=""
and configure it:
[...] # 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; } } [...]
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):
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:
set tty com0 stty com0 19200
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):
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):
net4521 root=boot-srv:/exports/net4521/root/ \ swap=boot-srv:/exports/net4521/swap
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:
nfs_server="YES" nfsd_flags="-tun 4"
and set up the filesystems to mount:
# dd if=/dev/zero of=/exports/net4521/swap bs=1m count=128
On the NFS server, the /etc/exports(5) file lists the exported filesystems and sets the hosts and export options for each one:
/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:
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.