Using pxeboot to install FreeBSD 5.3 on a laptop without bootable CDROM drive
This month I had
to install FreeBSD 5.3 on a laptop that lacked a bootable CDROM (a
Toshiba Portege R100). Rather than launch the installation from
floppies, I decided to explore the use of pxeboot (supported by the
R100's BIOS) to enable the laptop to boot the FreeBSD 5.3 Installation
CDROM over my local LAN. This
documentation is largely for my own benefit - a record of what I did
in case I ever need to do it again. With any luck, this
information will be useful to others (like you, if you're reading
this page and you aren't me).
What is pxeboot?
A quick summary - when your BIOS is set to perform a "LAN boot" it
pulls boot information (a pxeboot executable, boot-time configuration
details and an operational kernel) from another host on the local LAN.
The process requires your on-LAN DHCP server to co-operate by handing
back pxeboot-specific optional parameters - the booting machine uses
DHCP to query for both an IP address and the on-LAN location of the
boot time configuration files and kernel.
What I wanted to do is use pxeboot to force my laptop to boot from the
FreeBSD 5.3 installation CDROM, in the absence of a bootable CDROM
drive on the laptop itself.
Some more info on pxeboot can be found in the FreeBSD manpage or an article on the FreeBSD website.
An overview of the solution
There are three main components required on your local network:
- DHCP server
- TFTP server
- NFS server
When a client begins
to boot over the local LAN, it queries the DHCP server and expects to
get back not only a valid IP address but also pointers to where it can
find a functional pxeboot
executable. The client then initiates a TFTP transfer of the nominated
pxeboot executable into local memory, and begins executing pxeboot in place of the more usual boot loader.
pxeboot then attempts to NFS-mount a nominated file system that is
assumed to contain /boot, the location (under FreeBSD 5.3 at least) of
a bootable kernel and various boot-time configuration files.
I chose to create an NFS-exported file system under /cdroms/fb53pxe/ which contained only one directory - /boot/ - copied from the FreeBSD 5.3 installation CDROM. The TFTP server pointed to a copy of pxeboot located at /cdroms/fb53pxe/boot/pxeboot and the NFS server exported /cdroms/fb53pxe/.
(It was important to have a modifiable copy of /boot, rather than
exporting the FreeBSD 5.3 installation CDROM directly, because /boot/loader.conf needs an extra line to make pxebooting work properly for my needs.)
Creating the pxe-bootable version of the FreeBSD 5.3 Installer file system
As root I first created a minimal, bootable file system from the FreeBSD 5.3 installation CROM using the following steps:
mount /cdrom [...assuming the FreeBSD 5.3 installation CDROM is in the drive]
mkdir -p /cdroms/fb53pxe
cp -Rp /cdrom/boot /cdroms/fb53pxe
umount /cdrom
The main reason we create a separate copy of /boot on regular disk is to edit /cdroms/fb53pxe/boot/loader.conf by adding the following line:
vfs.root.mountfrom="ufs:/dev/md0c"
(This line forces the kernel to use memory mapped disk at /dev/md0c as
the root partition, despite the fact that pxeboot normally expects to
use an NFS-mounted root partition. The importance of this change will
become clearer below.)
DHCP Server configuration
I already had a DHCP server installed on my central machine (easily installed with 'pkg_add -r isc-dhcp3-server' and adding 'dhcpd_enable="YES"' to /etc/rc.conf). The IP to MAC address mappings are defined in /usr/local/etc/dhcpd.conf. In my case, my laptop machine (r100, at 192.168.10.40) already had a special entry that looked like this:
host r100 {
hardware ethernet 00:08:0d:88:c4:33; [This uniquely identifies the client]
fixed-address 192.168.10.40; [DHCP assigns this IP address to the client]
}
This entry was augmented with three additional lines to support pxeboot, and now looked like:
host r100 {
hardware ethernet 00:08:0d:88:c4:33;
fixed-address 192.168.10.40;
next-server 192.168.10.1; [Client will do TFTP and NFS transfers from 192.168.10.1]
filename "fb53pxe/boot/pxeboot"; [Grabbed via TFTP, executed in place of the client host's boot loader]
option root-path "/cdroms/fb53pxe/"; [pxeboot will look for /boot/kernel relative to this NFS-mounted directory]
}
TFTP Server configuration
TFTP was easily enabled by editting /etc/inetd.conf to have the following line:
tftp
dgram udp wait
root /usr/libexec/tftpd
tftpd -s /cdroms/
The line above means
that all TFTP requests will be resolved relative to /cdroms on my
server host (the "-s" forces tftpd to perform a chroot to /cdroms/
after starting up). Recall that I placed a copy of the installation CDROM's /boot directory tree under /cdroms/fb53pxe/boot. The path specified under filename in the DHCP server's configuration file (above) thus results in the client requesting the file/cdroms/fb53pxe/boot/pxeboot using TFTP, as required.
Run kill -HUP `cat /var/run/inetd.pid` after editting /etc/inetd.conf to ensure inetd is re-initialised correctly.
NFS Server configuration
I already had NFS running, so the only additional step was to add
/cdroms/fb53pxe in the list of exported filesystems. The following line
was added to /etc/exports:
/cdroms/fb53pxe -network 192.168.10 -mask 255.255.255.0
This export matches the option root-path in the DHCP
server's configuration file (above), the path that the pxeboot client
will automount and use as the intial root directory during the early
stage of booting.
Run kill -HUP `cat /var/run/mountd.pid` after editting /etc/exports to ensure the NFS server is re-initialised correctly.
What happens during boot now?
Assuming everything's working okay, a typical boot sequence is now:
- Client issues DHCP request, and is handed back an IP address along with next-server, filename and root-path information.
- Client initiates a TFTP request to 192.168.10.1 for filename, "fb53pxe/boot/pxeboot", which is assumed to point to a valid pxeboot executable. This results in "/cdroms/fb53pxe/boot/pxeboot" being copied over to the client.
- pxeboot begins executing, and NFS-mounts 192.168.10.1:/cdroms/fb53pxe/ as its temporary root partition. pxeboot then begins a regular boot loader behaviour - reading /boot/loader.conf (residing at 192.168.10.1:/cdroms/fb53pxe/boot/loader.conf), etc, and ultimately loading and booting a running kernel (which it will pull from 192.168.10.1:/cdroms/fb53pxe/kernel/kernel).
- 192.168.10.1:/cdroms/fb53pxe/boot/mfsroot.gz will be unpacked and used to create a RAM disk on /dev/md0c (this is standard for the Installation CDROM, because naturally we don't want to touch the client's hard drive just yet). The mfsroot.gz-derived RAM disk contains a sufficient root partition (with /sbin, etc) for the Installer to function.
- The newly booted kernel will use /dev/md0c
as its root partition (as a consequence of the line we added to
loader.conf above - if we had not done so the kernel would have
defaulted to continuing to use 192.168.10.1:/cdroms/fb53pxe/ as the root partition, which would then have failed because it lacks the required /sbin, /etc, and related directories).
Once the client is
booted in this fashion it continues into the standard FreeBSD 5.3
Installer, and from this point functions just the same as if we'd
booted from CDROM or floppies. One advantage of this approach is that
you can add multiple hosts in the DHCP server's configuration if you
need to run the FreeBSD 5.3 installer on numerous machines that support
pxeboot but lack bootable CDROM drives.
(In my case I continued on to doing the actual Installation over NFS,
since I was also exporting the entire FreeBSD 5.3 CDROM image via NFS
using the technique described here.)