Before delving into the inners of the installation, we need to retrieve the disk geometry values, which, as we will see, will come in handy more than once. To get this information, insert the Compact Flash card into its socket, attach to the device's serial console with a null-modem cable, connect with cu(1) and power the system up. You should get something like:
# cu -s 19200 -l cua00 comBIOS ver. 1.26a 20040819 Copyright (C) 2000-2004 Soekris Engineering. net45xx 0064 Mbyte Memory CPU 80486 133 Mhz Pri Mas SanDisk SDCFB-64 LBA 490-8-32 62 Mbyte [...]
The numbers 490, 8 and 32 are, respectively, the number of cylinders, heads (i.e. tracks per cylinder) and sectors (per track) of the disk.
Ok, now let the fun begin! We will create a bootable filesystem on the flash card and copy the files we need from the OS. To make fewer write operations on the memory card, the best thing is to create a disk-image file of the size of the CF card (see vnd(4) for details) and eventually copy it to the device. For instance, to create a 64MB virtual disk image, type:
# dd if=/dev/zero of=net4521.img bs=512 count=125440 125440+0 records in 125440+0 records out 64225280 bytes transferred in 1.399 secs (45875823 bytes/sec) # vnconfig -c svnd0 net4521.img
The bs parameter sets the block (sector) size (usually 512 bytes), and count the number of sectors, obtained by multiplying the disk geometry values (32 * 8 * 490). Note: if you want to write directly to the disk, without bothering with the virtual disk image, simply replace svnd0 with the appropriate disk drive (e.g. sd0) in the subsequent examples.
After creating the virtual disk, we need to disklabel(8) it, build the filesystem and make it bootable; but to fully understand these steps, we must first discuss how OpenBSD boots on the i386 architecture. So let's take a look, in parallel, at the boot process and how it reflects upon our installation procedure (for more information, please refer to [FAQ14]).
The Master Boot Record is the first physical sector (512 bytes) on the disk; it is loaded by the BIOS after the POST and it contains the primary partition table (Master Partition Table) and a small program (Master Boot Code) to load the Partition Boot Record (see below).
OpenBSD provides a "MBR template file" (/usr/mdec/mbr) which we can install with fdisk(8):
# fdisk -c 490 -h 8 -s 32 -iyf /usr/mdec/mbr svnd0 Writing MBR at offset 0. #
We need to specify the disk geometry (we have seen before how to retrieve these data) because we're not installing directly to the disk now, but to a virtual disk image. Note: if the OpenBSD release you're installing from is not the same as the release you're installing, you can extract the mbr file from the baseXX.tgz file set.
Next, the OpenBSD boot process goes through two stages:
Before installing the boot loaders, we need to create the disklabel(5), which contains detailed information about disk geometry and partitions and acts as an interface between the disk and the disk drivers contained within the kernel. The disklabel(8) utility allows you to write the label on the disk (once again, disk geometry information will come in handy):
# disklabel -E svnd0 Label editor (enter '?' for help at any prompt) > e Changing device parameters for /dev/rsvnd0c: disk type: [vnd] ESDI label name: [fictitious] net4521 sectors/track: [100] 32 tracks/cylinder: [1] 8 sectors/cylinder: [100] 256 number of cylinders: [1254] 490 total sectors: [125440] <enter> > a a offset: [0] 63 size: [125377] <enter> FS type: [4.2BSD] <enter> > q Write new label?: [y] y #
We have created only a single "/" partition: swapping on the compact flash is strongly discouraged. Now we can build the filesystem:
# newfs -S 512 /dev/rsvnd0a newfs: reduced number of fragments per cylinder group from 7832 to 7792 to enlarge last cylinder group /dev/rsvnd0a: 61.2MB in 125376 sectors of 512 bytes 5 cylinder groups of 15.22MB, 974 blocks, 2048 inodes each super-block backups (for fsck -b #) at: 32, 31200, 62368, 93536, 124704, #
mount it and install the two boot loaders with the installboot(8) command:
# mkdir /mnt/net4521 # mount /dev/svnd0a /mnt/net4521 # cp /usr/mdec/boot /mnt/net4521/ # /usr/mdec/installboot /mnt/net4521/boot /usr/mdec/biosboot svnd0
We can now set up some boot parameters in the /etc/boot.conf(5) configuration file. We will use it to set up the serial console, which has a default baud rate of 19200 (or 38400 for ALIX boards):
set tty com0 stty com0 19200
Now that the disk is ready, we only have to populate it. Let's start with the kernel, for which we have two options: if the CF card is not too small, the easy and smooth (and recommended) way is copying the standard bsd kernel to it:
# cp /bsd /mnt/net4521/
Or else, if you need the kernel to be smaller and faster at boot time, you can build a custom kernel with only the bare minimum features. The following is a sample configuration file suitable for the latter case:
# OpenBSD config file for Soekris net4521 embedded system machine i386 # architecture, used by config; REQUIRED option I486_CPU # Operation Related Options option DUMMY_NOPS # speed hack; recommended # Debugging Options option DDB # Filesystem Options option FFS option MFS option NFSCLIENT option FDESC option FIFO # Miscellaneous Options option PCIVERBOSE option CRYPTO option TIMER_FREQ=1189161 option PCCOMCONSOLE option CONSPEED=19200 # Networking Options option INET option INET6 option TCP_SACK option TCP_FACK option TCP_SIGNATURE option IPSEC option KEY option ALTQ option ALTQ_NOPCC maxusers 5 # estimated number of users config bsd root on wd0a mainbus0 at root cpu0 at mainbus? bios0 at mainbus0 pcibios0 at bios0 flags 0x0000 # use 0x30 for a total verbose isa0 at mainbus0 pci* at mainbus0 # power management and other environmental stuff elansc* at pci? # AMD Elan SC520 System Controller gpio* at elansc? # CardBus bus support cardbus* at cardslot? pcmcia* at cardslot? cbb* at pci? cardslot* at cbb? npx0 at isa? port 0xf0 irq 13 # math coprocessor isadma0 at isa? com0 at isa? port 0x3f8 irq 4 # standard PC serial ports com1 at isa? port 0x2f8 irq 3 com2 at isa? port 0x3e8 irq 5 # IDE wdc0 at isa? port 0x1f0 irq 14 flags 0x00 # WD100x compatible hard disk controller driver wd* at wdc? flags 0x0000 # WD100x compatible hard disk driver # Networking devices sis* at pci? # SiS 900/7016 ethernet Fast Ethernet driver nsphyter* at mii? phy ? # NS and compatible PHYs # Wireless network cards wi* at pcmcia? # PRISM 2-3 wireless network driver # Pseudo-devices pseudo-device mtrr 1 # driver for CPU memory range attributes pseudo-device nvram 1 # driver for reading PC NVRAM contents pseudo-device bio 1 # ioctl tunnel pseudo-device pseudo-device hotplug 1 # devices hot plugging pseudo-device ksyms 1 # kernel symbol table device pseudo-device systrace 1 # enforce and generate policies for system calls pseudo-device pf # Packet filter pseudo-device pflog # Packet filter logging interface pseudo-device pfsync # Packet filter state table logging interface pseudo-device loop 2 # Loopback pseudo-device bpfilter 16 # Berkeley Packet Filter pseudo-device tun 2 # Network tunnel pseudo-device pseudo-device enc 1 # IPSEC encapsulating Interface pseudo-device bridge 2 # Ethernet bridge interface pseudo-device vlan 32 # IEEE 802.1Q encapsulation/decapsulation pseudo-device pseudo-device gre 4 # GRE encapsulating network device pseudo-device pty 32 # Pseudo-terminals pseudo-device gif 4 # Generic tunnel interface
So let's build the kernel and install it:
# cd /usr/src/sys/arch/i386/conf # config NET4521 Don't forget to run "make depend" # cd ../compile/NET4521 # make clean && make depend && make [...] # cp bsd /mnt/net4521/
Next we will create the necessary configuration files in /etc (well, for the moment /mnt/net4521/etc/). We will only list the main ones here: a comprehensive list would be too dependent on the purpose of the device.
/dev/wd0a / ffs ro 1 1 swap /tmp mfs rw,nosuid,-P=/tmplate,-s=16384 0 0
As stated before, we map the /tmp filesystem to memory. /var and /root, which must be read-write, will be symbolic links to /tmp/var and /tmp/root respectively. We will also create a /tmplate directory containing the directory tree which mount_mfs(8) will use to populate /tmp after its creation (we will put pseudo-devices and files required by syslogd(8) into this directory later);
# mkdir /mnt/net4521/tmp{late,} # ln -s /tmp/{var,root} /mnt/net4521/ # mkdir -p /mnt/net4521/tmplate/var/cron/{tabs,atjobs} # chmod 555 /mnt/net4521/tmplate/var/cron # chmod 1770 /mnt/net4521/tmplate/var/cron/atjobs # chmod 1730 /mnt/net4521/tmplate/var/cron/cron # mkdir -p /mnt/net4521/tmplate/root # chmod 700 /mnt/net4521/tmplate/root
# echo "root:$(encrypt -b 8 mypasswd):0:0:daemon:0:0:Charlie &,,,:/root:/bin/ksh" >> /mnt/net4521/etc/master.passwd # echo "wheel:*:0:root" >> /mnt/net4521/etc/group # pwd_mkdb -d /mnt/net4521/etc /mnt/net4521/etc/master.passwd
Feel free to add all the system and administrative users and groups you will need. If you want to use sudo(8), which is usually a good idea, you need to create the sudoers(5) file (using the "visudo -f /mnt/net4521/etc/sudoers" command);
# ssh-keygen -t rsa -f /mnt/net4521/etc/ssh/ssh_host_rsa_key -N "" # ssh-keygen -t rsa1 -f /mnt/net4521/etc/ssh/ssh_host_key -N "" # ssh-keygen -t dsa -f /mnt/net4521/etc/ssh/ssh_host_dsa_key -N ""
# mkdir -p /mnt/net4521/tmplate/var/{log,run/dev} # touch /mnt/net4521/tmplate/var/log/{authlog,daemon,messages,secure} # touch /mnt/net4521/tmplate/var/run/utmp # chmod 640 /mnt/net4521/tmplate/var/log/{authlog,daemon} # chmod 600 /mnt/net4521/tmplate/var/log/secure # chmod 664 /mnt/net4521/tmplate/var/run/utmp
You may schedule newsyslog(8) to periodically archive log files:
# echo "0 * * * * /usr/bin/newsyslog" > /mnt/net4521/tmplate/var/cron/tabs/root # chmod 600 /mnt/net4521/tmplate/var/cron/tabs/root
Anyway, since /var will reside on volatile memory, it is recommended to forward log messages to a remote log host;
console "/usr/libexec/getty Pc" vt220 off secure ttyC0 "/usr/libexec/getty Pc" vt220 off secure ttyC1 "/usr/libexec/getty Pc" vt220 off secure ttyC2 "/usr/libexec/getty Pc" vt220 off secure ttyC3 "/usr/libexec/getty Pc" vt220 off secure tty00 "/usr/libexec/getty std.19200" vt100 on secure tty01 "/usr/libexec/getty std.9600" unknown off tty02 "/usr/libexec/getty std.9600" unknown off tty03 "/usr/libexec/getty std.9600" unknown off ttyp0 none network ttyp1 none network ttyp2 none network [...]
net.inet.ip.forwarding=1 [...]
Next, we need to copy the startup scripts (rc(8), rc.local(8), rc.securelevel(8), rc.conf(8), rc.conf.local(8), rc.shutdown(8), netstart(8)) and create the device files:
# mkdir /mnt/net4521/dev/ # cp /dev/MAKEDEV /mnt/net4521/dev/ # cd /mnt/net4521/dev/ # ./MAKEDEV tun0 tun1 tun2 tun3 bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 bpf8 \ > bpf9 fd1 fd1B fd1C fd1D fd1E fd1F fd1G fd1H fd0 fd0B fd0C fd0D fd0E fd0F \ > fd0G fd0H random crypto pf pctr systrace sd0 sd1 sd2 sd3 sd4 wd0 wd1 wd2 wd3 \ > fd tty00 tty01 tty02 tty03 ttyc0 ttyc1 ttyc2 ttyc3 apm std #
Note: rc(8) clears the /tmp directory on boot, thus removing the contents of the /var and /root directories; therefore, I would recommend that you delete the following lines from /mnt/net4521/etc/rc:
(cd /tmp && rm -rf [a-km-pr-zA-Z]*) (cd /tmp && find . ! -name . ! -name lost+found ! -name quota.user \ ! -name quota.group -execdir rm -rf -- {} \; -type d -prune)
/dev/log, used by syslogd(8), must be writable: therefore, we turn it into a symlink to /var/run/dev/log. The same applies to pseudo terminals, which must be able to change owner and permissions:
# ln -s /var/run/dev/log /mnt/net4521/dev/log # cd /mnt/net4521/tmplate/var/run/dev/ # /dev/MAKEDEV pty # for dev in [tp]typ?; do > ln -s /var/run/dev/$dev /mnt/net4521/dev/$dev > done #
Finally, we can install binaries and libraries. The simplest way is copying them from the system currently in use, or you may extract them from the installation file set (baseXX.tgz). To save some time, you can create a file with the list of the binaries to copy (a good starting point is flashsmall.txt from flashdist):
# tar -I bin_list.txt -cf - | tar -C /mnt/net4521/ -xpf - tar: Removing leading / from absolute path names in the archive # while read file; do > ldd $file 2>/dev/null | egrep 'rlib|rtld' | awk '{ print $7 }' > done < bin_list.txt | sort -u | xargs tar -cvf - | tar -C /mnt/net4521/ -xpf - tar: Removing leading / from absolute path names in the archive [...] #
If you wish to further decrease the binaries disk space, you can take a look at crunchgen(8), which builds them all in a single binary file that modifies its behaviour according to argv[0], or remove the debugging symbols from the shared libraries using the strip(1) command:
# strip -S /mnt/net4521/usr/lib/lib*
Now we only have to transfer the virtual filesystem we have created to the memory card:
# umount /mnt/net4521 # vnconfig -u svnd0 # dd if=net4521.img of=/dev/sd0c bs=512 125440+0 records in 125440+0 records out 64225280 bytes transferred in 383.307 secs (167556 bytes/sec) #
Plug the compact flash into the device, power it up and ...uncork the champagne!